本帖最后由 FreeRTOS 于 2016-11-22 00:05 编辑
今天看到论坛里的一个求助帖子:
http://www.openedv.com/forum.php ... 39&page=1#pid505654
让我想起了之前一直想仿照contiki来解决传统状态机碰到多个延时不好处理的情况,今天恰好有心情说干就干!
相关资料请查阅contiki源码,本测试程序完全仿照contiki的算法实现,不过是简化版的,大神勿喷
关于状态机的基础知识我这里就不作详细讲解了,好了直接上源码:
[mw_shl_code=applescript,true]#define LINE_NUM_GET(num) num = __LINE__; case __LINE__:
#define PROCESS_BEGIN(s) switch(s) { case 0:
#define PROCESS_END() }
/* 任务结构体 */
typedef struct _TaskStruct
{
void (*TaskHook)(void); // 要运行的任务函数
uint32_t Counter; // 任务全局节拍
uint16_t LineNum; // 行号
uint8_t Run; // 程序运行标记:0-不运行,1运行
} TaskStruct;
TaskStruct Tasks[3];
#define PROCESS_DELAY(task, n) do
{
task.Counter = GlobalTimerCnt;
LINE_NUM_GET(task.LineNum);
if( (GlobalTimerCnt - task.Counter) > n )
{
break;
}
return;
} while(0)[/mw_shl_code]
如果对contiki没了解过的话,看起来会有些吃力,这里我大概说明下。
在每个任务的开头与结尾必须是 PROCESS_BEGIN(s)和PROCESS_END(),至于这两个宏是什么等下再解释
然后就是仿照我们平时使用OS时最常用方式:在while(1)里完成对应的任务
好了,接下来就是最关键的部分PROCESS_DELAY(task, n),这部分代码就是以状态机来实现类操作系统延时!
说太多口水都干了,直接贴几个任务看看使用方法,使用非常简单:
[mw_shl_code=applescript,true]/**********************************************/
/* */
/* 任务1 */
/* */
/**********************************************/
void test_process1(void)
{
PROCESS_BEGIN(Tasks[0].LineNum);
while (1)
{
PROCESS_DELAY(Tasks[0], 1000);
Uart1SendString("
rocess1 Delay1 Test!
");
PROCESS_DELAY(Tasks[0], 2000);
Uart1SendString("
rocess1 Delay2 Test!
");
PROCESS_DELAY(Tasks[0], 1500);
Uart1SendString("
rocess1 Delay3 Test!
");
}
PROCESS_END();
}
/**********************************************/
/* */
/* 任务2 */
/* */
/**********************************************/
void test_process2(void)
{
PROCESS_BEGIN(Tasks[1].LineNum);
while (1)
{
PROCESS_DELAY(Tasks[1], 700);
Uart1SendString("
rocess2 Delay1 Test!
");
PROCESS_DELAY(Tasks[1], 1500);
Uart1SendString("
rocess2 Delay2 Test!
");
}
PROCESS_END();
}
/**********************************************/
/* */
/* 任务3 */
/* */
/**********************************************/
void test_process3(void)
{
PROCESS_BEGIN(Tasks[2].LineNum);
while (1)
{
PROCESS_DELAY(Tasks[2], 500);
Led_Toggle(LED1);
}
PROCESS_END();
}[/mw_shl_code]
任务1与任务2是串口每隔一段时间打印出测试字符串,任务3是小灯最喜欢的跑灯任务,没有之一!!!
下面就是创建任务,主要是对结构体成员LineNum清零(必须)和设置任务回调函数:
[mw_shl_code=applescript,true]/* 任务初始化 */
memset((void*)Tasks, 0, sizeof(Tasks));
Tasks[0].TaskHook = test_process1;
Tasks[1].TaskHook = test_process2;
Tasks[2].TaskHook = test_process3;[/mw_shl_code]
好了接下来看执行,执行跟状态机一样,就是各个任务对应的函数轮着来,值得注意的是并没有任务在原地死等,所有任务都以查询的方式进入和退出!!!
[mw_shl_code=applescript,true]while(1)
{
/* 任务执行,本质还是状态机,但类似操作系统风格 */
for(i=0; i<3; i++)
{
Tasks
.TaskHook();
}
}[/mw_shl_code]
到这里就完成了以状态机的方式来实现类操作系统风格的延时!可能各位对代码一时摸不着头脑,我大概说下算法的思想。
把宏PROCESS_BEGIN()与PROCESS_END()展开可以发现其实就是一个switch结构,任务里面的延时调用了PROCESS_DELAY()之后
会把行号记录在任务结构成员LineNum,而算法的精髓也是在于这里,每次调用任务的回调函数时switch结构会跳转到记录行号的位置
然后查询任务结构体成员Counter的值是否已达到延时的节拍,如果还没延时完毕就直接return,任务不需要在原地死等。由于所有任务共用一个堆栈,因此任务中不能出现临时变量,如果实在需要使用变量,那么就遵从contiki的建议使用静态变量,
个人不建议使用全局变量,没别的原因,主要是看起来太TM烦!
算法的大概思想就是这样,小灯近段时间烦心事太多了,所以没有来得及检查代码,只是粗略测试了好像没问题,希望各位大神
能帮忙检查下然后把问题报告给我,我会第一时间修改!
小灯采用阿波罗STM32F7开发板做的测试,如果你们手头上的板子不是的话,要么光顾下原子网店,否则就自行修改下吧。
Process_Delay.rar
(1.08 MB, 下载次数: 1266)
2016-11-21 23:51 上传
点击文件名下载附件
如果delay之前的代码耗时过长的话,这个哪怕上OS一样解决不了啊,毕竟耗时的代码就在那里,理论上是不可能降低的,除非你改进这部分代码的耗时
记得使用PT协程这种方式的时候,任务中是禁止使用switch case的,如果需要用到switch的功能,只能用if else来替代,否则,内层的switch会caseC语言的行号,导致外层的switch失效。
你说的这个,去看看DJYOS,事件驱动完美解决,
一周热门 更多>