单片机(伪)多任务处理:时间片轮询法
引入
在新手刚刚入门时,通常使用的程序架构为裸机、顺序执行。也就是说把所有的功能放在一个while(1)
死循环中然后单片机不断循环执行。有必要的话会加上一些中断用来处理一些紧急事件或者外部的信号,构成经典的前后台系统。但是这样做有什么弊端呢?这是我在我踩过坑后意识到的:
- 有些程序完全不需要频繁执行,比如LED的刷新,按键的检测等。放在死循环中执行对CPU比较浪费。
- 随着功能的增多或者代码变得复杂你会感觉到程序越写越困难,循环中的功能模块有时需要和中断联动,各个模块之间也有可能需要联动起来。即使使用状态机也力不从心…
- 勉勉强强完成了任务,后期的维护也变得相当的麻烦。
踩过坑后的我痛定思痛:准备以后写程序都请出RTOS这个大手子。但是后来我又发现 有时你想要实现的功能刚好介于复杂与不复杂之间…咋理解呢,就是说用顺序执行,复杂了点,用RTOS吧好像也没必要…毕竟移植还是挺麻烦的。当然还有就是RTOS体量过大,有些单片机吃不下,或者吃下了但是也撑的吃不下自己写的代码了属于是小鸟胃这一块,不过目前我还没遇到过,我用的单片机基本上属于大卫戴这一块
时间片轮询法
为了优化上面这些问题,大佬们于是提出了一种基于时间片的裸机开发架构,我们可以利用一个定时器提供心跳,不断的进行计数。然后当定时时间一到,那么就可以开始执行相应的任务了。
Talk is cheap,Show me the code.
首先是.h头文件
1 |
|
然后是.c
1 | /** |
按照分而治之的思想,完全可以把任务函数重新写在一个task.c文件中,这样更加简洁美观。
采用上面的代码,个人认为比为每个功能函数提供一个flag,时间到达后将任务标志为置位。然后在main函数的循环中检查标志位状态(类似状态机)那种方法要方便。避免了一些重复的工作。
什么意思
比如我有三个功能:
- A:5ms执行一次
- B:10ms执行一次
- C:3ms执行一次
我们随机提拔一个定时器作为心跳时钟,说白了就是掐表的嘛。这个定时器一般使用基本定时器性价比高一点。这个定时器每隔1ms就叫一下,我们可以决定ABC在上电时候时是否执行,或者上电后延迟一个自己的任务周期再执行。比如我们设定上电时:仅A执行。如果我们忽略代码的执行时间,那么程序就是这么运行的:
A-1ms-1ms-1ms(C)-1ms-1ms(A)-1ms(C)-1ms-1ms-1ms(C)-1ms(A、B)--------
如果上一个功能模块已经执行完了,但下一个功能模块的定时时间还没到,便会产生空闲时间,这那些1ms后没有括号的就是CPU的空闲段。同样可以像RTOS那样把空闲时间给空闲任务。
注意当前延时了多少时间是一个函数执行完就直接开始计算的,我之前就理解为了一个时间片只有一个函数执行。实际上不是的。
这样的方法有什么不足?
- 首先,像这样的丐版RTOS,实时性并没有真正的RTOS高,不是说执行就执行,会有一些延迟。
- 如果某个任务运行时间超过一个时间片,它可能会一直占用CPU,导致后面的任务无法及时执行,从而影响系统的响应时间。然后一整个就乱了。
但是个人感觉如果实时性要求不高,也无伤大雅🤔
比如时间片为1ms。A运行时间为2ms,每隔4ms执行一次。B运行时间不计,每隔3ms运行一次。
(A1ms-1ms)-1ms(B)-1ms(A1ms-1ms)(B)-1ms-1ms(A1ms([1])-1ms)(B[2])-----
参考文章
- 嵌入式裸机设计思想——时间片轮裸机开发架构+状态机+定时器调度机制_基于状态机制 定时器-CSDN博客遵循 CC 4.0 BY-SA 版权协议
- STM32裸机-时间片任务轮询_时间片轮询-CSDN博客遵循 CC 4.0 BY-SA 版权协议