赞
踩
目录
裸机程序的设计模式可以分为:轮询、前后台、定时器驱动、基于状态机。
轮询:周期性地查询各个模块或函数的状态,适用于简单且响应时间要求不高的系统,但无法有效解决复杂函数之间的相互影响问题。
前后台:通过将关键任务放在前台处理,而非关键任务放在后台,以确保关键任务的优先执行。这种方式对优先级管理有效,但复杂函数之间的相互影响仍可能存在。
定时器驱动:通过定时器周期性地触发任务执行,适合需要定期执行任务的场景,但不能解决函数间相互影响的问题。
基于状态机:将系统的各种状态和状态转移定义清晰,通过状态切换来控制系统行为。这种方法理论上可以避免复杂函数之间的直接影响,但在实践中确实需要良好的设计和调试
前面三种方法都无法解决一个问题:假设有A、B两个都很耗时的函数,无法降低它们相互之间的影响。第4种方法可以解决这个问题,但是实践起来有难度。
可以理解为我们stm32的正常顺序编程,一步一步来,若是前面的条件有延时或者执行时间过长,就会导致后面的条件执行往后推迟。
在main函数中是一个while循环,里面依次调用2个函数,这两个函数相互之间有影响:如果“喂一口饭”太花时间,就会导致迟迟无法“回一个信息”;如果“回一个信息”太花时间,就会导致迟迟无法“喂下一口饭”。
使用轮询模式编写程序看起来很简单,但是要求while循环里调用到的函数要执行得非常快,在复杂场景里反而增加了编程难度。
- void main()
- {
- while (1)
- {
- 喂一口饭();
- 回一个信息();
- }
- }
所谓“前后台”就是使用中断程序。当主程序在执行过程中,若是另一程序触发中断,优先中断的程序进行执行。
会出现问题:中断程序要是执行过长,将会导致迟迟离不开中断,并且要是创建多个中断还会出现抢占优先级的情况。
- // 前后台程序
- void main()
- {
- while (1)
- {
- // 后台程序
- 喂一口饭();
- }
- }
-
- // 前台程序
- void 滴_中断()
- {
- 回一个信息();
- }
在这个场景里,给同事回复信息非常及时:即使正在喂饭也会暂停下来去回复信息。“喂一口饭”无法影响到“回一个信息”。但是,如果“回一个信息”太花时间,就会导致 “喂一口饭”迟迟无法执行。
继续改进,假设小孩吞下饭菜后会发出“啊”的一声,妈妈听到后才会喂下一口饭。喂饭、回复信息都是使用中断函数来处理。示例程序如下:
- // 前后台程序
- void main()
- {
- while (1)
- {
- // 后台程序
- }
- }
-
- // 前台程序
- void 滴_中断()
- {
- 回一个信息();
- }
-
- // 前台程序
- void 啊_中断()
- {
- 喂一口饭();
- }
main函数中的while循环是空的,程序的运行靠中断来驱使。如果电脑声音“滴”、小孩声音“啊”不会同时、相近发出,那么“回一个信息”、“喂一口饭”相互之间没有影响。在不能满足这个前提的情况下,比如“滴”、“啊”同时响起,先“回一个信息”时就会耽误“喂一口饭”,这种场景下程序遭遇到了轮询模式的缺点:函数相互之间有影响。(中断之间抢占优先级)
定时器驱动模式,是前后台模式的一种,可以按照不用的频率执行各种函数。比如需要每2分钟给小孩喂一口饭,需要每5分钟给同事回复信息。那么就可以启动一个定时器,让它每1分钟产生一次中断,让中断函数在合适的时间调用对应函数。示例代码如下:
- // 前后台程序: 定时器驱动
- void main()
- {
- while (1)
- {
- // 后台程序
- }
- }
-
- // 前台程序: 每1分钟触发一次中断
- void 定时器_中断()
- {
- static int cnt = 0;
- cnt++;
- if (cnt % 2 == 0)
- {
- 喂一口饭();
- }
- else if (cnt % 5 == 0)
- {
- 回一个信息();
- }
- }
这种模式适合调用周期性的函数,并且每一个函数执行的时间不能超过一个定时器周期。如果“喂一口饭”很花时间,比如长达10分钟,那么就会耽误“回一个信息”;反过来也是一样的,如果“回一个信息”很花时间也会影响到“喂一口饭”;这种场景下程序遭遇到了轮询模式的缺点:函数相互之间有影响。
当“喂一口饭”、“回一个信息”都需要花很长的时间,无论使用前面的哪种设计模式,都会退化到轮询模式的缺点:函数相互之间有影响。可以使用状态机来解决这个缺点,示例代码如下:
- // 状态机
- void main()
- {
- while (1)
- {
- 喂一口饭();
- 回一个信息();
- }
- }
在main函数里,还是使用轮询模式依次调用2个函数。
关键在于这2个函数的内部实现:使用状态机,每次只执行一个状态的代码,减少每次执行的时间,代码如下:
- void 喂一口饭(void)
- {
- static int state = 0;
- switch (state)
- {
- case 0:
- {
- /* 舀饭 */
- /* 进入下一个状态 */
- state++;
- break;
- }
- case 1:
- {
- /* 喂饭 */
- /* 进入下一个状态 */
- state++;
- break;
- }
- case 2:
- {
- /* 舀菜 */
- /* 进入下一个状态 */
- state++;
- break;
- }
- case 3:
- {
- /* 喂菜 */
- /* 恢复到初始状态 */
- state = 0;
- break;
- }
- }
- }
-
- void 回一个信息(void)
- {
- static int state = 0;
-
- switch (state)
- {
- case 0:
- {
- /* 查看信息 */
- /* 进入下一个状态 */
- state++;
- break;
- }
- case 1:
- {
- /* 打字 */
- /* 进入下一个状态 */
- state++;
- break;
- }
- case 2:
- {
- /* 发送 */
- /* 恢复到初始状态 */
- state = 0;
- break;
- }
- }
- }
以“喂一口饭”为例,函数内部拆分为4个状态:舀饭、喂饭、舀菜、喂菜。每次执行“喂一口饭”函数时,都只会执行其中的某一状态对应的代码。以前执行一次“喂一口饭”函数可能需要4秒钟,现在可能只需要1秒钟,就降低了对后面“回一个信息”的影响。
同样的,“回一个信息”函数内部也被拆分为3个状态:查看信息、打字、发送。每次执行这个函数时,都只是执行其中一小部分代码,降低了对“喂一口饭”的影响。
使用状态机模式,可以解决裸机程序的难题:假设有A、B两个都很耗时的函数,怎样降低它们相互之间的影响。但是很多场景里,函数A、B并不容易拆分为多个状态,并且这些状态执行的时间并不好控制。所以这并不是最优的解决方法,需要使用多任务系统。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。