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