这个应该属于是状态机的范畴,看了一本《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--; } } }