原始碼下載
前言
使用 SDCC 來寫 rtos 比起用 keil-c 來得方便許多,因為 SDCC 有一個 naked 修飾元。naked function 會省略前置的 push 動作及最後的 pop 及 ret 指令,這些動作都需要使用者自己加入。在 sdccman.pdf 內的說明指出 naked function 主要是用於 interrupt function 來省略一堆無意義的 push/pop 動作,剛好被我用來增加 push/pop 動作(完全相反的立意)。
rtos_start
- 其實我寫的 rtos 很簡單,也就是把 stack 分成四份,配給四個 task,讓每個 task 的 stack 各自獨立不互相干擾。
- 再利用 timer 0 的中斷處理來切換 task。
- rtos_task_id 是記錄目前正在執行的 task 是那一個。
- rtos_stack 是記錄每一個 task 的 stack 位置。
- 由於在切換 task 時,會 pop 14 個 register 及 task 的執行位置。所以一開始需要把每個 task 的起始位置 push 到堆疊里,並把 stack 起始位置加上 14,在做切換 task 時,才會順利執行相應的函數。
- 由於第一個 task 就是主程序,所以不用做 push 動作,因為在第一次切換 task 時,會自動 push,因此才會有 rtos_stack[0] = SP; 的指令。
- call task1() 及 call rtos_start() 理論上會浪費 stack 空間才對,但因為被 SDCC 最佳化的關係,二個指令都變成 ljmp task1 及 ljmp rtos_start,並沒有浪費到 stack 空間,請安心使用。
#define PUSH_SIZE 14
void rtos_start(void)
{
unsigned char i, size;
__idata unsigned char *sp;
// variable init
rtos_task_id = 0;
// init stack
size = (255 - SP) >> 2; // size = (256 - SP) / MAX_TASKS;
sp = (__idata unsigned char *)SP;
for (i=0; i> 8;
rtos_stack[i] = (unsigned char)sp + PUSH_SIZE;
sp = sp + size - 2;
}
rtos_stack[0] = SP;
// timer0 init
TMOD = (TMOD & 0xF0) | 0x01;
TH0 = 0;
TL0 = 0;
TF0 = LOW;
ET0 = HIGH;
TR0 = HIGH;
// jump to task 1
task1();
}
rtos_nexttask
- rtos_nexttask 的動作很簡單,就是保存現在所有的 register (r0~r7, acc, b, dph, dpl, psw, bp)及目前的 sp 位置,然後變更到下一個 task 的 sp,再還原所有的 register,最後 return 到新 task 的位置(在 rtos_start 時已經記錄到 堆疊里)。
- bp 這個東東應該不算 register,這個主要是被用在 reentrant 函數,一個很特殊的全域變數,忘了保存它還會造成程式錯亂喔。
void rtos_nexttask(void) __naked
{
// save current stack
rtos_stack[rtos_task_id] = SP;
// load next stack
if (++rtos_task_id >= MAX_TASKS) rtos_task_id = 0;
SP = rtos_stack[rtos_task_id];
// restore register
_asm
pop _bp
pop ar7
pop ar6
pop ar5
pop ar4
pop ar3
pop ar2
pop ar1
pop ar0
pop psw
pop dpl
pop dph
pop b
pop acc
ret
_endasm;
}
void rtos_isr(void) __interrupt (1) __naked
{
// store register
_asm
push acc
push b
push dph
push dpl
push psw
push ar0
push ar1
push ar2
push ar3
push ar4
push ar5
push ar6
push ar7
push _bp
_endasm;
// call rtos_nexttask
*(__idata unsigned char *)++SP = (unsigned int)&rtos_nexttask & 0xff;
*(__idata unsigned char *)++SP = (unsigned int)&rtos_nexttask >> 8;
_asm reti _endasm;
}
執行結果
567890123456789012345678901234567890123456SP = 1D
rstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefgSP = 5A
QRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGSP = 97
[\]^_`[\]^_`[\]^_`[\]^_`[\]^_`[\]^_`[\]^_`SP = D4
789012345678901234567890123456789012345678SP = 1D
hijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwSP = 5A
HIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWSP = 97
[\]^_`[\]^_`[\]^_`[\]^_`[\]^_`[\]^_`[\]^_`[SP = D4
張貼留言