tiny rtos for 8051 using SDCC part 2

Posted by: 邱小新 at 下午2:25 in , ,

原始碼下載

前言

使用 SDCC 來寫 rtos 比起用 keil-c 來得方便許多,因為 SDCC 有一個 naked 修飾元。naked function 會省略前置的 push 動作及最後的 pop 及 ret 指令,這些動作都需要使用者自己加入。在 sdccman.pdf 內的說明指出 naked function 主要是用於 interrupt function 來省略一堆無意義的 push/pop 動作,剛好被我用來增加 push/pop 動作(完全相反的立意)。

rtos_start

  1. 其實我寫的 rtos 很簡單,也就是把 stack 分成四份,配給四個 task,讓每個 task 的 stack 各自獨立不互相干擾。
  2. 再利用 timer 0 的中斷處理來切換 task。
  3. rtos_task_id 是記錄目前正在執行的 task 是那一個。
  4. rtos_stack 是記錄每一個 task 的 stack 位置。
  5. 由於在切換 task 時,會 pop 14 個 register 及 task 的執行位置。所以一開始需要把每個 task 的起始位置 push 到堆疊里,並把 stack 起始位置加上 14,在做切換 task 時,才會順利執行相應的函數。
  6. 由於第一個 task 就是主程序,所以不用做 push 動作,因為在第一次切換 task 時,會自動 push,因此才會有 rtos_stack[0] = SP; 的指令。
  7. 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<MAX_TASKS; i++) { *++sp = (unsigned int)task_func[i] & 0xff; *++sp = (unsigned int)task_func[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

  1. rtos_nexttask 的動作很簡單,就是保存現在所有的 register (r0~r7, acc, b, dph, dpl, psw, bp)及目前的 sp 位置,然後變更到下一個 task 的 sp,再還原所有的 register,最後 return 到新 task 的位置(在 rtos_start 時已經記錄到 堆疊里)。
  2. 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

0 意見

張貼留言