当前位置: 首页 > news >正文

[学习资料] 什么是状态机?怎么设计MCU状态机

1、什么是状态机?
状态机(State Machine, SM),也称为有限状态机(Finite State Machine, FSM),是嵌入式系统软件设计中一种非常重要的用来描述一个系统的行为模型和编程模式。
状态机由一组状态、一组输入事件和一组转换规则组成,系统在任何时刻都处于一种状态,当输入事件发生时,根据当前状态和转换规则,系统可能会转移到另一种状态。
因此,状态机主要包括以下几个基本要素:
状态(State):系统所处的不同模式或阶段,一个状态机至少要包含两个状态;
事件(Event):触发状态转换的条件或输入;
转换(Transition): 从一个状态到另一个状态的变化;
动作(Action):状态转换时执行的操作。
状态机广泛应用于嵌入式系统软件设计中,特别是在需要明确不同状态行为和事件处理的应用场景,包括协议开发、系统界面启动和按键操作等等。
所以,通过使用状态机,开发者可以更容易地构建出可靠、易维护且高效的嵌入式软件系统。
2、怎么设计MCU状态机?
状态机的特性非常适合用于MCU(微控制器)的程序设计,因此接下来作者就通过实际代码向大家阐述一下如何设计MCU状态机。
状态机设计的一般步骤如下所示:
先明确系统状态:
你首先需要分析和明确系统需要哪些状态,常见的但不仅限于以下状态:
初始化状态(INIT)
空闲状态(IDLE)
等待状态(WAIT)
运行状态(RUN)
错误状态(ERROR)
休眠状态(SLEEP)
写状态(WRITE)
读状态(READ)
……
再定义状态转换条件:
当触发什么事件时进行状态切换,常见的但不仅限于以下事件:
定时器超时
外部中断
数据接收完成
等待超时
延时时间到
计数器计数值到
错误、报警和故障发生
……
最后选择状态机实现方式:
常见的MCU状态机实现方式主要包括两种:
第一种方式:switch-case结构
这种方式应该是大家比较熟悉的一种吧,一般在头文件里面通过宏定义或者枚举方式定义好需要的状态机值,然后在源文件里用switch-case结构实现状态机的代码逻辑,示例代码如下:
复制
  1. //header file
  2. /*types of master commands*/
  3. #define FALLBACK 0x5A
  4. #define MASTERIDENT 0x95
  5. #define DEVICEIDENT 0x96
  6. #define DEVICE_STARTUP 0x97
  7. #define PD_OUTOUT_OPERATE 0x98
  8. #define DEVICE_OPERATE 0x99
  9. #define DEVICE_PREOPERATE 0x9A
  10. //source file
  11. switch(slave_state)
  12. {
  13. caseFALLBACK:
  14. {
  15. //to add your code
  16. break;
  17. }
  18. caseMASTERIDENT:
  19. {
  20. break;
  21. }
  22. caseDEVICEIDENT:
  23. {
  24. break;
  25. }
  26. caseDEVICE_STARTUP:
  27. {
  28. break;
  29. }
  30. casePD_OUTOUT_OPERATE:
  31. {
  32. break;
  33. }
  34. caseDEVICE_PREOPERATE:
  35. {
  36. break;
  37. }
  38. caseDEVICE_OPERATE:
  39. {
  40. break;
  41. }
  42. default:
  43. {
  44. break;
  45. }
  46. }
这种结构实现的状态机代码看上去比较简单明了易懂,但是如果状态机数量比较多的话,代码看上去就比较臃肿和繁琐。此时,你就可以考虑使用第二种方式来实现状态机。
第二种方式:状态表驱动结构
表驱动方法是一种让你可以在表中查找信息,而不必使用很多的逻辑语句(if-else或switch-case)来把它们找出来的方法。在简单的情况下,逻辑语句确实更简单更直接,但是随着逻辑链的复杂,表驱动法就变得越来越富有吸引力了。21ic论坛上也有专门介绍表驱动法的比较好的文章,比如链接https://bbs.21ic.com/icview-3441826-1-1.html。
那到底怎么用表驱动法实现状态机代码呢?请看代码结构:
复制
  1. //define communication command
  2. #define COMM_SERVER_SCAN 0x0001
  3. #define COMM_SERVER_INFO 0x0002
  4. #define COMM_SERVER_DATA 0x0003
  5. #define COMM_SERVER_DONE 0x0004
  6. //define struct
  7. typedefstruct
  8. {
  9. uint16_tuiCmd;//communication command
  10. void(*pHandler)(CommServerPack_t *pStr);//function pointer
  11. } CommServerHandler_t;
  12. //define struct array
  13. constCommServerHandler_t g_strCommServerHandler[] =
  14. {
  15. { COMM_SERVER_SCAN, fCommServerVendorInfo},
  16. {COMM_SERVER_INFO, fCommServerVendorInfo},
  17. {COMM_SERVER_DATA, fCommServerVendorInfo},
  18. {COMM_SERVER_DONE, fCommServerVendorInfo},
  19. {0,0},
  20. };
  21. // define communication info handle
  22. voidfCommServerVendorInfo(CommServerPack_t *pStr)
  23. {
  24. //add your code
  25. }
  26. //state machine application
  27. for(i =0;; i++)
  28. {
  29. if(g_strCommServerHandler[i].uiCmd ==0x00)
  30. {
  31. break;
  32. }
  33. elseif(strRecvPack.uiCmd == g_strCommServerHandler[i].uiCmd)
  34. {
  35. g_strCommServerHandler[i].pHandler(&strRecvPack);
  36. break;
  37. }
  38. }
当然,以上的代码结构只是一个比较简单的框架,你可以在此基础上对控制状态机的结构体进行扩展,以满足你的应用需求。
因此,使用表驱动法能够得到代码简洁、执行效率高、扩展性好和可维护性好等好处。
如果系统有多个任务,需要注意以下几点:
为每个功能模块设计独立的状态机;
使用优先级调度确保关键状态及时处理;
注意状态机间的通信和同步。
文章最后,提供一些MCU状态机的设计技巧:
保持状态机简洁:每个状态只做必要的事情;
明确状态转换条件:避免模糊的转换逻辑;
添加默认处理:处理未预料到的事件;
考虑错误恢复:设计错误状态和恢复路径;
记录状态历史:有助于调试复杂问题;
使用状态图工具:如UML状态图辅助设计。
通过合理设计和使用MCU状态机,将会让你的MCU程序结构更加清晰、易于维护和扩展,同时也会提高整套系统的可靠性和响实时性。

---------------------
作者:dffzh
链接:https://bbs.21ic.com/icview-3457184-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。
http://www.cnnetsun.cn/news/175191.html

相关文章:

  • 升压芯片很简单(一),快速选择升压芯片+利用升压芯片设计LED电源
  • 基于web的人才招聘网站设计 nodejs vue
  • 测试20个降AI率工具后,我找到了2个去ai痕迹效果好的网站,还有免费降AI额度。
  • Thinkphp和Laravel在线点餐系统的设计与实现vue
  • 现代cpp在传统内存分配上的改进
  • Java毕设项目:基于springboot的物业报修系统的设计与实现(源码+文档,讲解、调试运行,定制等)
  • 【计算机毕业设计案例】基于springboot的物业报修系统的设计与实现线上化的报修管理平台(程序+文档+讲解+定制)
  • Java毕设选题推荐:基于springboot的社区团购系统的设计与实现、拼团下单、配送调度、资金结算【附源码、mysql、文档、调试+代码讲解+全bao等】
  • Java计算机毕设之基于springboot的幼儿园管理系统的设计与实现为幼儿园(含普惠园、民办园、连锁园)设计的 “家园共育 + 日常运营 + 安全监管(完整前后端代码+说明文档+LW,调试定制等)
  • I/O多路复用
  • 视频播放器PotPlayer下载安装教程:超详细图文步骤(PC+安卓)
  • Semantic Kernel 实战系列(六) - Memory与向量存储
  • 一个基于 .NET MAUI 的开箱即用的 UI 组件库,可快速搭建面向业务的应用程序界面!
  • Semantic Kernel 实战系列(七) - 高级主题 - Agents 与多代理系统
  • LeetCode每日一题——K个一组翻转链表
  • 大模型后训练:中美路径与商业闭环|附56页PDF文件下载
  • 震惊!选对云服务器代理商,这5个关键指标必须知道!
  • 2025年度复盘与总结
  • ESA正式授予Sivers波束成形技术开发合同
  • 基于UKF-IMM无迹卡尔曼滤波与交互式多模型的轨迹跟踪算法matlab仿真,对比EKF-IMM和UKF
  • Java毕设项目:基于springboot的高校校园一卡通管理系统的设计与实现(源码+文档,讲解、调试运行,定制等)
  • 2025年最实用的3个免费降ai率工具和免费ai查重工具,不用焦虑ai率过高!
  • 计算机Java毕设实战-基于springboot村委办公管理系统 基于SpringBoot的乡村事务综合服务平台的设计与实现【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • Java毕设选题推荐:基于springboot的村务管理系统的设计与实现智慧村务管理系统的设计与实现【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 老派C++程式設計師 vs. 現代C++程式設計師:類型系統觀念的全面戰爭
  • 2025年论文去AI率工具合集:每天5次免费AIGC查重+1500字免费降AI!
  • MySQL 千万级表变更字段,要想不锁表,可以这么做!
  • 【毕业设计】基于springboot的校园零售管理系统的设计与实现(源码+文档+远程调试,全bao定制等)
  • 硬件自查自纠!十年前的电脑可能还可以再战十年
  • 一键配置 Web 前端开发环境(PowerShell 自动化脚本)