做了7個文件,分別是
os_core.h //核心文件的參數定義
os_core.c //核心文件,包含進程的6種操作,系統級別的操作
task_switch.h //任務切換函數的定義
task_switch.c //任務切換函數的實現,采用搶占式中斷調用(T2自動重載)
sem.h //信號量的定義,其實也就是3個函數,創建信號量,發送一個信號量,接收信號量,在教材里面講得那么復雜,差點把人搞暈了,實現起來不要太簡單
sem.c //信號量實現
main.c //任務以及它的操作和主函數包括在這里
//---------------------------------------------------os_core.h---------------------------------------------------------------// #ifndef __OS_CORE_H__ #define __OS_CORE_H__ #include <reg52.h> typedef signed char int8s; typedef unsigned char int8u; typedef signed int int16s; typedef unsigned int int16u; typedef signed long int32s; typedef unsigned long int32u; //任務的5種狀態 #define TASK_STATUS_CREATE 0x00 #define TASK_STATUS_RDY 0x01 #define TASK_STATUS_SUSPEND 0x02 #define TASK_STATUS_ENDED0x04 #define TASK_STATUS_SEM0x08 //任務控制塊 typedef struct { int8u task_status;//任務狀態 int8u task_priority;//任務的優先級 int8u task_stack_top;//任務棧頂地址(保存任務棧頂的地址) int8u task_wait_tick;//任務延時等待次數 }OS_TCB; #define MAX_TASKS 4//最大的任務數量 //任務的優先級從小到大一次排列,數值越小,優先級越高 #define TASK_STACK_DEPTH 17//最大任務堆棧深度(2字節程序地址+13字節寄存器數據+2字節中斷過程保存字節) #define NUM_RESISTER_PUSHED 13//人工堆棧中需要被保存的寄存器數量(這里因為是8051,所以有13字節) extern idata volatile OS_TCB os_tcb[MAX_TASKS];//任務控制塊列表 extern volatile int8u task_running_id;//正在運行任務的標號 extern volatile int8u os_res_list;//系統資源表,表示哪些資源是否被占用(最大為8個) extern volatile int8u idata task_stack[MAX_TASKS][TASK_STACK_DEPTH];//每個任務的任務棧 extern volatile int8u task_int_list;//任務中斷標記(最大為8個) extern volatile int8u int_count;//進入中斷次數 extern volatile int8u os_en_cr_count;//進入臨界區次數 #define os_enter_critical() {EA=0;os_en_cr_count++;} //進入臨界區 #define os_exit_critical() {if(os_en_cr_count>0){os_en_cr_count--;if(os_en_cr_count==0){EA=1;}}}//退出臨界區 //任務的六種操作 void task_create(void (*task)(void),int8u task_priority); void task_delete(int8u task_id); void task_sleep(); void task_wake(); void task_suspend(int8u task_id); void task_resume(int8u task_id); //系統操作 void os_init();//初始化系統 void os_start();//開始系統 void os_delay(int8u tick);//任務延時 void os_task_idle();//空任務,具有最低優先級 #endif //---------------------------------------------------os_core.c---------------------------------------------------------------// #include "os_core.h" #include "task_switch.h" idata volatile OS_TCB os_tcb[MAX_TASKS];//任務控制塊列表 volatile int8u task_running_id;//正在運行任務的標號 volatile int8u os_res_list;//系統資源表,表示哪些資源是否被占用(最大為8個) volatile int8u idata task_stack[MAX_TASKS][TASK_STACK_DEPTH];//每個任務的任務棧 volatile int8u task_int_list;//任務中斷標記(最大為8個) volatile int8u int_count;//進入中斷次數 volatile int8u os_en_cr_count;//進入臨界區次數 /* ******************************************************************************************************** 任務操作 ******************************************************************************************************** */ void task_create(void (*task)(void),int8u task_priority) { static int8u i; static int8u task_id; static int8u *stack_point; for(i=0; i<MAX_TASKS;i++) {//為任務分配id和資源(注意,task的id和資源的分配是自動的) if((os_res_list&(0x01<<i))==0) {//如果是空的話,那么會從第0個開始分配,即第一個任務的ID號肯定是0,也就是空閑任務ID肯定是0 task_id = i; os_res_list|=0x01<<i; break; } } os_tcb[task_id].task_status = TASK_STATUS_CREATE;//將任務置于創建態 //為任務分配堆棧空間 stack_point = task_stack[task_id]; for(i=0; i<TASK_STACK_DEPTH; i++) {//先清除堆棧 *(stack_point+i) = 0; } *stack_point = (int16u)task; //任務低8位給task_stack[task_id][0] stack_point++; *stack_point = (int16u)task>>8; //任務高8位給task_stack[task_id][1] stack_point += NUM_RESISTER_PUSHED; //將13個寄存器位置保存起來 //為任務添加屬性 os_tcb[task_id].task_stack_top = (int8u)stack_point; os_tcb[task_id].task_priority = task_priority; os_tcb[task_id].task_wait_tick = 0; os_tcb[task_id].task_status = TASK_STATUS_RDY;//將任務置于就緒態 } void task_suspend(int8u task_id) { //掛起一個任務 os_enter_critical(); os_tcb[task_id].task_status = TASK_STATUS_SEM;//將該任務的狀態置于被掛起的狀態(由于信號量) os_exit_critical(); if(task_id == task_running_id) { os_task_switch(); //調用一下任務切換,切換到其它任務 } } void task_resume(int8u task_id) { os_enter_critical(); if(os_res_list&(0x01<<task_id) != 0) { //如果要恢復的任務存在 os_tcb[task_id].task_status = TASK_STATUS_RDY;//將任務狀態置于就緒態 os_exit_critical(); if(os_tcb[task_id].task_priority < os_tcb[task_running_id].task_priority) { os_task_switch(); //調用一下任務切換,切換到優先級更高的任務 } } } /* void task_delete(int8u task_id) { //刪除一個任務 os_enter_critical(); if(os_res_list&(0x01<<task_id) != 0) { //如果要刪除的任務存在 os_tcb[task_id].task_status = TASK_STATUS_ENDED; //將任務狀態置于結束態 os_res_list &= ~(0x01<<task_id); //釋放任務使用的堆棧等資源 os_tcb[task_id].task_wait_tick = 0; os_exit_critical(); os_task_switch(); } } */ /* ******************************************************************************************************** 系統操作 ******************************************************************************************************** */ void os_init(){ //初始化系統 EA = 0;//關閉總中斷 ET2 = 1;//開啟定時器2中斷 T2CON = 0x00;//自動重載 //T2MOD = 0x00; RCAP2H = 0xD8; //晶振使用12MHZ,定時時間10ms RCAP2L = 0xF0; os_res_list = 0x00; //資源全部置空 os_en_cr_count = 0; //進入臨階段0次 task_create(&(os_task_idle),MAX_TASKS-1);//創建空閑進程,優先級為MAX_TASKS-1 } void os_start(){//開始系統 task_running_id = 0;//指示task_idle 為第一個運行的任務 os_tcb[task_running_id].task_stack_top -= NUM_RESISTER_PUSHED;//去掉寄存器的堆棧,以免在調用的時候出現堆棧溢出 SP = os_tcb[task_running_id].task_stack_top; //將空閑任務置于運行態 TR2 = 1;//開啟定時器2中斷 EA = 1;//開啟總中斷 //while(1);//等待定時器中斷的調用 } void os_delay(int8u tick){//任務延時 os_enter_critical(); os_tcb[task_running_id].task_wait_tick = tick;//設置延時節拍數 os_tcb[task_running_id].task_status = TASK_STATUS_SUSPEND;//將置于處于掛起態,等待延時 os_exit_critical(); os_task_switch(); os_exit_critical(); } void os_task_idle() {//空任務,具有最低優先級 //static int8u i; while(1) { //os_enter_critical(); //i++; //os_exit_critical(); } } //---------------------------------------------------task_switch.h---------------------------------------------------------------// #ifndef __TASK_SWITCH_H__ #define __TASK_SWITCH_H__ #include "os_core.h" void os_task_switch();//任務調度 #endif //---------------------------------------------------task_switch.c---------------------------------------------------------------// #include "task_switch.h" void os_task_switch(){//任務調度 static int8u i; EA = 0; __asm PUSH ACC//保存寄存器 __asm PUSH B __asm PUSH PSW __asm PUSH DPH __asm PUSH DPL __asm MOV A,R0 __asm PUSH ACC __asm MOV A,R1 __asm PUSH ACC __asm MOV A,R2 __asm PUSH ACC __asm MOV A,R3 __asm PUSH ACC __asm MOV A,R4 __asm PUSH ACC __asm MOV A,R5 __asm PUSH ACC __asm MOV A,R6 __asm PUSH ACC __asm MOV A,R7 __asm PUSH ACC os_tcb[task_running_id].task_stack_top = SP;//保存當前的堆棧指針 //os_tcb[task_running_id].task_status = TASK_STATUS_RDY;//不用在這里修改當前任務的狀態,已經在任務函數中修改過了 task_running_id = 0; for(i=0; i<MAX_TASKS; i++) { if((os_res_list&(0x01<<i)) != 0) {//判斷任務的資源是否還在使用,即任務是否還存在 if(os_tcb[i].task_status == TASK_STATUS_RDY) { if(os_tcb[i].task_priority<os_tcb[task_running_id].task_priority) { task_running_id = i; //找到優先級最高的,而且處于就緒狀態的任務,將它置位當前要運行的任務 } } } } SP = os_tcb[task_running_id].task_stack_top; __asm POP ACC//恢復寄存器 __asm MOV R7,A __asm POP ACC __asm MOV R6,A __asm POP ACC __asm MOV R5,A __asm POP ACC __asm MOV R4,A __asm POP ACC __asm MOV R3,A __asm POP ACC __asm MOV R2,A __asm POP ACC __asm MOV R1,A __asm POP ACC __asm MOV R0,A __asm POP DPL __asm POP DPH __asm POP PSW __asm POP B __asm POP ACC EA = 1; } void timer2_isr(void) interrupt 5 using 1 { int8u i; EA = 0; __asm MOV A,R0 __asm PUSH ACC __asm MOV A,R1 __asm PUSH ACC __asm MOV A,R2 __asm PUSH ACC __asm MOV A,R3 __asm PUSH ACC __asm MOV A,R4 __asm PUSH ACC __asm MOV A,R5 __asm PUSH ACC __asm MOV A,R6 __asm PUSH ACC __asm MOV A,R7 __asm PUSH ACC TF2 = 0; os_tcb[task_running_id].task_stack_top = SP; task_running_id = 0; for(i=0; i<MAX_TASKS; i++) { if((os_res_list&(0x01<<i)) != 0) {//判斷任務的資源是否還在使用,即任務是否還存在 if(os_tcb[i].task_status == TASK_STATUS_SUSPEND) { //只有被掛起的任務才能時間減少 if(os_tcb[i].task_wait_tick != 0) { os_tcb[i].task_wait_tick--; if(os_tcb[i].task_wait_tick == 0) { os_tcb[i].task_status = TASK_STATUS_RDY; } } } if(os_tcb[i].task_status == TASK_STATUS_RDY) { if(os_tcb[i].task_priority<os_tcb[task_running_id].task_priority) { task_running_id = i; //找到優先級最高的,而且處于就緒狀態的任務,將它置位當前要運行的任務 } } } } SP = os_tcb[task_running_id].task_stack_top; __asm POP ACC//恢復寄存器 __asm MOV R7,A __asm POP ACC __asm MOV R6,A __asm POP ACC __asm MOV R5,A __asm POP ACC __asm MOV R4,A __asm POP ACC __asm MOV R3,A __asm POP ACC __asm MOV R2,A __asm POP ACC __asm MOV R1,A __asm POP ACC __asm MOV R0,A EA = 1; } //---------------------------------------------------sem.h---------------------------------------------------------------// #ifndef __SEM_H__ #define __SEM_H__ #include "os_core.h" #define MAX_SEMS 2 typedef struct { int8u os_event_state;//0:不可用,1:可用 int8u os_task_pend_tbl;//等待信號量的任務列表 }OS_EVENT; extern volatile OS_EVENT os_sem[MAX_SEMS]; void os_task_sem_pend(int8u index); void os_task_sem_post(int8u index); void os_sem_create(int8u index,int8u state); #define os_sem_clean(index) os_sem[index].os_event_state = 0 #endif //---------------------------------------------------sem.c---------------------------------------------------------------// #include "sem.h" volatile OS_EVENT os_sem[MAX_SEMS]; void os_task_sem_pend(int8u index) { os_enter_critical(); if(index< MAX_SEMS) { if(os_sem[index].os_event_state!=0) {//信號量可用 os_sem[index].os_event_state--; } else { os_sem[index].os_task_pend_tbl |= (0x01<<task_running_id); task_suspend(task_running_id); } } os_exit_critical(); } void os_task_sem_post(int8u index) { int8u i; os_enter_critical(); if(index<MAX_SEMS) { for(i=0; i<MAX_TASKS; i++) { //找到的i是優先級最高且處于等待狀態的任務 if((os_sem[index].os_task_pend_tbl&(0x01<<i)) != 0) {//如果要恢復的任務存在 break; } } if(i < MAX_TASKS) { os_sem[index].os_task_pend_tbl &= ~(0x01<<i); task_resume(i); } else { os_sem[index].os_event_state++; } } os_exit_critical(); } void os_sem_create(int8u index,int8u state) { //初始化信號量 if(index < MAX_SEMS) { os_sem[index].os_task_pend_tbl = 0; os_sem[index].os_event_state = state; } } //---------------------------------------------------main.c---------------------------------------------------------------// #include "os_core.h" #include "task_switch.h" #include "sem.h" void task_1(void) { static int8u i; while(1) { os_task_sem_pend(1); i++; P1 = 0x01<<(i%8); os_delay(100); os_task_sem_post(1); } } sbit led = P2^1; void task_2(void) { while(1) { os_task_sem_pend(0); os_task_sem_pend(1); led = 1; os_delay(10); led = 0; os_delay(10); os_task_sem_post(0); os_task_sem_post(1); } } void task_3(void) { static int8u j; while(1) { os_task_sem_pend(0); j++; P3 = 0x01<<(j%8); os_delay(100); os_task_sem_post(0); } } int main(void) { os_init(); P1=0; P2=0; P3=0; os_sem_create(0,1); os_sem_create(1,1); task_create(&(task_2),0); task_create(&(task_1),1); task_create(&(task_3),2); os_start(); return 0; }
總結:
在51單片機上實現操作系統是我們不常討論的話題,其實操作系統還是有蠻大的用處的, 上次我寫了一篇文章:一個簡單的51單片機操作系統的實現,連接是:http://www.zg4o1577.cn/mcu/1325.html ,這篇其實是對上篇文章的補充.實現更多的一些功能,希望大家多多指點啊.
實現了任務的互斥,使得兩個任務之間能夠互斥運行,但是不能是兩個以上,因為系統采用的是搶占式的中斷調度,所以兩個以上任務公用一個信號量,會出現優先級較低的那個任務出現饑餓的情況。
任務的實時性是很有保障的,因為內部函數比較簡單,所以不存在嵌套中斷的說法。
計算一下程序使用的RAM,一共使用大概101字節,剩余107字節可用,感覺還不錯.
現在已經成功地實現信號量的功能。
剩下的還有郵箱、事件、內存等等了,不過由于8051RAM空間還是小了些,如果能擴充到4KB以上,發展潛力倒是會有很大的提高,不過外置RAM肯定速度上比不了內置的。
自己動手做51操作系統現在暫時告一段落,操作系統還是在片上資源豐富的系統上用比較好,不過STC的那個最NB的STC90C516AD擁有4KB的RAM和62KB的ROM用起來也可以的。價格也不貴,有空再去嘗試...
當任務多了的時候,任務的配置模塊也很多,RAM里面84%以上的空間都是用于任務的各種屬性的配置,包括任務堆棧和任務的互斥。
希望接下來的復習能夠更加認真!!!現在算是了卻了我內心中的一樁心愿吧,真正實現了自己的操作系統!!!(功能匱乏,只有進程調度和互斥實現)
現在我才知道鏈表的操作是多么地耗費內存.創建一個指針就要3個字節,如果搞個鏈表出來,包括結構體,在資源不那么豐富的8051上(操作系統中),簡直是宰牛用殺雞刀,完全吃不消.但是如果是AVR或者ARM等片上資源豐富的單片機,那就不存在了.
為什么手機的操作系統里面一個安裝文件動不動就幾MB的大小,為什么Android中的程序動不動就占用幾MB的內存?
這就和操作系統有很大關系了,創建一個任務,給它定義進程控制塊,堆棧,還要包括它的GUI,別看圖形界面特別簡單,實際上采用圖形界面還是相當吃內存的.在進程切換中,保存堆棧和一些進程信息,這又得占用一部分內存.Android因為采用虛擬機的緣故,這虛擬機它也得吃內存啊,開個程序就有個虛擬機跟著,速度當然不如symbian了.可惜symbian內存和cpu的頻率比較低...
所以內存占用和系統任務的個數是線性關系的.當某個程序比較大,它占用內存越多,而且可能會頻繁地讀取內存,這樣就會造成其它程序速度變慢.這就是為什么在聽歌的時候,上網有時會有點卡的原因之一.
系統啟動的時候,會把程序加載進入內存中,當需要加載的程序數量過多時,自然就會比較卡.所以開機的時候會有一段延遲.