這個應該屬於是狀態機的范疇,看了一本《UML 狀態機圖的使用C/C++設計》里面介紹的Vannilla內核跟介紹的實現思想很像。基於狀態機思想設計的程序,可以有效運行於裸機上,基於OS的任務調度的思想,可以設置優先級,打亂程序的執行順序。與操作系統不同的是,操作系統可以打斷任務運行,並把保留每個任務的棧數據,而這個系統即使新加入的任務的優先級比正在運行的任務優先級高,也只能等待上一個任務執行完畢才會被運行。應避免使用延時函數,對於占用時間長的任務,可以對其進行狀態分解,比如按鍵任務:在狀態1,首次檢測到按下;狀態2,下次循環時檢測到按鍵還是處於按下狀態時,切換到該狀態;狀態3,下次循環還是檢測按鍵被按下時切換到改狀態,並執行安檢任務。利用循環做其他事情的時間所占的時間用來和消抖的時間做抵消,這就是一個簡單的狀態機任務。
以下程序盡量做到與硬件相關。
具體實現方法:構建一個任務表示任務屬性的結構體
1 typedef uint32_t event_t; 2 typedef uint32_t tsTaskPriority_t; 3 4 typedef void ( *pfTsTaskEventHandler_t )( event_t ); 5 6 typedef struct tsTaskTableEntry_tag { 7 event_t events; //任務事件 8 tsTaskPriority_t priority; //任務優先級 9 pfTsTaskEventHandler_t pfTaskEventHandler; //任務執行函數 10 } tsTaskTableEntry_t;
接口頭文件 interface.h:
1 #ifndef _INTERFACE_H_ 2 #define _INTERFACE_H_ 3 4 #include "sys.h" 5 6 #define FALSE 0 7 #define TRUE 1 8 9 typedef uint32_t event_t; 10 typedef uint32_t tsTaskID_t; 11 typedef uint32_t tsTaskPriority_t; 12 typedef uint32_t index_t; 13 typedef uint8_t bool_t; 14 15 #define gTsMaxTasks_c 10 //最大任務數 16 #define gTsIdleTaskID_c (( tsTaskID_t ) 0 ) //空閑任務ID 17 #define gTsInvalidTaskID_c (( tsTaskID_t ) -1 ) //空任務指針 18 #define gTsIdleTaskPriority_c (( tsTaskPriority_t ) 0 ) //空閑任務優先級 19 #define gTsInvalidTaskPriority_c (( tsTaskPriority_t ) -1 ) //空任務優先級 20 21 22 #define _Interrupt_enable() INTX_ENABLE() //打開總中斷 23 #define _Interrupt_disable() INTX_DISABLE() //關閉總中斷 24 25 typedef void ( *pfTsTaskEventHandler_t )( event_t ); //函數指針 26 typedef void ( *pfTimerEventHandler_t )( event_t ); //時間函數指針 27 28 29 #define NumberOfElements(array) ((sizeof(array) / (sizeof(array[0])))) //獲取任務隊列的大小 30 31 #endif
既然是基於事件在跑任務,接下來就是事件處理的頭文件
event.h
1 #ifndef _EVENT_H_ 2 #define _EVENT_H_ 3 #include "sys.h" 4 #include "stdint.h" 5 #include "interface.h" 6 7 8 typedef struct tsTaskTableEntry_tag { 9 event_t events; //任務事件 10 tsTaskPriority_t priority; //任務優先級 11 pfTsTaskEventHandler_t pfTaskEventHandler; //任務執行的函數 12 } tsTaskTableEntry_t; 13 14 extern tsTaskTableEntry_t maTsTaskTable[gTsMaxTasks_c]; //任務隊列 容量為10 可以設置宏定義修改 15 extern tsTaskID_t maTsTaskIDsByPriority[NumberOfElements(maTsTaskTable)]; //優先級列表 16 17 void IdleTask(event_t events); //空閑任務優先級最低切必須實現,可以在其中實現低功耗功能 18 void TS_ClearEvent(tsTaskID_t taskID,event_t events); //清除任務的事件 19 void TS_Init(void); //任務列表初始化 20 tsTaskID_t TS_CreateTask(tsTaskPriority_t taskPriority,pfTsTaskEventHandler_t pfTaskEventHandler); //向添加任務到任務列表 21 void TS_DestroyTask (tsTaskID_t taskID); //從任務列表刪除任務 22 bool_t TS_PendingEvents(void); //查詢任務列表是否有可執行的任務 23 void TS_SendEvent(tsTaskID_t taskID,event_t events); //發送消息給制指定的任務 也就是 任務屬性中 event 24 void TS_Scheduler(void); //任務調度 25 26 #endif
接下來是事件函數的實現:
event.c
1 #include "event.h" 2 #include "stdint.h" 3 #include "global.h" 4 #include "task.h" 5 #include "string.h" 6 #include "delay.h" 7 #include "timer_event.h" 8 9 static tsTaskTableEntry_t maTsTaskTable[gTsMaxTasks_c]; 10 static tsTaskID_t maTsTaskIDsByPriority[NumberOfElements(maTsTaskTable)]; 11 12 static tsTaskID_t gIdleTaskID = 0; 13 14 // 清除任務事件標志 15 void TS_ClearEvent(tsTaskID_t taskID,event_t events) 16 { 17 18 _Interrupt_disable(); //關閉總中斷 19 maTsTaskTable[taskID].events &= ~events; // 清標志位,置0 20 _Interrupt_enable(); //打開總中斷 21 } 22 23 /* Initialize the task scheduler. */ 24 void TS_Init(void) { 25 memset(maTsTaskTable, (u8)gTsInvalidTaskPriority_c, sizeof(maTsTaskTable)); // 清除任務表為無效 26 memset(maTsTaskIDsByPriority, (u8)gTsInvalidTaskID_c, sizeof(maTsTaskIDsByPriority)); // 清除優先級表為無效 27 28 gIdleTaskID = TS_CreateTask(IDLE_TASK_PRIORITY, IdleTask); //創建空閑任務 優先級 0 29 gIdleTaskID = TS_CreateTask(PRINTF_TASK_PRIORITY,test_printf); //打印任務函數 優先級 1 30 // gIdleTaskID = TS_CreateTask(LED_FLASH_PRIORITY,LED_Flash); //打印任務函數 優先級 2 31 addTimerEvent(CYCLE_EVENT,1,1000,LED_Flash); //添加定時器事件 32 33 34 35 36 if(gTsInvalidTaskID_c == gIdleTaskID) 37 { 38 printf("Task is create failed\r\n"); 39 } 40 41 } 42 /* TS_Init() */ 43 44 45 /****************************************************************************/ 46 47 /* Add a task to the task table. 48 * Return the task ID, or gTsInvalidTaskID_c if the task table is full. 49 * 50 * taskPriority == 0 is reserved for the idle task, and must never be specified 51 * for any other task. TS_CreateTask() does not check for this. 52 * 53 * Note that TS_CreateTask() does not prevent a given event handler function 54 * pointer from being added more than once to the task table. 55 * 56 * Note that if TS_CreateTask() is called with a taskPriority that is the 57 * same as the priority of a task that is already in the task table, the 58 * two tasks will end up in adjacent slots in the table. Which one is 59 * called first by the scheduler is not specified. 60 */ 61 tsTaskID_t TS_CreateTask(tsTaskPriority_t taskPriority,pfTsTaskEventHandler_t pfTaskEventHandler) 62 { 63 index_t i; 64 index_t freeSlot = gTsInvalidTaskID_c; //默認為空ID -1 65 index_t sizeofTaskId = sizeof(maTsTaskIDsByPriority[0]); 66 67 /* Try to find a free slot in the task table. 發現任務表的空閑位置,索引保存到freeSlot*/ 68 for (i = 0, freeSlot = gTsInvalidTaskID_c;(i < NumberOfElements(maTsTaskTable)); ++i) { 69 if (maTsTaskTable[i].priority == gTsInvalidTaskPriority_c) { 70 freeSlot = i; 71 break; 72 } 73 } /* for (i = 0, freeSlot = 0xFF; ... */ 74 75 if (freeSlot == gTsInvalidTaskID_c) { 76 return gTsInvalidTaskID_c; // 任務表已滿 77 } 78 79 // 初始化任務條目 80 maTsTaskTable[freeSlot].events = 0; 81 maTsTaskTable[freeSlot].pfTaskEventHandler = pfTaskEventHandler; 82 maTsTaskTable[freeSlot].priority = taskPriority; 83 84 /* maTsTaskIDsByPriority is maintained in sorted order, so 1) find the */ 85 /* place where this new task should go; 2) move everything up; and 3) add */ 86 /* the new task. */ 87 88 /* 優先級從小到大,等級從高到低 */ 89 for (i = 0; i < NumberOfElements(maTsTaskIDsByPriority); i++) { 90 /* If the end of the table is reached, just add the new task. */ 91 if (maTsTaskIDsByPriority[i] == gTsInvalidTaskPriority_c) 92 { 93 break; // 如果里面存入的是非法的優先級,則說明結束 94 } 95 96 /* If all tasks from this point on have lower priorities than the task */ 97 /* being added, move the rest up and insert the new one. */ 98 99 /* 找到優先級比其大的第一個索引,之后將該索引(包含自己)后的數據都后移,空閑出此位置 */ 100 if (maTsTaskTable[maTsTaskIDsByPriority[i]].priority < taskPriority) { 101 memcpy(&maTsTaskIDsByPriority[i + 1], 102 &maTsTaskIDsByPriority[i], 103 (NumberOfElements(maTsTaskIDsByPriority) - i - 1) * sizeofTaskId); 104 break; 105 } 106 } /* for (i = 0; ... */ 107 maTsTaskIDsByPriority[i] = freeSlot; // 插入上面找到的索引位置 108 109 return freeSlot; 110 } /* TS_CreateTask() */ 111 112 113 114 /* Remove a task from the task table. */ 115 void TS_DestroyTask(tsTaskID_t taskID) 116 { 117 index_t i; 118 index_t sizeofTaskId = sizeof(maTsTaskIDsByPriority[0]); 119 120 if (maTsTaskTable[taskID].priority == gTsInvalidTaskPriority_c) { 121 return; 122 } 123 124 /* Mark this slot in the task descriptor table as unused. */ 125 /* 清除任務表中的記錄 */ 126 maTsTaskTable[taskID].priority = gTsInvalidTaskPriority_c; 127 128 /* Remove this task's ID from the priority table. Find it's position */ 129 /* in the table, and shift everything else down. */ 130 131 /* 找到優先級中的索引,將后面的索引前移,並將最后一個位置的索引標記為無效 */ 132 for (i = 0; i < NumberOfElements(maTsTaskIDsByPriority); i++) { 133 if (maTsTaskIDsByPriority[i] == taskID) { 134 memcpy(&maTsTaskIDsByPriority[i], 135 &maTsTaskIDsByPriority[i + 1], 136 (NumberOfElements(maTsTaskIDsByPriority) - i - 1) * sizeofTaskId); 137 138 /* Note that exactly one entry was removed. */ 139 maTsTaskIDsByPriority[NumberOfElements(maTsTaskIDsByPriority) - 1] = gTsInvalidTaskID_c; 140 break; 141 } 142 } 143 144 return; 145 } /* TS_DestroyTask() */ 146 147 // 查找是否有信號事件 148 bool_t TS_PendingEvents(void) { 149 index_t i; 150 151 for (i = 0; i < NumberOfElements(maTsTaskTable); ++i) { 152 if (( maTsTaskTable[i].priority != gTsInvalidTaskPriority_c) 153 && maTsTaskTable[i].events) { 154 return TRUE; 155 } 156 } 157 158 return FALSE; 159 } 160 // 發送信號事件 161 /* Send events to a task. */ 162 void TS_SendEvent(tsTaskID_t taskID,event_t events) 163 { 164 _Interrupt_disable(); // 關中斷 165 maTsTaskTable[taskID].events |= events; 166 _Interrupt_enable(); //開中斷 167 } /* TS_SendEvent() */ 168 169 // 任務輪詢函數,在主循環中調用 其他任務沒有事件發生時 就執行空閑任務 170 void TS_Scheduler(void) 171 { 172 index_t activeTask; 173 event_t events; 174 index_t i; 175 index_t taskID; 176 177 /* maTsTaskIDsByPriority[] is maintained in task priority order. If there */ 178 /* are fewer than the maximum number of tasks, the first gInvalidTaskID_c */ 179 /* marks the end of the table. */ 180 for (;;) { 181 /* Look for the highest priority task that has an event flag set. */ 182 activeTask = gTsIdleTaskID_c; // 如果未找到有效的信號事件,則調用空閑函數 183 for (i = 0; i < NumberOfElements(maTsTaskIDsByPriority); ++i) 184 { 185 taskID = maTsTaskIDsByPriority[i]; 186 if (taskID == gTsInvalidTaskID_c) { 187 break; 188 } 189 190 if (maTsTaskTable[taskID].events) { 191 activeTask = taskID; // 找到有信號事件的任務ID 192 break; 193 } 194 } 195 196 /* If there are no outstanding events, call the idle task. */ 197 _Interrupt_disable(); // 關中斷 198 events = maTsTaskTable[activeTask].events; // 取出信號 199 maTsTaskTable[activeTask].events = 0; // 清空原始信號 200 _Interrupt_enable(); //開中斷 201 202 (*maTsTaskTable[activeTask].pfTaskEventHandler)(events); // 調用對用的事件函數 203 } /* for (;;) */ 204 } /* TS_Scheduler() */ 205 206 /* 207 空閑任務
函數功能:統計空閑任務被執行的次數 208 */ 209 void IdleTask(event_t events) 210 { 211 static u16 count = 0; 212 delay_us(100); 213 count++; 214 if(10000 == count) 215 { 216 count = 0; 217 printf("Idle has ran 10000 times\r\n"); 218 } 219 }
測試函數:test_printf 和 LED_Flash
代碼如下:
enum TASK_ID //任務優先級枚舉 { IDLE_TASK_PRIORITY = 0, PRINTF_TASK_PRIORITY = 1, LED_FLASH_PRIORITY = 2, }; /* 打印測試程序 */ void test_printf(event_t events) { printf("hello world ...%02d\r\n",events); //打印事件 } /* LED燈閃爍 */ void LED_Flash(event_t events) { LED0 = !LED0; //燈閃爍 } //定時器3 1秒鍾 中斷服務程序 void TIM3_IRQHandler(void) { static u8 count = 0; if(TIM3->SR&0X0001)//溢出中斷 { LED1=!LED1; TS_SendEvent( LED_FLASH_PRIORITY,0x01); //發送事件到制定的任務 count++; if(count%2) { TS_SendEvent(PRINTF_TASK_PRIORITY,count); //發送事件到制定的任務 } } TIM3->SR&=~(1<<0);//清除中斷標志位 }
在主函數初始化區域執行TS_Init();在while循環調用 TS_Scheduler();就可以了
附上時間事件,類似於操作系統中的定時任務:
timer_event.h
1 #ifndef _TIMER_EVENT_H_ 2 #define _TIMER_EVENT_H_ 3 #include "sys.h" 4 #include "interface.h" 5 6 #define MAX_TIMER_EVENT_Q 5 7 8 #define CYCLE_EVENT 1L 9 #define COMMON_EVENT 0L 10 11 typedef struct 12 { 13 u8 type; // 事件類別,循環事件還是延時事件 14 u16 id; // 定時器編號 15 u16 timer; // 延時或周期時間計數器 16 u16 timerBackup; // 用於周期事件的時間計數備份 17 pfTimerEventHandler_t pfTimerEventHandler; //時間事件處理函數 盡量簡短 18 19 }timerEventType; 20 21 22 void SysTickHandler(void); //系統滴答時鍾處理函數 23 void addTimerEvent(u8 type, u16 id, u16 timer,pfTimerEventHandler_t fun);//添加時間事件 24 void delTimerEvent(u16 id); //刪除時間事件 25 26 #endif
實現過程,使用時在丹皮阿尼中添加一個1ms或者10ms的時間片的定時器任務,用來驅動時間事件:
#include "timer_event.h" #include "event.h" static u8 _timerEventTail = 0; //記錄當前隊列中時間事件的個數 static timerEventType _timerEvent[MAX_TIMER_EVENT_Q]; /* 函數描述:系統時間片處理函數 輸入參數; 無 輸出參數:無 返回值: 無 */ void SysTickHandler(void) { u8 i = 0; for(i=0; i<_timerEventTail; i++) { _timerEvent[i].timer--; if(_timerEvent[i].timer == 0) { _timerEvent[i].pfTimerEventHandler(_timerEvent[i].id);// 事件觸發處理函數 if(_timerEvent[i].type == CYCLE_EVENT) { // 循環事件,重新計數 _timerEvent[i].timer = _timerEvent[i].timerBackup; } else { // 延時事件,觸發后刪除 delTimerEvent(_timerEvent[i].id); } } } } /* 函數描述:添加定時器事件 輸入參數: type:定時器類型 id:定時器ID timer:定時時間 fun:回調函數 輸出參數:無 返回值:無 */ void addTimerEvent(u8 type, u16 id, u16 timer,pfTimerEventHandler_t fun) { _timerEvent[_timerEventTail].type = type; //事件類型 _timerEvent[_timerEventTail].id = id; //時間事件編號 _timerEvent[_timerEventTail].timer = timer; // 時間單位是系統Tick間隔時間 _timerEvent[_timerEventTail].timerBackup = timer; // 延時事件並不使用 _timerEvent[_timerEventTail].pfTimerEventHandler = fun; // 延時事件並不使用 _timerEventTail++; } /* 函數描述:刪除定時器任務 輸入參數:定時器ID 輸出參數: 無 返回值:無 */ void delTimerEvent(u16 id) { u8 i = 0,j; for(i=0; i<_timerEventTail; i++) { if(_timerEvent[i].id == id) { for(j=i; j<_timerEventTail; j++) { _timerEvent[j] = _timerEvent[j+1]; } _timerEventTail--; } } }