分享一种嵌入式系统的计划思绪,保证及时性和可抢占的任务调理模式,可用于代替RTOS系统,适用于各种32位单片机,方便搭建或移植,可读性强,易拓展和维护,占用更少的资源。
核心目标:
1、淘汰甚至取消硬延迟,以判断状态和倒计时的方式开启任务作为前台;
2、淘汰制止,尽量只保存串口制止和一个定时器制止作为背景。
基础思绪:
举例,按功能需求,前台必要运行TaskA、TaskB、TaskC三个任务,其中C为优先级更高的任务,在A或B运行时,C可以抢占运行,C运行结束后继续运行A或B。(条件:C任务运行用时要短,否则必要对C进行优化,制止影响及时性)
- 创建任务状态变量,用于跟进每个任务的状态信息,其中至少必要包括:
uint8_t flag;//任务状态,开启还是关闭
uint32_t delay;//任务运行倒计时,用于临时壅闭任务,这段时间可以运行其他任务
uint8_t step;// 淘汰甚至取消硬延迟的情况下,对单个任务必要分段,用switch语句的方式只运行任务的一小部分,中间的过分使用delay进行壅闭,step指示任务当前处于哪一段或将要跳转到哪一段
依次判断A、B任务的flag和delay,确定是否运行该任务;
在A、B任务内,为flag、delay、step赋值,进而实现任务的开关,壅闭和跳转的功能;
- 在定时器制止(以滴答定时器或创建的1ms定时器为例)内处理背景
判断每个任务的delay或另外声明的delay变量是否大于零,是则--,确保每1毫秒声明的壅闭用delay大于零时都可以减1,实现倒计时;
判断C任务的flag和delay,确定是否运行该任务,此时A或B未运行完也可以先运行C任务;
- 原先用于其他周期性触发的各种定时器任务,可以新增倒计时变量和任务delay一样在制止内减1,并在前台任务内,判断该变量和重新赋值的情况下代替原先的定时器任务。
基础思绪已经能够满足两个核心目标,但还可以从下面几个方面进行改进:
- 如何在需求增加或变动的情况下提升可拓展性,如新增的任务、switch语句更密集的分段;
- 如何更系统性的管理因达成核心目的而新增的各种变量,如switch语句状态、各种倒计时变量等等;
- 如何在多任务多分段流程非线性运行而是频仍跳转的情况下提升可读性;
- 如何在现有的项目上更效率的实现核心目标的思绪。
进阶思绪:
- 有系统的任务管理,以结构体的形式封装任务状态、以函数传参返回的形式封装任务状态的设置和查看;
- 任务调理,使用函数指针封装任务,以for循环的方式判断以及调理任务;
- 制止switch语句的臃肿,以switch语句嵌套switch语句的形式按逻辑或习惯进行多层嵌套;
具体的进阶思绪就不具体睁开描述了,本文档进入正题;假如将核心目标、基础思绪以及进阶思绪的逻辑部分剥离出来以工具的形式直接调用,保证系统的及时性并且还能实现方便搭建或移植,可读性强,易拓展和维护,占用更少的资源这些优点的难处将紧张集中在对功能需求的理解、实现以及工具的运用上了。(提前说明:工具的入门使用相对简单,但熟练的运用必要更多的耐心来理解)以下将工具暂命名为easyTask OS
静态库:task_lib.lib
头文件:task_lib.h
头文件内容介绍:
- //1、任务级别枚举
- enum
- {
- TASKLEV = 0,
- PPTASKLEV
- };
- //用于传参时指代任务的级别:在前台调度的普通任务、在后台调度的可抢占式任务
复制代码
- //任务状态枚举
- enum
- {
- TASK_DISABLE = 0,
- TASK_ENABLE,
- TASK_TIMELY,
- TASK_BUSY
- };
- //用于传参时需要设置的任务状态
复制代码
- //3、 倒计时级别枚举
- enum
- {
- COUNTMSLEV = 0,
- COUNTSECLEV
- };
- //用于传参时指代倒计时的级别:ms级倒计时、sec级倒计时(使用us级定时器作为后台时则相应变为us级和ms级倒计时,此时可用自定义的枚举进行表示,但对应的0和1不能改变)
复制代码
- //4、初始化
- bool TaskInit(uint8_t * infos);
- /*
- uint8_t infos[7]={普通任务数量,抢占式任务数量,ms级倒计时数量,sec级倒计时数量,正向累计数量,任务复杂度,允许的switch语句嵌套层数上限};
- //任务复杂度
- ms级倒计时数量和sec级倒计时数量累加不能超过254;
- 允许的switch语句嵌套层数上限不能超过254;
- 允许的switch语句嵌套层数上限设置为0时则自动改为1;
- */
复制代码
- //5、任务调度判断
- bool TaskisAllow(uint8_t lev,uint8_t point);
- void TaskDusted(void);
- /*
- if(TaskisAllow(lev,point))//判断lev级别point位置的任务是否允许调度
- {
- (*TaskFnc[point])();//函数指针的运用,执行任务
- TaskDusted();//执行完后的收尾工作,执行抢占式任务或需要对任务限制运行时一定要调用(在使用技巧补充部分会展开描述)
- }
- */
复制代码
- //6、任务状态返回
- uint8_t TaskFlag(uint8_t lev,uint8_t point);
- //返回lev级别point位置的任务状态,一般用于查询其他任务的状态
- uint8_t TaskStep(uint8_t steplev);
- //返回当前任务在steplev级别下的step值(steplev:switch语句的嵌套级别 step:switch语句的expression值,在使用技巧补充部分会展开描述)
- uint32_t TaskDelay(uint8_t lev,uint8_t point);
- //返回lev级别point位置的任务阻塞delay值,一般用于查询其他任务的阻塞
- uint32_t TaskOutDelay(void);
- //返回当前任务的超时延迟outdelay值
复制代码
- //7、任务管理
- bool TaskCtrl(uint8_t lev,uint8_t point,uint8_t flag,uint8_t step,uint32_t delay);
- //设置lev级别point位置的任务的状态、steplev为0时的step值、阻塞值
- bool TaskSet(uint8_t flag);
- //设置当前任务的状态
- bool TaskJump(uint8_t steplev,uint8_t step,uint32_t delay,uint32_t outdelay);
- //设备当前任务在steplev级别下的step值、阻塞延迟、超时延迟
- bool TaskOutCheck(uint32_t outdelay);
- //当前任务的超时判断,超时延迟<=outdelay时返回真,否则返回假
复制代码
- //8、倒计时管理
- bool CountSet(uint8_t countlev,uint8_t point,uint32_t delay);
- //设置倒计时级别为countlev时,point位置的倒计时的延迟delay
- bool CountCheck(uint8_t countlev,uint8_t point,uint32_t delay);
- //判断倒计时级别为countlev时,point位置的倒计时,延迟<=delay时返回真,否则返回假
- uint32_t CountDelay(uint8_t countlev,uint8_t point);
- //返回倒计时级别为countlev时,point位置的倒计时延迟值
复制代码
- //9、计数管理
- uint32_t CountAdd(uint8_t point);
- //point位置的正向累计加1,并返回加1后的累计值
- bool CountClear(uint8_t point);
- //point位置的正向累计清零
复制代码
- //10、阻塞管理
- void DelayFnc(void);
- //在1ms定时器中断内调用,系统的实时性精度将控制在1ms
- //或在1us定时器中断内调用,系统的实时性精度将控制在1us
复制代码
- //11、其他
- char *TaskHelp(void);//以 “%s”, TaskHelp()的形式使用,打印easyTask OS信息
- char *TaskShow_h(void);//以 “%s”, TaskShow_h ()的形式使用,打印整个.h文件内容
- //打印时uint8_t buf[]容量建议为2048
复制代码 使用技巧实例:
以下现实项目为例,使用easyTask OS实现功能需求;
外设:三个LED灯(从左至右依次为LED1、LED2、LED3),两个按键(KEY1,KEY2)
需求:
- 跑马灯,默认为从左到右依次点亮LED1、LED2、LED3、LED1…间隔为1秒,点亮1秒后熄灭紧接着点亮下一个LED;
- 按下KEY1(按下后并松开),改变点亮次序为从右至左,从现在点亮的LED处,熄灭后将点亮其左侧LED(LED1熄灭后点亮LED3),再次点按后改变点亮次序为从左至右,以此类推;
- 短按KEY2(2秒内松开),改变跑马灯间隔为2秒,再次点按后改变间隔为3秒,再次点按规复为1秒,以此类推;
- 长按KEY2凌驾2秒后,改变跑马灯间隔为0.5秒,长按松开后规复间隔为长按之前的间隔(1秒、2秒或3秒)
到这里发起停息浏览,可以思考自己会如何实现,大概会有更多的劳绩。
实现:
1、声明宏界说
- LED*_H;//LED*点亮
- LED*_L;//LED*熄灭
- LED*_T;//LED*翻转
- KEY*_R;//KEY*按下返回1否则返回0
复制代码 2、声明函数、函数指针、全局变量
- void LedTask(void);
- void KeyTask(void);
- void (*TaskFnc[])(void) =
- {
- LedTask,
- KeyTask,
- };
- uint8_t led_flags[2][3]={{1,2,0},{2,0,1}};
- uint8_t led_flag = 0;//点亮顺序
- uint16_t led_delay[4]={1000,2000,3000,500};
- uint8_t led_state[2]={0};//间隔(当前和上一次)
- uint8_t infos[7]={2,0,1,0,0,0,1};
复制代码 3、LED任务
- void LedTask(void)
- {
- switch(TaskStep(0))
- {
- case 0:
- LED1_H;
- LED2_L;
- LED3_L;
- break;
- case 1:
- LED1_L;
- LED2_H;
- LED3_L;
- break;
- case 2:
- LED1_L;
- LED2_L;
- LED3_H;
- break;
-
- }
- TaskJump(0,led_flags[led_flag][TaskStep(0)],led_delay[led_state[0]],0);
- }
复制代码 4、KEY任务
- void KeyTask(void)
- {
- switch(TaskStep(0))
- {
- case 0:
- TaskSet(TASK_BUSY);//运行限制取消
- if(KEY1_R)
- {
- if(CountCheck(COUNTMSLEV,0,0))//按键刚按下
- CountSet(COUNTMSLEV,0,10+10)
- else if(CountCheck(COUNTMSLEV,0,10))//完成消抖
- CountSet(COUNTMSLEV,0,10)//保持按下的状态
- }
- else
- {
- if(CountCheck(COUNTMSLEV,0,0)){}
- else if(CountCheck(COUNTMSLEV,0,10))
- {
- CountSet(COUNTMSLEV,0,0);
- if(led_flag)led_flag = 0;
- else led_flag = 1;
- }
- else CountSet(COUNTMSLEV,0,0); //消抖,倒计时清零
- }
- TaskJump(0,1,0, TaskOutDelay());//超时延迟继承,KEY2使用
- break;
- case 1:
- TaskSet(TASK_ENABLE);//运行限制开启
- if(KEY2_R)
- {
- if(TaskOutCheck(0))TaskJump(0,0,0,2000+10);//启用超时延迟,做消抖和长按判断
- else if(TaskOutCheck(10))//长按中
- {
- if(led_state[0] != 3)
- {
- led_state[1] = led_state[0];//保存长按前的间隔
- led_state[0] = 3;
- }
- TaskJump(0,0,0,10);//保持长按状态
- }
- else TaskJump(0,0,0,TaskOutDelay());//超时延迟继承,长按未持续两秒,未松开
- }
- else
- {
- if(TaskOutCheck(0)){};
- else if(TaskOutCheck(10))//长按结束
- {
- if(led_state[0] == 3)
- led_state[0] = led_state[1];
- }
- else if(TaskOutCheck(2000))//触发短按
- [
- led_state[0]++;
- if(led_state[0] >= 3)led_state[0] = 0;
- ]
- TaskJump(0,0,0,0);//超时延迟清零
- }
- break;
- }
- }
复制代码 5、main函数内
- /*
- 时钟、IO等初始化
- */
- uint8_t i;
- TaskInit(infos);
- TaskCtrl(0,0,1,0,0);
- TaskCtrl(0,1,1,0,0);
- while
- {
- for(i=0;i<2;i++)
- {
- if(TaskisAllow(0,i))
- {
- (*TaskFnc[i])();
- TaskDusted();
- }
- }
- }
复制代码 6、定时器制止内
关于easyTask OS的lib、h文件以及更具体的说明pdf,请见下面的链接:
easyTask OS: https://url97.ctfile.com/d/29116897-62189287-72ed38?p=4269 (访问密码: 4269)
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |