事件与状态机
本节介绍
📝本节您将了解常用的软件架构,并将多个架构组合使用到项目中。
🏆本章⽬标
1️⃣了解常用的软件架构
2️⃣根据状态机写代码
常用的软件架构介绍
轮询结构
原理
主程序在一个无限循环中依次检查多个任务或外设状态,按固定顺序处理所有事件,无优先级机制。
案例
以按键扫描举个例子:
c
while(1) {
if(KEY1按下) 处理按键1;
if(KEY2按下) 处理按键2;
延时防抖(10ms);
}
1
2
3
4
5
2
3
4
5
常见问题
如果某个任务耗时过长,后续的任务全部延迟
优先级反转,重要任务可能排在循环末尾
中断结构
原理
通过硬件中断机制响应外部事件,立即暂停主程序,执行中断服务程序(ISR),完成后恢复主程序。主程序就是前台,中断服务程序就是后台。
案例
以串口中断接收举例子:
c
void UART_ISR() {
if(接收完成) 将数据存入缓冲区;
}
1
2
3
2
3
常见问题
中断风暴:高频中断导致系统卡死
程序过长:如执行复杂运算,阻塞其他中断
未清除中断标志:导致反复进入中断
状态机结构
原理
将任务分解为离散状态,通过事件触发状态迁移,典型实现方式:switch-case或查表法。
案例
c
switch(state)
{
case 待机状态:
低功耗
break;
case 发送状态:
发送数据
break;
case 接收状态:
接收数据
break;
// ...其他状态
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
常见问题
状态遗漏:未处理某个迁移条件
超时未处理:缺乏超时复位机制
全局变量滥用:导致状态耦合
操作系统结构
原理
通过实时操作系统(如FreeRTOS、uC/OS)管理多任务,提供任务调度、信号量、队列等机制。
案例
以传感器数据采集+显示举个例子:
c
void Task1() { // 高优先级任务
while(1) {
读取温度传感器();
vTaskDelay(100);
}
}
void Task2() { // 低优先级任务
while(1) {
LCD刷新显示();
vTaskDelay(500);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
常见问题
优先级反转:低优先级任务占用资源阻塞高优先级任务
栈溢出:任务栈分配不足导致内存踩踏
死锁:信号量使用不当导致任务相互等待
本项目架构
在实际开发中,单一的结构通常满足不了要求,因此经常会将几种结构混在一起使用。例如,本项目就使用的 轮询 + 中断 + 状态机
结构。
项目中涉及到的界面切换:
c
//定义页面
typedef enum {
DEFAULT_PAGE=0, // 首页(默认)
PID_PAGE, // 定速页
DISTANCE_PAGE, // 定距页
SET_PAGE, // 设置页
PARAMETER_PAGE // 调参页
} SystemPageShow;
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
首页状态机
![]() |
---|
定速状态机
![]() |
---|
定距状态机
![]() |
---|
设置状态机
![]() |
---|
调参状态机
![]() |
---|
中断前后台管理
前台任务
前台是在 main 中的 while 中执行。
![]() |
---|
后台任务
后台是在一个 20ms 定时器中断中执行。
![]() |
---|
事件管理器
整个项目涉及到的状态和变量要记录更新的频率很大,因此再加入一个事件管理,对一些状态的切换,变量的更新进行细致的管理。
相关代码
代码中涉及到的事件、功能、状态:
c
//定义功能
typedef enum {
NO_FUNCTION, // 无功能
SPEED_FUNCTION, // 定速功能
DISTANCE_FUNCTION, // 定距功能
} Function;
//定义触发事件
typedef enum {
IDLE_EVENT=0, //空闲
ENTER_EVENT, //进入事件
QUIT_EVENT, //退出事件
MOTOR_EVENT, //电机事件
LONG_PRESS_ADD_START_EVENT, //长按加开始事件
LONG_PRESS_SUBTRACT_START_EVENT, //长按减开始事件
LONG_PRESS_END_EVENT, //长按结束事件
} SystemEvent;
//定义电机状态
typedef enum{
MOTOR_STATUS_OFF=0, //电机关
MOTOR_STATUS_ON, //电机开
}MotorStatus;
//定义按键长按状态
typedef enum{
LONG_PRESS_END=0, //长按停止
LONG_PRESS_ADD_START, //长按加
LONG_PRESS_SUBTRACT_START, //长按减
}LongPressStatus;
//定义系统相关信息
typedef struct {
SystemPageShow show_state; //当前界面显示页
LongPressStatus long_press_state; //当前按键长按状态
int default_page_flag; //当前首页(默认页)选择的内容
int set_page_flag; //当前设置页选择的内容
MotorStatus motor_flag; //当前电机状态
Function function_state; //当前功能
} SystemStatus;
// 系统全局变量
SystemStatus system_status;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
事件流程图
![]() | ![]() |
---|---|
![]() | ![]() |
---|---|
状态机+中断效果
因代码修改较多难以描述。建议大家直接下载例程查看。