电子产业一站式赋能平台

PCB联盟网

搜索
查看: 23|回复: 0
收起左侧

从需求到状态机设计:3200字保姆式项目实例教程!

[复制链接]

291

主题

291

帖子

2201

积分

三级会员

Rank: 3Rank: 3

积分
2201
发表于 昨天 09:57 | 显示全部楼层 |阅读模式
关注公众号,回复“入门资料”获取单片机入门到高级开挂教程
开发板带你入门,我们带你飞

文 | 无际(微信:2777492857)
全文约3555字,阅读大约需要 10 分钟
之前直播,有人让我讲讲状态机,因为涉及内容比较多,当时一时半会没想好,口头表述不够系统。
           
而且要有具体需求去设计比较直观些,今天这篇文章,就以我们无际单片机的项目《WiFi+4G+LoRa 防盗报警主机》为例,讲解下状态机的设计。
           
报警主机这个设备是智能家居和安防系统的核心,功能丰富、技术多样,非常适合用来学习嵌入式软件设计的全流程。
           
我们将从需求分析开始,一步步梳理功能、设计状态机,最后用代码实现,确保你看完就能明白整个过程,并能动手实践。文章会用通俗易懂的语言,配上代码和注释,全文3000字以上,耐心看完,受益匪浅。
           
1.项目背景与需求分析
防盗报警主机是安防系统的大脑,它的任务是检测非法入侵并及时通知用户。
           
我们这个项目的主机支持 WiFi、4G和 LoRa 三种通信方式,并带锂电池应急供电,确保无论环境如何都能保持可靠的警报传递。
           
WiFi 和 4G 负责连接云平台,实现远程监控和通知;LoRa 是一种低功耗广域网技术,用于和家里的本地设备(如门磁探测器、遥控器、红外、烟感等)组网通信。
           
在动手设计之前,我们先把需求理清楚,满血版的功能,实在太多了,像通信、防盗报警、探测器组网、电话、短信、OTA等等。   
           
所以,我只提取其中一个防盗报警模式功能,去教大家入门状态机设计,方便理解。
           
通信功能:主机需要通过 WiFi 和 4G 与云平台交互,发送警报或接收用户指令;通过 LoRa 与门磁探测器、遥控器通信,获取传感器信号或用户控制信号。
           
防盗报警模式:系统支持多种工作模式,包括“撤防”(不检测入侵)、“在家布防”(只检测外部入侵)、“离家布防”(检测所有区域入侵)和“报警中”(触发警报并通知用户)。
           
状态机是一种非常适合嵌入式系统的设计方法,能让复杂的逻辑变得有条理,代码也更容易维护。
  
2.为什么要用状态机?
在嵌入式开发中,像防盗报警主机这样的设备需要根据用户指令、传感器信号和通信状态不断切换工作模式。如果用一堆 if-else 判断来写代码,逻辑很快就会乱成一团,调试和维护都特别痛苦。状态机(State Machine)就是专门解决这种问题的利器。
           
状态机的核心思想是把系统的工作模式定义为有限的“状态”,然后通过“事件”触发状态之间的切换。这样,代码逻辑变得清晰,每次只处理当前状态下的事件,既直观又容易扩展。
           
比如,我们可以定义“撤防”和“报警中”两个状态,当收到“入侵检测”事件时,从“撤防”切换到“报警中”,同时触发蜂鸣器和发送通知。这样的设计是不是一目了然?
           
接下来,我们就从需求出发,设计这个防盗报警主机的状态机,包括状态定义、事件设计和转移规则,最后再用代码实现。   
           
3.状态机的设计过程
定义状态
根据需求,防盗报警主机有几种明显的工作模式,我们直接把它们定义为状态:
?Idle(撤防):系统不检测任何入侵,用户可以随意活动,家里有人时常用这个模式。
?Armed_Home(在家布防):只检测外部区域(比如门窗)的入侵,忽略内部活动,适合晚上睡觉时使用。
?Armed_Away(离家布防):检测所有区域的入侵,家里没人时用这个模式。
?Alarm(报警中):检测到入侵后进入这个状态,触发蜂鸣器并发送警报通知。
           
这样,系统的状态就定下来了:Idle、Armed_Home、Armed_Away 和 Alarm。这四个状态覆盖了所有核心功能。
           
设计事件
状态机靠事件驱动,事件就是触发状态转换的“信号”。根据需求,我们定义以下事件:
           
?Arm_Home_Command:用户通过遥控器或手机发送“在家布防”命令。
?Arm_Away_Command:用户发送“离家布防”命令。   
?Disarm_Command:用户发送“撤防”命令。
?Intrusion_Detected:门磁探测器等传感器检测到入侵。
?Timeout:警报持续一段时间后自动停止。
           
这些事件会推动系统在不同状态之间切换,接下来我们就定义这些切换规则。
           
状态转移规则   
状态机的核心逻辑是“当前状态 + 事件 = 下一个状态”。我们逐个状态来看看它们在不同事件下的反应。
           
Idle 状态
?用户发送 Arm_Home_Command,系统进入 Armed_Home,开始监控外部区域。
?用户发送 Arm_Away_Command,系统进入 Armed_Away,监控所有区域。
           
在这个状态下,系统不检测入侵,所以 Intrusion_Detected 不会触发任何动作。
           
Armed_Home 状态
?收到 Intrusion_Detected(仅外部区域),系统进入 Alarm,触发警报。
?收到 Disarm_Command,系统回到 Idle,停止监控。
           
在家布防模式下,内部活动不会触发警报,这是和离家布防的关键区别。
               
Armed_Away 状态
?收到 Intrusion_Detected(任何区域),系统进入 Alarm。
?收到 Disarm_Command,系统回到 Idle。
           
离家布防模式下,所有入侵都会触发警报,保护力度最大。
           
Alarm 状态
?收到 Disarm_Command,系统回到 Idle,关闭警报。
?收到 Timeout,警报持续一段时间后自动停止,系统回到 Idle。
           
在这个状态下,系统会持续触发蜂鸣器并尝试通过 WiFi、4G 发送警报通知。
           
动作与行为     
状态切换时,系统还需要执行一些动作,比如:
?进入 Alarm 时:启动蜂鸣器、发送警报通知到云平台。
?回到 Idle 时:关闭所有警报和指示灯。
           
这些动作可以在状态转换时执行,也可以在进入状态时处理,具体实现时可以根据硬件需求调整。
               
4.代码实现
设计好状态机后,我们用 C 语言把它实现出来。嵌入式开发中,状态机通常用 switch-case 或函数指针实现,这里我们选简单的 switch-case,方便理解。
           
定义状态和事件
先定义状态和事件的枚举类型:
  • #include // 定义状态typedef enum {    STATE_IDLE,        // 撤防    STATE_ARMED_HOME,  // 在家布防    STATE_ARMED_AWAY,  // 离家布防    STATE_ALARM        // 报警中} State;// 定义事件typedef enum {    EVENT_ARM_HOME,    // 在家布防命令    EVENT_ARM_AWAY,    // 离家布防命令    EVENT_DISARM,      // 撤防命令    EVENT_INTRUSION,   // 入侵检测    EVENT_TIMEOUT      // 超时} Event;// 当前状态,初始为撤防State current_state = STATE_IDLE;               
    实现状态机逻辑
               
    接下来写一个函数 handle_event,根据当前状态和事件处理状态转换:
  • void handle_event(Event event) {    switch (current_state)     {        case STATE_IDLE:            if (event == EVENT_ARM_HOME)             {                printf("进入在家布防模式
    ");                current_state = STATE_ARMED_HOME;            } else if (event == EVENT_ARM_AWAY)             {                printf("进入离家布防模式
    ");                current_state = STATE_ARMED_AWAY;            }            break;        case STATE_ARMED_HOME:            if (event == EVENT_INTRUSION)             {                printf("外部入侵检测到,触发报警
    ");                current_state = STATE_ALARM; // 假设外部入侵检测逻辑已区分            } else if (event == EVENT_DISARM)             {                printf("从在家布防撤防
    ");                current_state = STATE_IDLE;            }            break;        case STATE_ARMED_AWAY:            if (event == EVENT_INTRUSION)             {                printf("入侵检测到,触发报警
    ");                current_state = STATE_ALARM;            } else if (event == EVENT_DISARM)             {                printf("从离家布防撤防
    ");                current_state = STATE_IDLE;            }            break;        case STATE_ALARM:            if (event == EVENT_DISARM)             {                printf("从报警状态撤防
    ");                current_state = STATE_IDLE;            } else if (event == EVENT_TIMEOUT)             {                printf("报警超时,回到撤防
    ");                current_state = STATE_IDLE;            }            break;        default:            printf("未知状态
    ");            break;    }}这段代码用 switch-case 实现了状态机的逻辑。每个状态下,根据传入的事件,系统会打印当前行为并更新 current_state。在实际项目中,printf 可以替换为硬件操作,比如控制蜂鸣器或发送网络数据。
               
    测试状态机
    我们来模拟一个使用场景:用户布防、检测到入侵、撤防、报警超时。主函数如下:
  • int main() {    printf("初始状态:撤防
    ");    handle_event(EVENT_ARM_AWAY);     // 用户发送离家布防命令    handle_event(EVENT_INTRUSION);    // 传感器检测到入侵    handle_event(EVENT_DISARM);       // 用户撤防    handle_event(EVENT_TIMEOUT);      // 超时(此时无作用)    return 0;}
    运行结果会是:
    初始状态:撤防
    进入离家布防模式
    入侵检测到,触发报警
    从报警状态撤防
    这个输出清晰地展示了状态机的每一步转换,完全符合我们设计的需求。
               
    当然,这个只是展示一个框架模型,无际单片机这个项目的功能,远远比这个复杂,细节很多,从头到尾开发的话,哪怕有经验的情况下,也要一年半载的。

    h13bphtxbic6403418713.png

    h13bphtxbic6403418713.png

               
    光状态机还不够,需要多种架构的组合,比如表驱动法+状态机。
                   
    5.状态机的优势与优化
    通过上面的设计和代码,我们可以看到状态机的几个优点:
    ?逻辑清晰:每个状态和事件都独立处理,代码结构一目了然,后面每个功能修改都互不影响。
    ?易于扩展:如果想加个“低电量警告”状态,只需在 State 枚举中加一项,再增加对应的 case 分支。
    ?调试方便:打印当前状态就能快速定位问题。
               
    不过,在实际应用中,还有一些细节需要注意。比如,如果状态和事件太多,可能导致“状态爆炸”,这时可以考虑用子状态机,把复杂状态拆分成更小的模块。
               
    另外,每个状态下的事件要尽量覆盖全面,避免遗漏;比如在 Alarm 状态下,可以加个定时器检查 Timeout,确保警报不会无限鸣响。
               
    通过这个防盗报警主机的项目,我们从需求分析开始,梳理了防盗模式的功能需求,接着设计了状态机,定义了状态和事件,最后用 C 语言实现了完整的逻辑。这个过程不仅展示了状态机的强大之处,也提供了一个可以直接套用到其他嵌入式项目的模板。
               
    在实际开发中,你可以根据硬件特性调整代码,比如用中断处理传感器信号,用 FreeRTOS 任务管理通信模块。状态机是个灵活的工具,只要掌握了它的设计思路,就能轻松应对各种复杂需求。希望这篇文章能给你带来启发,如果你有自己的项目想法,不妨试试用状态机设计一下,效果绝对超出预期!   
    end

    nm5ebsh1c1s6403418813.jpg

    nm5ebsh1c1s6403418813.jpg


    下面是更多无际原创的个人成长经历、行业经验、技术干货。
    1.电子工程师是怎样的成长之路?10年5000字总结
    2.如何快速看懂别人的代码和思维
    3.单片机开发项目全局变量太多怎么管理?
    4.C语言开发单片机为什么大多数都采用全局变量的形式
    5.单片机怎么实现模块化编程?实用程度让人发指!
    6.c语言回调函数的使用及实际作用详解

    7.手把手教你c语言队列实现代码,通俗易懂超详细!

    8.c语言指针用法详解,通俗易懂超详细!
  • 回复

    使用道具 举报

    发表回复

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则


    联系客服 关注微信 下载APP 返回顶部 返回列表