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 ""


#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
org 0xf800
  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

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
 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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

 xor.b #(1<<0), &P1OUT
 mov #60000, R4
 dec R4
 jnz loop1
 jmp TASK1

;;;;;;;;;;;;;TASK 2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

 xor.b #(1<<6), &P1OUT
 mov #10000,R4
 dec R4
 jnz loop2
 jmp TASK2

;;;;;;;;;;;;TASK 3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

 xor.b #(1<<1),&P1OUT
 call #delay3
 jmp TASK3
;delay subroutine
 mov #0xffff, R4
loop3: dec R4
 jnz loop3


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

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..

1 comment :

  1. 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.