Task switching is one of the main function of an operating system. We could 'feel' a computer is doing multiple tasks at a time. The OS is actually switching the tasks one by one in a circular manner and executing each one for a small period of time and we feel it is doing all tasks at a time....
A small demo of multitasking in MSP430G2231 microcontroller:
Here, three independent tasks are to be switched on every timer interrupt. The CPU registers used in previous task is to be stored some where and the register values of the task to which it is switched is to be retained as before. Also, the switched task should run from where it is paused earlier.
Below is my asm code for running three independent tasks in an MSP430G2231 microcontroller. (just a demo, stack depth is very limited due to the limited RAM)
;------------------------------------------------- ;microcontroller : MSP430G2231 ;assembler : naken430asm ;------------------------------------------------- .include "msp430g2x31.inc" ;;;;;;;DEFINITIONS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #define TASK_INDEX 0x27e ;task index in 0x27e #define TASK_BASE 0x27 ;base address of SP backup array #define MAX_TASK 3 ;maximun tasks #define TASK1_TOS 0x278 ;top of the stack of task1 #define TASK2_TOS 0x252 #define TASK3_TOS 0x22a ;;;;;;;;;;;;SETTING TIMER INTERRUPT AND SOME INITIAL VALUES;;;;;; org 0xf800 start: mov.w #(WDTPW|WDTHOLD), &WDTCTL ;disable watchdog mov.b #255,&P1DIR ;set PORT1 as output mov.b #0, &P1OUT ;clear PORT1 mov.w #(TASSEL_2|MC_1|ID_3), &TACTL ;set timer control register mov.w #(CCIE), &TACCTL0 ;enable CC interrupt mov.w #50, &TACCR0 ;set CC interrupt threshold mov.w #0,&TASK_INDEX ;set task index to 0 mov.w #TASK2, &(TASK2_TOS-2); ;push task2 start address for 1st pop mov.w #TASK3, &(TASK3_TOS-2); ;push task3 start address for 1st po mov.w #(TASK2_TOS - 14), &(TASK_BASE +2) ;backup task2 SP for 1st pop mov.w #(TASK3_TOS - 14), &(TASK_BASE +4) ;backup task3 SP for 1st pop mov.w #TASK1_TOS, SP ;set SP before task1 eint ;global interrupt enable mov SR, &(TASK2_TOS - 4) ;set initial SR for task2 for 1st pop mov SR, &(TASK3_TOS - 4) ;set initial SR for task3 for 1st pop mov #TASK1,PC ;begin task 1 ;;;;;;;;;;;;TASK SWITCHING ROUTINE (ON TIMER INTERRUPT);;;;;;;;;; isr: ;reach here on interrupt push r4 ;push register to stack push r5 push r6 push r7 push r8 mov.w &TASK_INDEX, r4 ;mov current task index to r4 mov.w SP,TASK_BASE(r4) ;mov current paused task SP to backup array incd r4 ;double increment the index cmp #(MAX_TASK * 2), r4 ;check index reached maximum value jne notequal clr r4 ;if yes, clear index notequal: mov.w TASK_BASE(r4),SP ;now restore SP of next task (going to switch now] mov.w r4, &TASK_INDEX ;now save the current task index pop r8 ;pop registers pop r7 pop r6 pop r5 pop r4 pop SR ;pop status register pop PC ;pop program counter [task swithing] ;;;;;;;;;;;;;TASK 1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; TASK1: xor.b #(1<<0), &P1OUT mov #60000, R4 loop1: dec R4 jnz loop1 jmp TASK1 ;;;;;;;;;;;;;TASK 2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; TASK2: xor.b #(1<<6), &P1OUT mov #10000,R4 loop2: dec R4 jnz loop2 jmp TASK2 ;;;;;;;;;;;;TASK 3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; TASK3: xor.b #(1<<1),&P1OUT call #delay3 jmp TASK3 ;delay subroutine delay3: mov #0xffff, R4 loop3: dec R4 nop nop jnz loop3 ret ;;;;;;;;;;;;;;;;;VECTORS;;;;;;;;;;;;;;;;;;;;;;;;;;;; org 0xfffe ;reset vector dw start ;to write start address to 0xfffe on programming org 0xfff2 ;timer interrupt vector (CC) dw isr ;to write isr address to 0xfff2 on programming
Explanation:
I hope I have explained it via comment in almost all lines...
The stack is mainly divided into 3 equal parts for three tasks... 8 bytes of ram is used for the stackpoint backup array (6 bytes) and index (2 bytes). On every timer interrupt, 5 general purpose registers (just skipped other gp registers due to ram limitation), the PC and SR is pushed into the stack of the same task. Then the SP of the task is saved to the stack backup array using indexed addressing mode. Now the stackpointer of next task is retained from the array. Then the registers , then the SR and finally the PC is popped and thus the next task is resumed by pausing the current task and this repeats by switching the three tasks continuously with a small task run time of about 400 instruction cycles... This could be controlled by modifying the value in the capture compare register (TACCR0). Timer interrupt will occur when timer value (TAR) matches with TACCR0. I used a timer pre-scaler of 1:8 so that timer increments on every 8 instruction cycle.
Here, the three tasks are three independent LED blinking with different time periods which could be visualized seperatly. Also I used the same CPU registers in all tasks...
Now I could edit the three tasks according to my need and the three tasks will run independently as if it is running on three separate microcontrollers...But any way, the maximum stack depth is 24 bytes.... Also, I didn't backup the registers from r9 to r15 to save considerable amount of stack space which could be utilized by each task..
Now I could edit the three tasks according to my need and the three tasks will run independently as if it is running on three separate microcontrollers...But any way, the maximum stack depth is 24 bytes.... Also, I didn't backup the registers from r9 to r15 to save considerable amount of stack space which could be utilized by each task..
I am however grant from you, despite I am difficult to obtain my desires. I truly maintain session numerous that is published on your blog. supcompare.gr
ReplyDelete