状态机设计与嵌入式系统开发完整指南
从面向过程到面向对象,从理论到实践的全面解析
目录
- 第一章:引言------为什么状态机是嵌入式开发的核心
- 第二章:状态机基础理论
- 第三章:嵌入式平台的资源限制与设计取舍
- 第四章:FIFO队列与单片机通信中断
- 第五章:状态机设计模式详解
- 第六章:C语言实现面向对象状态机
- 第七章:FreeRTOS与状态机
- 第八章:层次状态机HSM高级应用
- 第九章:实战项目------智能小车状态机设计
- 第十章:嵌入式设计模式总结
- 第十一章:参考资源与开源项目
- 结语
第一章:引言------为什么状态机是嵌入式开发的核心
1.1 状态机在嵌入式系统中的普遍性
在嵌入式开发领域,状态机(State Machine)无疑是最重要、最核心的设计模式之一。毫不夸张地说,是否熟练掌握状态机,很大程度上直接决定了嵌入式工程师的代码掌控能力。在嵌入式开发中,几乎80%以上的程序都有状态机的影子。在一个思路清晰而且高效的程序中,必然有状态机的身影浮现。
很多嵌入式开发者只是掌握一些很基础的状态机编程,甚至认为状态机就是简单的switch-case语句,这是对状态机思想的极大误解。状态机不仅仅是一种编程技巧,更是一种系统化的思维方式,它帮助开发者将复杂的系统行为抽象为清晰的状态转换图,从而使代码结构更加清晰、易于维护和扩展。
1.2 从混乱到秩序:状态机的价值
在实际项目中,我们经常遇到这样的情况:代码逻辑错综复杂,各种标志位(flag)满天飞,条件判断嵌套层次过深,导致代码难以理解和维护。这种状态通常被称为"意大利面条式代码"(Spaghetti Code)。状态机的引入可以将这种混乱的状态梳理成清晰的结构。
举个例子,一个简单的按键检测功能,如果不使用状态机,可能会写成这样:
// 混乱的按键检测代码
void key_scan(void) {
static uint8_t key_cnt = 0;
static uint8_t key_pressed = 0;
if (GPIO_ReadInputDataBit(KEY_GPIO, KEY_PIN) == 0) {
key_cnt++;
if (key_cnt > 20 && !key_pressed) {
key_pressed = 1;
// 执行按键动作
}
} else {
if (key_cnt > 0) {
key_cnt--;
if (key_cnt == 0 && key_pressed) {
key_pressed = 0;
// 按键释放处理
}
}
}
}
而使用状态机重构后,代码变得清晰易读:
// 状态机版本的按键检测
typedef enum {
KEY_STATE_IDLE, // 空闲状态
KEY_STATE_DEBOUNCE, // 消抖状态
KEY_STATE_PRESSED, // 按下状态
KEY_STATE_RELEASE // 释放状态
} KeyState;
void key_state_machine(KeyState *state) {
switch (*state) {
case KEY_STATE_IDLE:
if (is_key_pressed()) {
*state = KEY_STATE_DEBOUNCE;
start_debounce_timer();
}
break;
case KEY_STATE_DEBOUNCE:
if (debounce_timeout()) {
if (is_key_pressed()) {
*state = KEY_STATE_PRESSED;
on_key_pressed();
} else {
*state = KEY_STATE_IDLE;
}
}
break;
case KEY_STATE_PRESSED:
if (!is_key_pressed()) {
*state = KEY_STATE_RELEASE;
on_key_released();
}
break;
case KEY_STATE_RELEASE:
*state = KEY_STATE_IDLE;
break;
}
}
1.3 面向过程与面向对象的思考
用户提到的一个重要问题是:"好多的那种面对过程的好像都是走那种状态机的,他好像弄不了那个面向对象的那个设计"。这其实是一个常见的误解。
状态机一定是面向对象的------这句话道出了本质。状态机的设计理念就是基于对象的。只不过像C语言这样的语言本身不直接支持面向对象,但可以通过结构体、函数指针等机制做到类似对象的效果。
在C++、Java、Python等支持面向对象的语言中,状态机可以自然地用类、继承、多态来实现。而在C语言中,我们需要用结构体来模拟类,用函数指针来模拟方法,用函数指针数组来实现多态。
1.4 嵌入式平台的特殊性
嵌入式开发与PC端开发最大的区别在于资源受限。用户提到的观点非常准确:
"各种语言由于平台的不同,涉及一些功能优化,比如PC端由于性能比较高,支持try catch throw这种异常模式,但这种异常模式在资源受限的嵌入式平台是不允许使用的。还包括一些运行时解析像C++的auto、容器的typefromid这些功能都是要根据部署和开发平台规划使用的。"
在嵌入式平台(如STM32、ESP32、MSP430等)上,我们需要考虑:
- 内存限制:RAM可能只有几KB到几百KB
- Flash限制:程序存储空间可能只有几十KB到几MB
- 实时性要求:必须在确定的时间内响应外部事件
- 功耗限制:电池供电设备需要尽可能降低功耗
- 可靠性要求:工业控制、医疗设备等场景要求极高的稳定性
这些限制决定了我们在嵌入式平台上使用设计模式时需要做出取舍。例如:
- 禁用异常处理 :使用
-fno-exceptions编译选项,用错误码替代try/catch - 禁用RTTI :使用
-fno-rtti编译选项,避免运行时类型信息的开销 - 避免动态内存分配:使用静态分配或内存池,防止内存碎片
- 避免虚函数表开销:在极度资源受限的场景,慎用C++多态
1.5 状态机的五个基本状态
在嵌入式开发中,特别是引入了RTOS(如FreeRTOS)的系统,状态机通常围绕五个基本状态展开:
- 开始(Start/Init):系统初始化,配置硬件,建立任务
- 空闲(Idle):系统等待事件,可以进入低功耗模式
- 运行(Running):正常执行业务逻辑
- 停止(Stop):暂停业务,但保持状态
- 异常(Error/Exception):处理错误情况,可能进入安全模式或重启
这五个大状态构成了嵌入式系统的生命周期,然后根据业务需求可以套子状态机,做到低耦合。
1.6 本文的结构与目标
本文将从国内技术资源出发,系统性地介绍状态机的理论和实践,包括:
- 状态机的基础理论和数学模型
- 嵌入式平台的资源限制和设计取舍
- FIFO队列和中断通信机制
- 各种状态机设计模式的详细解析
- C语言实现面向对象状态机的技巧
- FreeRTOS与状态机的结合
- 层次状态机HSM的高级应用
- 智能小车等实际项目的完整案例
- 国内优秀的参考博文和开源项目
目标是帮助读者建立完整的状态机知识体系,能够在实际项目中灵活运用状态机思想,写出高质量、易维护的嵌入式代码。
第二章:状态机基础理论
2.1 有限状态机的定义
有限状态机(Finite State Machine,FSM)是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型。
一个完整的FSM由以下五个要素组成:
- 状态(State):系统在某一时刻的稳定状况
- 事件(Event):触发状态转换的外部或内部信号
- 转换(Transition):从一个状态到另一个状态的变化
- 动作(Action):状态转换时或进入/退出状态时执行的操作
- 条件(Guard):决定是否允许转换的布尔表达式
用数学语言描述,一个FSM可以表示为一个五元组:
M = (S, E, T, s0, F)
其中:
- S:有限状态集合
- E:有限事件集合
- T:转换函数,T: S × E → S
- s0:初始状态,s0 ∈ S
- F:终止状态集合(可选),F ⊆ S
2.2 Mealy机与Moore机
在状态机理论中,有两种经典的模型:
Mealy机
Mealy机的输出不仅取决于当前状态,还取决于输入事件。其输出函数为:
λ: S × E → Λ
其中Λ是输出字母表。
特点:
- 输出与事件同步
- 通常需要较少的状态
- 输出可能在状态转换时发生变化
Moore机
Moore机的输出仅取决于当前状态,与输入事件无关。其输出函数为:
λ: S → Λ
特点:
- 输出与状态同步
- 通常需要更多的状态
- 输出在状态稳定时保持不变
实际应用中的选择
在嵌入式开发中,Mealy机和Moore机往往会混合使用:
- Moore特性:进入/退出动作(Entry/Exit Action)
- Mealy特性:转换动作(Transition Action)
例如,在Zephyr SMF框架中,每个状态可以定义entry、run、exit三个动作,其中entry和exit是Moore特性,而run中处理事件并触发转换则是Mealy特性。
2.3 状态机的表示方法
2.3.1 状态转换表
状态转换表是一种表格化的表示方法,清晰地列出所有状态和事件的组合:
| 当前状态 | 事件A | 事件B | 事件C |
|---|---|---|---|
| 状态1 | 状态2/动作1 | - | 状态3/动作2 |
| 状态2 | - | 状态1/动作3 | 状态3/动作4 |
| 状态3 | 状态1/动作5 | 状态2/动作6 | - |
2.3.2 状态转换图
状态转换图(State Transition Diagram)是更直观的图形化表示,使用UML状态图规范:
- 状态:用圆角矩形表示
- 初始状态:用实心圆表示
- 终止状态:用同心圆表示
- 转换:用带箭头的线表示,标注触发事件和动作
+---------+
+-------------->| 状态1 |<--------------+
| +---------+ |
| | | |
事件A 事件B 事件C
| | | |
| v v |
| +---------+ |
+---------------| 状态2 |---------------+
+---------+
2.3.3 状态嵌套与层次
UML状态图支持状态嵌套,这是层次状态机(Hierarchical State Machine,HSM)的基础:
+------------------+
| 父状态 |
| +------------+ |
| | 子状态1 | |
| +------------+ |
| +------------+ |
| | 子状态2 | |
| +------------+ |
+------------------+
子状态继承父状态的行为,这是实现代码复用的关键机制。
2.4 状态机的实现方式
2.4.1 Switch-Case实现
最简单直接的方式:
typedef enum {
STATE_IDLE,
STATE_RUNNING,
STATE_PAUSED,
STATE_STOPPED
} State;
void state_machine_run(State *current_state, Event event) {
switch (*current_state) {
case STATE_IDLE:
if (event == EVENT_START) {
do_init();
*current_state = STATE_RUNNING;
}
break;
case STATE_RUNNING:
if (event == EVENT_PAUSE) {
do_pause();
*current_state = STATE_PAUSED;
} else if (event == EVENT_STOP) {
do_stop();
*current_state = STATE_STOPPED;
}
break;
case STATE_PAUSED:
if (event == EVENT_RESUME) {
do_resume();
*current_state = STATE_RUNNING;
} else if (event == EVENT_STOP) {
do_stop();
*current_state = STATE_STOPPED;
}
break;
case STATE_STOPPED:
if (event == EVENT_START) {
do_init();
*current_state = STATE_RUNNING;
}
break;
}
}
优点:
- 简单直观,易于理解
- 执行效率高
- 适合状态数量少的场景
缺点:
- 状态多时switch-case过长
- 状态逻辑分散在case语句中
- 不易扩展和维护
2.4.2 查表法实现
将状态转换信息存储在表格中:
typedef struct {
State current_state;
Event event;
State next_state;
void (*action)(void);
} Transition;
const Transition transition_table[] = {
{STATE_IDLE, EVENT_START, STATE_RUNNING, do_init},
{STATE_RUNNING, EVENT_PAUSE, STATE_PAUSED, do_pause},
{STATE_RUNNING, EVENT_STOP, STATE_STOPPED, do_stop},
{STATE_PAUSED, EVENT_RESUME, STATE_RUNNING, do_resume},
{STATE_PAUSED, EVENT_STOP, STATE_STOPPED, do_stop},
{STATE_STOPPED, EVENT_START, STATE_RUNNING, do_init},
};
void state_machine_run(State *current_state, Event event) {
for (int i = 0; i < sizeof(transition_table)/sizeof(Transition); i++) {
if (transition_table[i].current_state *current_state &&
transition_table[i].event event) {
if (transition_table[i].action) {
transition_table[i].action();
}
*current_state = transition_table[i].next_state;
return;
}
}
// 未找到匹配的转换,可以记录错误或忽略
}
优点:
- 状态转换逻辑集中管理
- 易于修改和扩展
- 可以动态修改转换表
缺点:
- 需要遍历查找,效率略低
- 表格占用内存
- 不支持复杂的条件判断
2.4.3 函数指针实现
每个状态用一个函数表示:
typedef State (*StateFunc)(Event);
State state_idle(Event event) {
if (event EVENT_START) {
do_init();
return STATE_RUNNING;
}
return STATE_IDLE;
}
State state_running(Event event) {
if (event EVENT_PAUSE) {
do_pause();
return STATE_PAUSED;
} else if (event == EVENT_STOP) {
do_stop();
return STATE_STOPPED;
}
return STATE_RUNNING;
}
// 状态函数表
StateFunc state_table[] = {
[STATE_IDLE] = state_idle,
[STATE_RUNNING] = state_running,
[STATE_PAUSED] = state_paused,
[STATE_STOPPED] = state_stopped,
};
void state_machine_run(State *current_state, Event event) {
*current_state = state_table[*current_state](event);
}
优点:
- 高内聚,每个状态的行为封装在各自函数中
- 模块化程度高
- 易于单元测试
缺点:
- 函数指针使用需要谨慎
- 可能影响代码可读性
- 函数调用有轻微开销
2.5 状态机的常见问题
2.5.1 状态爆炸
当系统复杂度增加时,状态数量可能呈指数级增长,这就是"状态爆炸"问题。
解决方案:
- 使用层次状态机(HSM)进行状态分组
- 提取共性行为到父状态
- 使用正交区域(Orthogonal Regions)表示并发状态
2.5.2 状态转换的完整性
需要确保:
- 每个状态对所有可能事件都有处理
- 或者明确忽略某些事件
- 使用默认处理函数捕获未处理事件
void state_machine_run(State *current_state, Event event) {
State next_state = state_table[*current_state](event);
if (next_state == STATE_UNHANDLED) {
// 事件未被处理,记录日志或执行默认操作
log_unhandled_event(*current_state, event);
} else {
*current_state = next_state;
}
}
2.5.3 状态机的可测试性
好的状态机设计应该易于测试:
- 状态转换应该是确定性的
- 避免在状态机中使用全局变量
- 提供查询当前状态的接口
- 支持模拟事件注入
第三章:嵌入式平台的资源限制与设计取舍
3.1 嵌入式系统的资源约束
嵌入式系统与通用计算机系统最大的区别在于资源受限。理解这些限制对于设计高效的状态机至关重要。
3.1.1 内存限制
典型的嵌入式MCU内存配置:
| MCU系列 | RAM | Flash | 典型应用 |
|---|---|---|---|
| STM32F103C8 | 20KB | 64KB | 工业控制 |
| STM32F407VG | 192KB | 1MB | 图像处理 |
| ESP32 | 520KB | 4MB | IoT设备 |
| MSP430G2553 | 512B | 16KB | 低功耗传感器 |
在这样的资源限制下,我们需要:
- 避免大数组和缓冲区
- 谨慎使用递归(会消耗栈空间)
- 优先使用静态分配
- 考虑内存对齐和填充
3.1.2 实时性要求
嵌入式系统通常有严格的实时性要求:
- 硬实时:必须在截止时间内完成,否则系统失效(如汽车ABS系统)
- 软实时:偶尔超时可以接受,但会影响性能(如音频播放)
状态机的设计需要保证:
- 状态转换的执行时间是确定的
- 避免在状态机中执行耗时操作
- 中断服务程序(ISR)必须快速完成
3.1.3 功耗限制
电池供电设备对功耗极其敏感:
- 运行模式:全速运行,功耗最高
- 睡眠模式:CPU停止,外设运行
- 深度睡眠:大部分外设关闭,仅保留唤醒源
状态机可以配合功耗管理:
typedef enum {
PWR_STATE_ACTIVE, // 全速运行
PWR_STATE_SLEEP, // 轻度睡眠
PWR_STATE_DEEP_SLEEP // 深度睡眠
} PowerState;
void power_state_machine(PowerState *state, Event event) {
switch (*state) {
case PWR_STATE_ACTIVE:
if (event == EVENT_IDLE_TIMEOUT) {
enter_sleep_mode();
*state = PWR_STATE_SLEEP;
}
break;
case PWR_STATE_SLEEP:
if (event == EVENT_INTERRUPT) {
exit_sleep_mode();
*state = PWR_STATE_ACTIVE;
} else if (event == EVENT_LONG_IDLE) {
enter_deep_sleep();
*state = PWR_STATE_DEEP_SLEEP;
}
break;
case PWR_STATE_DEEP_SLEEP:
if (event == EVENT_WAKEUP) {
system_wakeup();
*state = PWR_STATE_ACTIVE;
}
break;
}
}
3.2 C++在嵌入式中的取舍
3.2.1 禁用异常处理
C++的异常处理机制在嵌入式中通常被禁用,原因如下:
- 代码体积增加:异常处理需要额外的运行时支持代码
- 栈展开开销:异常发生时需要遍历栈帧,耗时不可预测
- 内存占用:每个包含异常处理的函数都需要额外的元数据
GCC编译选项:
arm-none-eabi-g++ -mcpu=cortex-m3 -mthumb -fno-exceptions -Os -o firmware.elf main.cpp
禁用异常后,使用错误码替代:
// 不使用异常
class Result {
public:
bool success;
int error_code;
// ...
};
Result init_device() {
if (!hardware_check()) {
return {false, ERROR_HARDWARE_FAIL};
}
return {true, 0};
}
// 使用时
Result r = init_device();
if (!r.success) {
handle_error(r.error_code);
}
3.2.2 禁用RTTI
运行时类型信息(RTTI)包括dynamic_cast和typeid,同样需要禁用:
arm-none-eabi-g++ -fno-rtti -fno-exceptions ...
禁用RTTI后:
- 使用
static_cast替代dynamic_cast - 手动实现类型检查机制
- 避免多态容器的类型查询
3.2.3 模板的使用
模板在嵌入式中可以使用,但需要注意:
- 代码膨胀:每个模板实例都会生成独立的代码
- 编译时间:复杂模板会增加编译时间
- 调试困难:模板错误信息难以阅读
建议:
- 使用显式实例化控制代码大小
- 避免深层嵌套的模板
- 优先使用简单的泛型编程
3.3 设计模式的嵌入式适配
不是所有设计模式都适合嵌入式,需要根据资源限制做出选择。
3.3.1 适合嵌入式的设计模式
- 单例模式(Singleton)
- 确保全局唯一的实例
- 常用于设备驱动、配置管理器
- 注意线程安全问题
// 线程安全的单例(FreeRTOS环境)
typedef struct {
// 配置数据
} ConfigManager;
static ConfigManager *g_instance = NULL;
static SemaphoreHandle_t g_mutex = NULL;
ConfigManager* config_manager_get_instance(void) {
if (g_mutex == NULL) {
g_mutex = xSemaphoreCreateMutex();
}
xSemaphoreTake(g_mutex, portMAX_DELAY);
if (g_instance == NULL) {
g_instance = pvPortMalloc(sizeof(ConfigManager));
// 初始化...
}
xSemaphoreGive(g_mutex);
return g_instance;
}
-
观察者模式(Observer)
- 一对多的依赖关系
- 用于事件通知机制
- 注意避免循环依赖
-
状态模式(State)
- 本文的核心内容
- 将状态封装为独立的类/结构体
-
策略模式(Strategy)
- 定义算法族,分别封装
- 运行时切换算法
3.3.2 需要谨慎使用的模式
-
工厂模式
- 动态创建对象需要内存分配
- 可以考虑静态工厂
-
装饰器模式
- 多层包装增加复杂性和开销
- 嵌入式中尽量简化
-
代理模式
- 额外的间接层影响性能
- 仅在必要时使用
3.3.3 不推荐的模式
-
原型模式
- 深拷贝操作开销大
- 嵌入式中很少使用
-
享元模式
- 需要维护共享对象池
- 增加代码复杂度
3.4 内存管理策略
3.4.1 静态分配vs动态分配
推荐静态分配:
// 推荐:静态分配
static uint8_t g_buffer[256];
static TaskHandle_t g_task_handle;
// 不推荐:动态分配
uint8_t *buffer = malloc(256); // 可能失败,产生碎片
3.4.2 内存池
如果需要动态分配,使用内存池:
#define POOL_SIZE 10
#define BLOCK_SIZE 32
typedef struct {
uint8_t buffer[BLOCK_SIZE];
bool used;
} MemoryBlock;
static MemoryBlock g_pool[POOL_SIZE];
void* pool_alloc(void) {
for (int i = 0; i < POOL_SIZE; i++) {
if (!g_pool[i].used) {
g_pool[i].used = true;
return g_pool[i].buffer;
}
}
return NULL; // 池耗尽
}
void pool_free(void *ptr) {
for (int i = 0; i < POOL_SIZE; i++) {
if (g_pool[i].buffer == ptr) {
g_pool[i].used = false;
return;
}
}
}
3.4.3 栈空间管理
RTOS任务栈大小需要仔细评估:
// 使用uxTaskGetStackHighWaterMark监控栈使用
void task_function(void *pvParameters) {
while (1) {
UBaseType_t remaining = uxTaskGetStackHighWaterMark(NULL);
printf("Stack remaining: %u words\n", remaining);
// 任务逻辑...
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
// 创建任务时合理分配栈空间
xTaskCreate(task_function, "Task", 512, NULL, 1, NULL);
3.5 代码优化技巧
3.5.1 编译器优化
# 优化代码大小
-Os
# 优化执行速度
-O2
# 链接时优化
-flto
# 移除未使用的代码
-ffunction-sections -fdata-sections
-Wl,--gc-sections
3.5.2 数据结构优化
- 使用位域节省内存:
typedef struct {
uint32_t is_running : 1;
uint32_t is_paused : 1;
uint32_t error_code : 6;
uint32_t data : 24;
} Status; // 仅占用4字节
- 使用
const将数据放入Flash:
const char *const error_messages[] = {
[ERROR_NONE] = "No error",
[ERROR_TIMEOUT] = "Timeout",
[ERROR_HARDWARE] = "Hardware failure",
};
3.5.3 算法优化
- 用查表法替代复杂计算
- 避免浮点运算(如果MCU没有FPU)
- 使用定点数替代浮点数
- 循环展开减少分支
第四章:FIFO队列与单片机通信中断
4.1 为什么需要FIFO和中断
在单片机通信中,数据通常是异步到达的。如果不使用中断和缓冲区,CPU必须不断轮询检查是否有新数据,这种方式效率极低。
4.1.1 轮询方式的弊端
// 低效的轮询方式
void uart_receive_poll(void) {
while (1) {
if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE)) {
uint8_t data = USART_ReceiveData(USART1);
process_data(data);
}
// CPU一直在忙等,无法做其他事情
}
}
问题:
- CPU占用率100%
- 功耗高
- 响应延迟不确定
4.1.2 中断+FIFO的优势
// 高效的中断方式
void USART1_IRQHandler(void) {
if (USART_GetITStatus(USART1, USART_IT_RXNE)) {
uint8_t data = USART_ReceiveData(USART1);
fifo_write(&rx_fifo, data); // 快速存入FIFO
}
}
// 主循环处理
void main_loop(void) {
while (1) {
uint8_t data;
if (fifo_read(&rx_fifo, &data)) {
process_data(data); // 异步处理
}
// 可以做其他事情
}
}
优势:
- CPU仅在数据到达时被中断
- 可以进入低功耗模式
- 响应及时
4.2 环形FIFO的原理
环形FIFO(First In First Out)是最常用的缓冲区结构,也称为环形缓冲区(Ring Buffer)。
4.2.1 基本结构
typedef struct {
uint8_t *buffer; // 缓冲区指针
uint16_t size; // 缓冲区大小
volatile uint16_t head; // 写指针(队头)
volatile uint16_t tail; // 读指针(队尾)
} RingFIFO;
4.2.2 空满判断
环形FIFO的关键是区分空和满的状态。常用方法是"牺牲一个单元":
bool fifo_is_empty(RingFIFO *fifo) {
return fifo->head fifo->tail;
}
bool fifo_is_full(RingFIFO *fifo) {
return ((fifo->head + 1) % fifo->size) fifo->tail;
}
当(head + 1) % size == tail时,认为FIFO已满。这样FIFO最多存储size - 1个元素。
4.2.3 读写操作
bool fifo_write(RingFIFO *fifo, uint8_t data) {
if (fifo_is_full(fifo)) {
return false; // FIFO已满
}
fifo->buffer[fifo->head] = data;
fifo->head = (fifo->head + 1) % fifo->size;
return true;
}
bool fifo_read(RingFIFO *fifo, uint8_t *data) {
if (fifo_is_empty(fifo)) {
return false; // FIFO为空
}
*data = fifo->buffer[fifo->tail];
fifo->tail = (fifo->tail + 1) % fifo->size;
return true;
}
4.3 线程安全的FIFO
在RTOS环境中,FIFO需要保证线程安全。
4.3.1 关中断保护(裸机环境)
bool fifo_write_isr_safe(RingFIFO *fifo, uint8_t data) {
__disable_irq(); // 关中断
bool result = fifo_write(fifo, data);
__enable_irq(); // 开中断
return result;
}
4.3.2 信号量保护(RTOS环境)
typedef struct {
RingFIFO fifo;
SemaphoreHandle_t mutex;
SemaphoreHandle_t data_available;
} RTOS_FIFO;
bool rtos_fifo_write(RTOS_FIFO *rf, uint8_t data, TickType_t wait) {
if (xSemaphoreTake(rf->mutex, wait) != pdTRUE) {
return false;
}
bool result = fifo_write(&rf->fifo, data);
xSemaphoreGive(rf->mutex);
if (result) {
xSemaphoreGive(rf->data_available);
}
return result;
}
bool rtos_fifo_read(RTOS_FIFO *rf, uint8_t *data, TickType_t wait) {
if (xSemaphoreTake(rf->data_available, wait) != pdTRUE) {
return false;
}
xSemaphoreTake(rf->mutex, portMAX_DELAY);
bool result = fifo_read(&rf->fifo, data);
xSemaphoreGive(rf->mutex);
return result;
}
4.4 单片机串口中断实战
4.4.1 STM32 HAL库实现
#define RX_BUFFER_SIZE 256
static uint8_t rx_buffer[RX_BUFFER_SIZE];
static RingFIFO rx_fifo = {
.buffer = rx_buffer,
.size = RX_BUFFER_SIZE,
.head = 0,
.tail = 0
};
// 初始化串口中断
void uart_init(void) {
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
HAL_UART_Init(&huart2);
// 使能接收中断
__HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);
HAL_NVIC_SetPriority(USART2_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(USART2_IRQn);
}
// 中断服务函数
void USART2_IRQHandler(void) {
if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE)) {
uint8_t data = (uint8_t)(huart2.Instance->DR & 0xFF);
fifo_write(&rx_fifo, data);
__HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_RXNE);
}
}
// 应用层读取
bool uart_read(uint8_t *data, uint32_t timeout) {
uint32_t start = HAL_GetTick();
while (HAL_GetTick() - start < timeout) {
if (fifo_read(&rx_fifo, data)) {
return true;
}
}
return false;
}
4.4.2 ESP32 IDF实现
#define UART_NUM UART_NUM_1
#define BUF_SIZE 256
static QueueHandle_t uart_queue;
void uart_init(void) {
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE
};
uart_param_config(UART_NUM, &uart_config);
uart_set_pin(UART_NUM, TX_PIN, RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
uart_driver_install(UART_NUM, BUF_SIZE * 2, 0, 20, &uart_queue, 0);
}
// 事件处理任务
void uart_event_task(void *pvParameters) {
uart_event_t event;
uint8_t data[128];
while (1) {
if (xQueueReceive(uart_queue, &event, portMAX_DELAY)) {
if (event.type == UART_DATA) {
int len = uart_read_bytes(UART_NUM, data, event.size, portMAX_DELAY);
// 处理数据...
process_received_data(data, len);
}
}
}
}
4.5 FIFO的高级应用
4.5.1 多生产者单消费者
多个中断源写入同一个FIFO:
typedef enum {
SOURCE_UART,
SOURCE_SPI,
SOURCE_I2C,
SOURCE_COUNT
} DataSource;
typedef struct {
uint8_t data;
DataSource source;
} FIFOItem;
typedef struct {
FIFOItem buffer[BUFFER_SIZE];
volatile uint16_t head;
volatile uint16_t tail;
} MultiSourceFIFO;
// 各中断源写入
void uart_isr(void) {
FIFOItem item = {
.data = UART->DR,
.source = SOURCE_UART
};
fifo_write_item(&multi_fifo, item);
}
void spi_isr(void) {
FIFOItem item = {
.data = SPI->DR,
.source = SOURCE_SPI
};
fifo_write_item(&multi_fifo, item);
}
// 统一处理
void process_task(void) {
FIFOItem item;
if (fifo_read_item(&multi_fifo, &item)) {
switch (item.source) {
case SOURCE_UART: handle_uart_data(item.data); break;
case SOURCE_SPI: handle_spi_data(item.data); break;
case SOURCE_I2C: handle_i2c_data(item.data); break;
}
}
}
4.5.2 DMA+FIFO组合
对于高速数据传输,DMA+FIFO是最佳组合:
// DMA半传输和传输完成中断
void DMA_IRQHandler(void) {
if (DMA_GetITStatus(DMA_IT_HT)) {
// 半传输完成,处理前半缓冲区
fifo_write_bulk(&rx_fifo, &dma_buffer[0], BUFFER_SIZE/2);
DMA_ClearITPendingBit(DMA_IT_HT);
}
if (DMA_GetITStatus(DMA_IT_TC)) {
// 传输完成,处理后半缓冲区
fifo_write_bulk(&rx_fifo, &dma_buffer[BUFFER_SIZE/2], BUFFER_SIZE/2);
DMA_ClearITPendingBit(DMA_IT_TC);
}
}
4.6 常见问题与调试
4.6.1 数据丢失
原因:
- FIFO太小
- 处理速度跟不上接收速度
- 中断被长时间禁用
解决:
- 增大FIFO大小
- 优化处理逻辑
- 使用DMA
- 增加流控(RTS/CTS)
4.6.2 数据错乱
原因:
- 线程安全问题
- 指针操作错误
- 缓冲区溢出
解决:
- 添加临界区保护
- 使用volatile关键字
- 添加边界检查
4.6.3 调试技巧
// 添加调试信息
typedef struct {
uint32_t write_count;
uint32_t read_count;
uint32_t overflow_count;
uint16_t max_used;
} FIFOSTats;
FIFOSTats fifo_stats;
bool fifo_write_debug(RingFIFO *fifo, uint8_t data) {
fifo_stats.write_count++;
uint16_t used = (fifo->head - fifo->tail + fifo->size) % fifo->size;
if (used > fifo_stats.max_used) {
fifo_stats.max_used = used;
}
if (fifo_is_full(fifo)) {
fifo_stats.overflow_count++;
return false;
}
return fifo_write(fifo, data);
}
第五章:状态机设计模式详解
5.1 嵌入式系统5大状态机设计模式
根据国内技术资源总结,嵌入式系统中常用的状态机设计模式有以下5种:
- Switch-Case语句法
- 查表法(Transition Table)
- 函数指针法
- 面向对象的状态模式
- 分层状态机(HSM)
5.2 Switch-Case语句法
5.2.1 基础实现
typedef enum {
STATE_IDLE,
STATE_RUNNING,
STATE_ERROR
} SystemState;
typedef enum {
EVENT_START,
EVENT_STOP,
EVENT_ERROR,
EVENT_RECOVER
} SystemEvent;
void state_machine(SystemState *state, SystemEvent event) {
switch (*state) {
case STATE_IDLE:
switch (event) {
case EVENT_START:
init_system();
*state = STATE_RUNNING;
break;
default:
log_unexpected(event);
break;
}
break;
case STATE_RUNNING:
switch (event) {
case EVENT_STOP:
shutdown_system();
*state = STATE_IDLE;
break;
case EVENT_ERROR:
handle_error();
*state = STATE_ERROR;
break;
default:
break;
}
break;
case STATE_ERROR:
switch (event) {
case EVENT_RECOVER:
recover_system();
*state = STATE_IDLE;
break;
default:
break;
}
break;
}
}
5.2.2 改进版本:使用宏简化
#define STATE_BEGIN(state) switch (state) {
#define STATE_END() }
#define ON_EVENT(event) case event:
#define TRANSITION_TO(new_state) do { *state = new_state; } while(0)
#define ACTION(action) do { action; } while(0)
void state_machine(SystemState *state, SystemEvent event) {
STATE_BEGIN(*state) {
case STATE_IDLE:
switch (event) {
ON_EVENT(EVENT_START)
ACTION(init_system());
TRANSITION_TO(STATE_RUNNING);
break;
}
break;
case STATE_RUNNING:
switch (event) {
ON_EVENT(EVENT_STOP)
ACTION(shutdown_system());
TRANSITION_TO(STATE_IDLE);
break;
ON_EVENT(EVENT_ERROR)
ACTION(handle_error());
TRANSITION_TO(STATE_ERROR);
break;
}
break;
case STATE_ERROR:
switch (event) {
ON_EVENT(EVENT_RECOVER)
ACTION(recover_system());
TRANSITION_TO(STATE_IDLE);
break;
}
break;
} STATE_END()
}
5.2.3 实战案例:按键消抖状态机
#include "stm32f1xx_hal.h"
#include <stdio.h>
#define KEY_PIN GPIO_PIN_0
#define KEY_PORT GPIOA
#define DEBOUNCE_TIME 20 // 消抖时间20ms
#define POLL_INTERVAL 10 // 轮询间隔10ms
typedef enum {
KEY_IDLE, // 空闲状态
KEY_DEBOUNCE, // 消抖状态
KEY_PRESSED, // 按下状态
KEY_RELEASE // 释放状态
} KeyState;
typedef struct {
KeyState state;
uint32_t press_time;
uint32_t release_time;
uint32_t press_duration;
} KeyContext;
static KeyContext key_ctx = {0};
bool read_key_pin(void) {
return HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == GPIO_PIN_RESET;
}
void on_key_pressed(void) {
key_ctx.press_time = HAL_GetTick();
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // LED亮
printf("Key pressed\n");
}
void on_key_released(void) {
key_ctx.release_time = HAL_GetTick();
key_ctx.press_duration = key_ctx.release_time - key_ctx.press_time;
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); // LED灭
printf("Key released, duration: %lu ms\n", key_ctx.press_duration);
}
void key_state_machine(KeyContext *ctx) {
static uint32_t debounce_start = 0;
switch (ctx->state) {
case KEY_IDLE:
if (read_key_pin()) {
debounce_start = HAL_GetTick();
ctx->state = KEY_DEBOUNCE;
}
break;
case KEY_DEBOUNCE:
if (HAL_GetTick() - debounce_start >= DEBOUNCE_TIME) {
if (read_key_pin()) {
ctx->state = KEY_PRESSED;
on_key_pressed();
} else {
ctx->state = KEY_IDLE; // 抖动,回到空闲
}
}
break;
case KEY_PRESSED:
if (!read_key_pin()) {
ctx->state = KEY_RELEASE;
}
break;
case KEY_RELEASE:
on_key_released();
ctx->state = KEY_IDLE;
break;
}
}
// 主循环每10ms调用
void main_loop(void) {
while (1) {
key_state_machine(&key_ctx);
HAL_Delay(POLL_INTERVAL);
}
}
5.3 查表法(Transition Table)
5.3.1 基础实现
typedef enum {
STATE_INIT,
STATE_READY,
STATE_PROCESSING,
STATE_DONE,
STATE_COUNT
} State;
typedef enum {
EVENT_INITIALIZE,
EVENT_START,
EVENT_COMPLETE,
EVENT_RESET,
EVENT_COUNT
} Event;
typedef State (*Action)(void);
typedef struct {
State next_state;
Action action;
} Transition;
// 状态转换表
const Transition transition_table[STATE_COUNT][EVENT_COUNT] = {
// STATE_INIT
[STATE_INIT] = {
[EVENT_INITIALIZE] = {STATE_READY, init_action},
[EVENT_START] = {STATE_INIT, NULL}, // 无效转换
[EVENT_COMPLETE] = {STATE_INIT, NULL},
[EVENT_RESET] = {STATE_INIT, NULL}
},
// STATE_READY
[STATE_READY] = {
[EVENT_INITIALIZE] = {STATE_READY, NULL},
[EVENT_START] = {STATE_PROCESSING, start_action},
[EVENT_COMPLETE] = {STATE_READY, NULL},
[EVENT_RESET] = {STATE_INIT, reset_action}
},
// STATE_PROCESSING
[STATE_PROCESSING] = {
[EVENT_INITIALIZE] = {STATE_PROCESSING, NULL},
[EVENT_START] = {STATE_PROCESSING, NULL},
[EVENT_COMPLETE] = {STATE_DONE, complete_action},
[EVENT_RESET] = {STATE_INIT, reset_action}
},
// STATE_DONE
[STATE_DONE] = {
[EVENT_INITIALIZE] = {STATE_DONE, NULL},
[EVENT_START] = {STATE_DONE, NULL},
[EVENT_COMPLETE] = {STATE_DONE, NULL},
[EVENT_RESET] = {STATE_INIT, reset_action}
}
};
State state_machine(State current_state, Event event) {
if (current_state >= STATE_COUNT || event >= EVENT_COUNT) {
return current_state; // 无效输入
}
const Transition *trans = &transition_table[current_state][event];
if (trans->action != NULL) {
trans->action();
}
return trans->next_state;
}
5.3.2 压缩存储优化
对于稀疏的状态转换表,可以使用稀疏矩阵存储:
typedef struct {
State current_state;
Event event;
State next_state;
Action action;
} SparseTransition;
const SparseTransition sparse_table[] = {
{STATE_INIT, EVENT_INITIALIZE, STATE_READY, init_action},
{STATE_READY, EVENT_START, STATE_PROCESSING, start_action},
{STATE_READY, EVENT_RESET, STATE_INIT, reset_action},
{STATE_PROCESSING, EVENT_COMPLETE, STATE_DONE, complete_action},
{STATE_PROCESSING, EVENT_RESET, STATE_INIT, reset_action},
{STATE_DONE, EVENT_RESET, STATE_INIT, reset_action},
};
#define TABLE_SIZE (sizeof(sparse_table) / sizeof(SparseTransition))
State state_machine_sparse(State current_state, Event event) {
for (int i = 0; i < TABLE_SIZE; i++) {
if (sparse_table[i].current_state current_state &&
sparse_table[i].event event) {
if (sparse_table[i].action != NULL) {
sparse_table[i].action();
}
return sparse_table[i].next_state;
}
}
return current_state; // 未找到转换,保持当前状态
}
5.4 函数指针法
5.4.1 基础实现
typedef struct StateMachine StateMachine;
// 状态函数原型:返回下一个状态
typedef State (*StateFunc)(StateMachine *sm, Event event);
struct StateMachine {
StateFunc current_state;
void *user_data; // 用户数据指针
};
// 前向声明
State state_idle(StateMachine *sm, Event event);
State state_running(StateMachine *sm, Event event);
State state_paused(StateMachine *sm, Event event);
State state_idle(StateMachine *sm, Event event) {
switch (event) {
case EVENT_START:
printf("Starting...\n");
return state_running;
default:
return state_idle;
}
}
State state_running(StateMachine *sm, Event event) {
switch (event) {
case EVENT_PAUSE:
printf("Pausing...\n");
return state_paused;
case EVENT_STOP:
printf("Stopping...\n");
return state_idle;
default:
return state_running;
}
}
State state_paused(StateMachine *sm, Event event) {
switch (event) {
case EVENT_RESUME:
printf("Resuming...\n");
return state_running;
case EVENT_STOP:
printf("Stopping from paused...\n");
return state_idle;
default:
return state_paused;
}
}
void sm_init(StateMachine *sm) {
sm->current_state = state_idle;
sm->user_data = NULL;
}
void sm_dispatch(StateMachine *sm, Event event) {
sm->current_state = sm->current_state(sm, event);
}
5.4.2 带进入/退出动作
typedef struct {
void (*entry)(void *user_data);
void (*exit)(void *user_data);
StateFunc handle;
} StateDefinition;
typedef struct {
const StateDefinition *current_state_def;
void *user_data;
} EnhancedStateMachine;
const StateDefinition state_idle_def = {
.entry = idle_entry,
.exit = idle_exit,
.handle = state_idle
};
void sm_transition(EnhancedStateMachine *sm, const StateDefinition *new_state) {
if (sm->current_state_def && sm->current_state_def->exit) {
sm->current_state_def->exit(sm->user_data);
}
sm->current_state_def = new_state;
if (sm->current_state_def->entry) {
sm->current_state_def->entry(sm->user_data);
}
}
5.5 面向对象的状态模式(C++)
5.5.1 经典实现
class Context; // 前向声明
// 抽象状态类
class State {
public:
virtual ~State() = default;
virtual void handle(Context *context) = 0;
virtual void entry() {}
virtual void exit() {}
virtual const char* getName() const = 0;
};
// 上下文类
class Context {
private:
State *current_state_;
public:
Context(State *initial_state) : current_state_(initial_state) {
if (current_state_) {
current_state_->entry();
}
}
~Context() {
if (current_state_) {
current_state_->exit();
delete current_state_;
}
}
void set_state(State *new_state) {
if (current_state_) {
current_state_->exit();
delete current_state_;
}
current_state_ = new_state;
if (current_state_) {
current_state_->entry();
}
}
void request() {
if (current_state_) {
current_state_->handle(this);
}
}
const char* get_current_state_name() const {
return current_state_ ? current_state_->getName() : "None";
}
};
// 具体状态:关闭状态
class OffState : public State {
public:
void handle(Context *context) override;
const char* getName() const override { return "Off"; }
void entry() override { printf("Enter Off state\n"); }
void exit() override { printf("Exit Off state\n"); }
};
// 具体状态:开启状态
class OnState : public State {
public:
void handle(Context *context) override;
const char* getName() const override { return "On"; }
void entry() override { printf("Enter On state\n"); }
void exit() override { printf("Exit On state\n"); }
};
void OffState::handle(Context *context) {
printf("Switching to On\n");
context->set_state(new OnState());
}
void OnState::handle(Context *context) {
printf("Switching to Off\n");
context->set_state(new OffState());
}
// 使用
int main() {
Context context(new OffState());
for (int i = 0; i < 3; i++) {
printf("Current state: %s\n", context.get_current_state_name());
context.request();
}
return 0;
}
5.5.2 嵌入式优化版本
在资源受限的嵌入式环境中,需要优化:
// 使用静态分配替代动态分配
class EmbeddedStateMachine {
public:
template<typename StateType>
void transition() {
if (current_state_) {
current_state_->exit();
}
// 使用placement new在预分配内存中构造
current_state_ = new (state_buffer_) StateType();
current_state_->entry();
}
private:
alignas(alignof(std::max_align_t)) char state_buffer_[32]; // 预分配内存
State *current_state_;
};
// 或者使用对象池
template<typename T, size_t N>
class ObjectPool {
public:
T* allocate() {
for (size_t i = 0; i < N; ++i) {
if (!used_[i]) {
used_[i] = true;
return reinterpret_cast<T*>(&pool_[i * sizeof(T)]);
}
}
return nullptr; // 池耗尽
}
void deallocate(T *ptr) {
size_t index = (reinterpret_cast<char*>(ptr) - pool_) / sizeof(T);
if (index < N) {
used_[index] = false;
}
}
private:
alignas(alignof(T)) char pool_[N * sizeof(T)];
bool used_[N] = {false};
};
5.6 分层状态机(HSM)
分层状态机将在第八章详细讲解,这里先给出基本概念。
5.6.1 核心概念
- 状态分层:状态分"父状态"和"子状态",形成树形结构
- 行为继承:子状态能直接"继承"父状态的事件处理逻辑
- 行为覆写:如果子状态有特殊逻辑,可以"覆写"父状态的行为
5.6.2 简单示例
// 智能门锁示例
typedef enum {
STATE_STANDBY, // 待机状态(顶级)
STATE_VERIFYING, // 验证状态(父状态)
STATE_FINGERPRINT, // 指纹验证(子状态)
STATE_PASSWORD, // 密码验证(子状态)
STATE_UNLOCKED // 已解锁
} LockState;
// 事件冒泡处理
bool handle_event(LockState *current, Event event) {
LockState state = *current;
while (state != STATE_NONE) {
// 尝试在当前状态处理事件
if (try_handle(state, event)) {
return true;
}
// 冒泡到父状态
state = get_parent_state(state);
}
return false; // 事件未被处理
}
第六章:C语言实现面向对象状态机
6.1 用结构体模拟类
C语言虽然没有class关键字,但可以通过结构体和函数指针来模拟面向对象。
6.1.1 封装
// 模拟类:Motor
typedef struct {
// 私有成员(约定加下划线前缀)
int _speed;
int _direction;
bool _is_running;
// 公有方法(函数指针)
void (*set_speed)(struct Motor *self, int speed);
int (*get_speed)(struct Motor *self);
void (*start)(struct Motor *self);
void (*stop)(struct Motor *self);
} Motor;
// 方法实现
static void motor_set_speed(Motor *self, int speed) {
if (speed >= 0 && speed <= 100) {
self->_speed = speed;
}
}
static int motor_get_speed(Motor *self) {
return self->_speed;
}
static void motor_start(Motor *self) {
self->_is_running = true;
// 硬件操作...
}
static void motor_stop(Motor *self) {
self->_is_running = false;
// 硬件操作...
}
// 构造函数
void motor_init(Motor *self) {
self->_speed = 0;
self->_direction = 1;
self->_is_running = false;
// 绑定方法
self->set_speed = motor_set_speed;
self->get_speed = motor_get_speed;
self->start = motor_start;
self->stop = motor_stop;
}
// 使用
Motor my_motor;
motor_init(&my_motor);
my_motor.set_speed(&my_motor, 50);
my_motor.start(&my_motor);
6.1.2 继承
// 基类:Device
typedef struct {
int id;
char name[32];
void (*init)(void *self);
void (*deinit)(void *self);
} Device;
// 派生类:Sensor
typedef struct {
Device base; // 继承基类
int value;
int threshold;
void (*read)(void *self);
} Sensor;
// Sensor的方法
static void sensor_init(void *self) {
Sensor *s = (Sensor *)self;
s->value = 0;
s->threshold = 100;
printf("Sensor %s initialized\n", s->base.name);
}
static void sensor_read(void *self) {
Sensor *s = (Sensor *)self;
// 读取硬件...
s->value = adc_read(s->base.id);
}
// 构造函数
void sensor_init_default(Sensor *self, int id, const char *name) {
self->base.id = id;
strncpy(self->base.name, name, sizeof(self->base.name) - 1);
self->base.init = sensor_init;
self->base.deinit = NULL;
self->read = sensor_read;
}
6.1.3 多态
// 多态接口
#define DEVICE_OPS(dev) ((Device *)(dev))
void device_init(void *dev) {
Device *d = DEVICE_OPS(dev);
if (d->init) {
d->init(dev);
}
}
// 使用多态
Device *devices[10];
devices[0] = (Device *)&sensor1;
devices[1] = (Device *)&motor1;
for (int i = 0; i < num_devices; i++) {
device_init(devices[i]); // 调用各自的init方法
}
6.2 面向对象状态机实现
6.2.1 状态接口
// 前向声明
typedef struct StateContext StateContext;
// 状态接口(虚函数表)
typedef struct {
const char *name;
void (*entry)(void *self, StateContext *ctx);
void (*exit)(void *self, StateContext *ctx);
void (*handle)(void *self, StateContext *ctx, Event event);
} StateVTable;
// 状态基类
typedef struct {
const StateVTable *vtable;
} State;
// 上下文
struct StateContext {
State *current_state;
void *user_data;
};
// 状态操作宏
#define STATE_NAME(state) ((State *)(state))->vtable->name
#define STATE_ENTRY(state, ctx) \
do { if (((State *)(state))->vtable->entry) \
((State *)(state))->vtable->entry(state, ctx); } while(0)
#define STATE_EXIT(state, ctx) \
do { if (((State *)(state))->vtable->exit) \
((State *)(state))->vtable->exit(state, ctx); } while(0)
#define STATE_HANDLE(state, ctx, event) \
((State *)(state))->vtable->handle(state, ctx, event)
6.2.2 具体状态实现
// Idle状态
typedef struct {
State base;
int idle_count;
} IdleState;
static void idle_entry(void *self, StateContext *ctx) {
IdleState *idle = (IdleState *)self;
idle->idle_count = 0;
printf("Enter Idle state\n");
}
static void idle_handle(void *self, StateContext *ctx, Event event) {
IdleState *idle = (IdleState *)self;
switch (event) {
case EVENT_START:
// 转换到Running状态
transition_to(ctx, (State *)running_state_get());
break;
default:
idle->idle_count++;
break;
}
}
static const StateVTable idle_vtable = {
.name = "Idle",
.entry = idle_entry,
.exit = NULL,
.handle = idle_handle
};
void idle_state_init(IdleState *self) {
self->base.vtable = &idle_vtable;
self->idle_count = 0;
}
6.2.3 状态机管理器
typedef struct {
StateContext ctx;
State *states[MAX_STATES];
int state_count;
} StateMachine;
void sm_init(StateMachine *sm, State *initial_state) {
sm->ctx.current_state = initial_state;
sm->ctx.user_data = NULL;
sm->state_count = 0;
STATE_ENTRY(initial_state, &sm->ctx);
}
void sm_transition(StateMachine *sm, State *new_state) {
if (sm->ctx.current_state) {
STATE_EXIT(sm->ctx.current_state, &sm->ctx);
}
sm->ctx.current_state = new_state;
if (new_state) {
STATE_ENTRY(new_state, &sm->ctx);
}
}
void sm_dispatch(StateMachine *sm, Event event) {
if (sm->ctx.current_state) {
STATE_HANDLE(sm->ctx.current_state, &sm->ctx, event);
}
}
6.3 实战:通用按键状态机框架
// 按键事件
typedef enum {
KEY_EVENT_PRESS, // 按下
KEY_EVENT_RELEASE, // 释放
KEY_EVENT_HOLD, // 长按
KEY_EVENT_REPEAT // 重复
} KeyEvent;
// 按键状态
typedef enum {
KEY_STATE_IDLE,
KEY_STATE_DEBOUNCE,
KEY_STATE_PRESSED,
KEY_STATE_LONG_PRESS
} KeyStateEnum;
// 按键配置
typedef struct {
GPIO_TypeDef *port;
uint16_t pin;
uint32_t debounce_ms;
uint32_t long_press_ms;
uint32_t repeat_interval_ms;
} KeyConfig;
// 按键对象
typedef struct {
KeyConfig config;
KeyStateEnum state;
uint32_t state_enter_time;
uint32_t last_repeat_time;
bool pressed_reported;
bool long_press_reported;
// 事件回调
void (*on_event)(KeyEvent event, void *user_data);
void *user_data;
} Key;
// 状态机实现
void key_process(Key *key) {
bool is_pressed = HAL_GPIO_ReadPin(key->config.port, key->config.pin) == GPIO_PIN_RESET;
uint32_t now = HAL_GetTick();
switch (key->state) {
case KEY_STATE_IDLE:
if (is_pressed) {
key->state = KEY_STATE_DEBOUNCE;
key->state_enter_time = now;
}
break;
case KEY_STATE_DEBOUNCE:
if (now - key->state_enter_time >= key->config.debounce_ms) {
if (is_pressed) {
key->state = KEY_STATE_PRESSED;
key->pressed_reported = false;
key->long_press_reported = false;
} else {
key->state = KEY_STATE_IDLE;
}
}
break;
case KEY_STATE_PRESSED:
if (!is_pressed) {
key->state = KEY_STATE_IDLE;
if (key->on_event) {
key->on_event(KEY_EVENT_RELEASE, key->user_data);
}
} else {
if (!key->pressed_reported) {
key->pressed_reported = true;
if (key->on_event) {
key->on_event(KEY_EVENT_PRESS, key->user_data);
}
}
if (now - key->state_enter_time >= key->config.long_press_ms) {
key->state = KEY_STATE_LONG_PRESS;
}
}
break;
case KEY_STATE_LONG_PRESS:
if (!is_pressed) {
key->state = KEY_STATE_IDLE;
if (key->on_event) {
key->on_event(KEY_EVENT_RELEASE, key->user_data);
}
} else {
if (!key->long_press_reported) {
key->long_press_reported = true;
if (key->on_event) {
key->on_event(KEY_EVENT_HOLD, key->user_data);
}
key->last_repeat_time = now;
}
// 重复事件
if (key->config.repeat_interval_ms > 0 &&
now - key->last_repeat_time >= key->config.repeat_interval_ms) {
key->last_repeat_time = now;
if (key->on_event) {
key->on_event(KEY_EVENT_REPEAT, key->user_data);
}
}
}
break;
}
}
// 使用示例
void on_key_event(KeyEvent event, void *user_data) {
const char *event_names[] = {"PRESS", "RELEASE", "HOLD", "REPEAT"};
printf("Key event: %s\n", event_names[event]);
}
Key key1 = {
.config = {
.port = GPIOA,
.pin = GPIO_PIN_0,
.debounce_ms = 20,
.long_press_ms = 1000,
.repeat_interval_ms = 200
},
.on_event = on_key_event
};
// 在定时器中断中调用(每10ms)
void TIM_IRQHandler(void) {
key_process(&key1);
}
第七章:FreeRTOS与状态机
7.1 FreeRTOS任务状态
FreeRTOS中每个任务的生命周期本身就是一个标准状态机。
7.1.1 任务状态定义
// FreeRTOS任务状态(简化)
typedef enum {
eRunning, // 运行态:正在执行
eReady, // 就绪态:可以运行但未被调度
eBlocked, // 阻塞态:等待事件或超时
eSuspended, // 挂起态:被显式挂起
eDeleted // 删除态:等待清理
} eTaskState;
7.1.2 状态转换
+--------+ 创建 +--------+
| 不存在 | --------> | 就绪态 |
+--------+ +--------+
|
| 调度器选择运行
v
+--------+
+--------------| 运行态 |--------------+
| +--------+ |
| | |
被抢占| | 阻塞 |删除
| v |
| +--------+ |
+------------>| 阻塞态 | |
+--------+ |
| |
| 超时/事件发生 |
v |
+--------+ |
| 就绪态 |-------------+
+--------+
|
| 挂起
v
+--------+
| 挂起态 |
+--------+
7.1.3 状态转换API
// 运行 -> 阻塞
void vTaskDelay(TickType_t xTicksToDelay);
void vTaskDelayUntil(TickType_t *pxPreviousWakeTime, TickType_t xTimeIncrement);
// 运行 -> 挂起
void vTaskSuspend(TaskHandle_t xTaskToSuspend);
// 挂起 -> 就绪
void vTaskResume(TaskHandle_t xTaskToResume);
BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume);
// 运行 -> 删除
void vTaskDelete(TaskHandle_t xTaskToDelete);
7.2 任务内状态机
在FreeRTOS任务中实现状态机是常见的做法。
7.2.1 轮询式状态机
void vStateMachineTask(void *pvParameters) {
State current_state = STATE_INIT;
while (1) {
switch (current_state) {
case STATE_INIT:
// 初始化操作
if (init_success()) {
current_state = STATE_IDLE;
} else {
current_state = STATE_ERROR;
}
break;
case STATE_IDLE:
// 等待事件
if (xSemaphoreTake(event_sem, pdMS_TO_TICKS(100)) == pdTRUE) {
current_state = STATE_PROCESSING;
}
break;
case STATE_PROCESSING:
// 处理数据
process_data();
current_state = STATE_IDLE;
break;
case STATE_ERROR:
// 错误处理
handle_error();
vTaskDelay(pdMS_TO_TICKS(5000));
current_state = STATE_INIT; // 尝试重启
break;
}
vTaskDelay(pdMS_TO_TICKS(10)); // 释放CPU
}
}
7.2.2 事件驱动状态机
typedef enum {
MSG_TYPE_START,
MSG_TYPE_STOP,
MSG_TYPE_DATA,
MSG_TYPE_ERROR
} MessageType;
typedef struct {
MessageType type;
uint8_t data[64];
size_t len;
} Message;
static QueueHandle_t msg_queue;
void vEventDrivenTask(void *pvParameters) {
State current_state = STATE_IDLE;
Message msg;
while (1) {
// 等待消息,超时100ms
if (xQueueReceive(msg_queue, &msg, pdMS_TO_TICKS(100)) pdTRUE) {
// 根据状态和消息类型处理
switch (current_state) {
case STATE_IDLE:
if (msg.type MSG_TYPE_START) {
start_operation();
current_state = STATE_RUNNING;
}
break;
case STATE_RUNNING:
switch (msg.type) {
case MSG_TYPE_DATA:
process_data(msg.data, msg.len);
break;
case MSG_TYPE_STOP:
stop_operation();
current_state = STATE_IDLE;
break;
case MSG_TYPE_ERROR:
handle_error();
current_state = STATE_ERROR_RECOVERY;
break;
}
break;
case STATE_ERROR_RECOVERY:
if (msg.type == MSG_TYPE_START) {
if (recover_system()) {
current_state = STATE_RUNNING;
}
}
break;
}
} else {
// 超时处理(心跳)
on_timeout(¤t_state);
}
}
}
// 其他任务或ISR发送消息
void on_data_received(uint8_t *data, size_t len) {
Message msg = {
.type = MSG_TYPE_DATA,
.len = len > 64 ? 64 : len
};
memcpy(msg.data, data, msg.len);
xQueueSendFromISR(msg_queue, &msg, NULL);
}
7.3 多任务状态机协调
复杂系统中,多个任务的状态机需要协调工作。
7.3.1 主从状态机
// 主状态机任务
void vMasterTask(void *pvParameters) {
MasterState state = MASTER_INIT;
while (1) {
switch (state) {
case MASTER_INIT:
// 创建从任务
xTaskCreate(vSlaveTask1, "Slave1", 256, NULL, 2, &slave1_handle);
xTaskCreate(vSlaveTask2, "Slave2", 256, NULL, 2, &slave2_handle);
state = MASTER_WAIT_READY;
break;
case MASTER_WAIT_READY:
// 等待所有从任务就绪
if (wait_for_slaves_ready(pdMS_TO_TICKS(5000))) {
state = MASTER_RUNNING;
broadcast_event(EVENT_SYSTEM_READY);
} else {
state = MASTER_ERROR;
}
break;
case MASTER_RUNNING:
// 监控系统运行
if (check_system_health()) {
vTaskDelay(pdMS_TO_TICKS(1000));
} else {
state = MASTER_ERROR;
}
break;
case MASTER_ERROR:
broadcast_event(EVENT_SYSTEM_ERROR);
// 尝试恢复或重启
vTaskDelay(pdMS_TO_TICKS(5000));
state = MASTER_INIT;
break;
}
}
}
// 从任务
void vSlaveTask1(void *pvParameters) {
SlaveState state = SLAVE_INIT;
Event event;
while (1) {
if (xQueueReceive(event_queue, &event, pdMS_TO_TICKS(100)) pdTRUE) {
switch (state) {
case SLAVE_INIT:
if (event EVENT_SYSTEM_READY) {
state = SLAVE_ACTIVE;
}
break;
case SLAVE_ACTIVE:
if (event == EVENT_SYSTEM_ERROR) {
state = SLAVE_STANDBY;
} else {
do_work();
}
break;
case SLAVE_STANDBY:
if (event == EVENT_SYSTEM_READY) {
state = SLAVE_ACTIVE;
}
break;
}
}
}
}
7.3.2 状态同步机制
// 使用事件组同步状态
typedef enum {
STATE_BIT_INIT = (1 << 0),
STATE_BIT_READY = (1 << 1),
STATE_BIT_RUNNING = (1 << 2),
STATE_BIT_ERROR = (1 << 3)
} StateBits;
static EventGroupHandle_t state_event_group;
void set_system_state(StateBits state) {
xEventGroupSetBits(state_event_group, state);
}
bool wait_for_state(StateBits state, TickType_t timeout) {
EventBits_t bits = xEventGroupWaitBits(
state_event_group,
state,
pdFALSE, // 不清除位
pdTRUE, // 等待所有指定位
timeout
);
return (bits & state) == state;
}
7.4 FreeRTOS与HSM结合
// HSM事件定义
typedef struct {
uint32_t sig;
void *data;
} HsmEvent;
// HSM任务
typedef struct {
QueueHandle_t event_queue;
HsmState *current_state;
void *user_data;
} HsmTask;
void vHsmTask(void *pvParameters) {
HsmTask *hsm = (HsmTask *)pvParameters;
HsmEvent event;
// 初始转换
hsm->current_state = hsm_init(hsm->current_state);
while (1) {
if (xQueueReceive(hsm->event_queue, &event, portMAX_DELAY) == pdTRUE) {
HsmState *new_state = hsm_dispatch(hsm->current_state, &event);
if (new_state != hsm->current_state) {
// 执行状态转换
hsm_exit(hsm->current_state);
hsm->current_state = new_state;
hsm_entry(hsm->current_state);
}
}
}
}
// 从ISR发送事件
void hsm_post_event_isr(HsmTask *hsm, uint32_t sig, void *data) {
HsmEvent event = {.sig = sig, .data = data};
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(hsm->event_queue, &event, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
第八章:层次状态机HSM高级应用
8.1 HSM核心概念
层次状态机(Hierarchical State Machine,HSM)通过状态嵌套解决了传统FSM的"状态爆炸"问题。
8.1.1 状态层次结构
系统状态(顶状态)
├── 待机状态
│ └── 低功耗子状态
├── 运行状态
│ ├── 初始化子状态
│ ├── 正常工作子状态
│ │ ├── 模式A
│ │ └── 模式B
│ └── 错误处理子状态
└── 关机状态
8.1.2 行为继承
子状态自动继承父状态的事件处理:
// 父状态:运行状态
bool running_state_handle(Event event) {
switch (event) {
case EVENT_STOP:
transition_to(STATE_STANDBY);
return true; // 事件已处理
case EVENT_ERROR:
transition_to(STATE_ERROR);
return true;
default:
return false; // 事件未处理,传递给子状态
}
}
// 子状态:正常工作状态
bool normal_state_handle(Event event) {
switch (event) {
case EVENT_DATA_RECEIVED:
process_data();
return true;
default:
// 未处理的事件冒泡到父状态
return running_state_handle(event);
}
}
8.2 HSM实现
8.2.1 状态结构定义
typedef struct HsmState HsmState;
struct HsmState {
const char *name;
HsmState *parent; // 父状态指针
void (*entry)(void); // 进入动作
void (*exit)(void); // 退出动作
void (*init)(void); // 初始子状态
bool (*handle)(Event); // 事件处理
};
// 获取状态的层次深度
int hsm_get_depth(HsmState *state) {
int depth = 0;
while (state) {
depth++;
state = state->parent;
}
return depth;
}
// 查找最近公共祖先(LCA)
HsmState* hsm_find_lca(HsmState *s1, HsmState *s2) {
int d1 = hsm_get_depth(s1);
int d2 = hsm_get_depth(s2);
// 调整到同一深度
while (d1 > d2) {
s1 = s1->parent;
d1--;
}
while (d2 > d1) {
s2 = s2->parent;
d2--;
}
// 同时向上查找
while (s1 != s2) {
s1 = s1->parent;
s2 = s2->parent;
}
return s1;
}
8.2.2 状态转换
void hsm_transition(HsmState **current, HsmState *target) {
if (*current == target) {
// 自转换
if ((*current)->exit) (*current)->exit();
if ((*current)->entry) (*current)->entry();
return;
}
// 查找LCA
HsmState *lca = hsm_find_lca(*current, target);
// 退出到LCA(不包括LCA)
HsmState *exit_state = *current;
while (exit_state != lca) {
if (exit_state->exit) exit_state->exit();
exit_state = exit_state->parent;
}
// 构建进入路径
HsmState *path[16];
int path_len = 0;
HsmState *enter_state = target;
while (enter_state != lca) {
path[path_len++] = enter_state;
enter_state = enter_state->parent;
}
// 从LCA进入目标状态(反向)
for (int i = path_len - 1; i >= 0; i--) {
if (path[i]->entry) path[i]->entry();
}
*current = target;
// 执行初始转换(如果有)
if ((*current)->init) {
(*current)->init();
}
}
8.2.3 事件分发
bool hsm_dispatch(HsmState *current, Event event) {
HsmState *state = current;
// 从当前状态开始,向上冒泡
while (state) {
if (state->handle && state->handle(event)) {
return true; // 事件已处理
}
state = state->parent;
}
return false; // 事件未被处理
}
8.3 HSM实战:智能门锁
// 状态声明
HsmState state_standby;
HsmState state_verifying;
HsmState state_fingerprint;
HsmState state_password;
HsmState state_unlocked;
HsmState state_alarm;
// 待机状态
bool standby_handle(Event event) {
switch (event) {
case EVENT_FINGERPRINT_DETECTED:
hsm_transition(¤t_state, &state_fingerprint);
return true;
case EVENT_PASSWORD_ENTERED:
hsm_transition(¤t_state, &state_password);
return true;
default:
return false;
}
}
void standby_entry(void) {
printf("Enter standby mode\n");
led_set_color(LED_BLUE);
}
HsmState state_standby = {
.name = "Standby",
.parent = NULL,
.entry = standby_entry,
.handle = standby_handle
};
// 验证状态(父状态)
bool verifying_handle(Event event) {
switch (event) {
case EVENT_CANCEL:
hsm_transition(¤t_state, &state_standby);
return true;
case EVENT_TIMEOUT:
hsm_transition(¤t_state, &state_alarm);
return true;
default:
return false;
}
}
void verifying_entry(void) {
printf("Start verification\n");
start_timeout_timer(30000); // 30秒超时
}
void verifying_exit(void) {
stop_timeout_timer();
}
HsmState state_verifying = {
.name = "Verifying",
.parent = NULL,
.entry = verifying_entry,
.exit = verifying_exit,
.handle = verifying_handle
};
// 指纹验证(子状态)
bool fingerprint_handle(Event event) {
switch (event) {
case EVENT_FINGERPRINT_MATCH:
record_fingerprint_id();
hsm_transition(¤t_state, &state_unlocked);
return true;
case EVENT_FINGERPRINT_MISMATCH:
if (++mismatch_count >= 3) {
hsm_transition(¤t_state, &state_alarm);
}
return true;
default:
// 未处理的事件冒泡到父状态
return false;
}
}
void fingerprint_entry(void) {
printf("Fingerprint verification\n");
activate_fingerprint_sensor();
}
HsmState state_fingerprint = {
.name = "Fingerprint",
.parent = &state_verifying, // 指定父状态
.entry = fingerprint_entry,
.handle = fingerprint_handle
};
// 已解锁状态
void unlocked_entry(void) {
printf("Door unlocked\n");
unlock_door();
led_set_color(LED_GREEN);
start_relock_timer(5000); // 5秒后自动上锁
}
HsmState state_unlocked = {
.name = "Unlocked",
.parent = NULL,
.entry = unlocked_entry
};
8.4 HSM框架选择
8.4.1 Zephyr SMF
Zephyr RTOS内置的轻量级状态机框架:
#include <smf.h>
// 定义状态
const struct smf_state states[] = {
[STATE_IDLE] = SMF_CREATE_STATE(idle_entry, idle_run, idle_exit, NULL),
[STATE_RUNNING] = SMF_CREATE_STATE(running_entry, running_run, running_exit, NULL),
};
// 设置初始状态
smf_set_initial(SMF_CTX(&s_obj), &states[STATE_IDLE]);
// 运行状态机
while (1) {
smf_run_state(SMF_CTX(&s_obj));
k_sleep(K_MSEC(10));
}
8.4.2 QP/C
专业级的层次状态机框架:
#include "qpc.h"
// 定义状态
static QState state_idle(MyHSM *me);
static QState state_running(MyHSM *me);
// 状态实现
static QState state_idle(MyHSM *me) {
QState status;
switch (Q_SIG(me)) {
case Q_ENTRY_SIG:
// 进入动作
status = Q_HANDLED();
break;
case START_SIG:
Q_TRAN(&state_running);
status = Q_HANDLED();
break;
default:
status = Q_SUPER(&QHsm_top);
break;
}
return status;
}
第九章:实战项目------智能小车状态机设计
9.1 项目概述
设计一个基于STM32的智能小车,具备以下功能:
- 红外循迹
- 超声波避障
- 蓝牙遥控
- 自动/手动模式切换
- 状态指示(LED)
9.2 系统架构
+------------------+
| 应用层状态机 |
| (模式管理) |
+------------------+
|
+--------v---------+
| 行为状态机 |
| (循迹/避障/遥控) |
+------------------+
|
+--------v---------+
| 驱动层 |
| (电机/传感器) |
+------------------+
9.3 状态定义
// 主状态(模式)
typedef enum {
MODE_STANDBY, // 待机
MODE_MANUAL, // 手动遥控
MODE_AUTO_TRACK, // 自动循迹
MODE_AUTO_AVOID, // 自动避障
MODE_ERROR // 错误状态
} CarMode;
// 运动状态
typedef enum {
MOTION_STOPPED, // 停止
MOTION_FORWARD, // 前进
MOTION_BACKWARD, // 后退
MOTION_TURN_LEFT, // 左转
MOTION_TURN_RIGHT, // 右转
MOTION_ROTATE // 旋转
} CarMotion;
// 传感器状态
typedef enum {
SENSOR_IDLE,
SENSOR_TRACKING, // 循迹中
SENSOR_OBSTACLE, // 检测到障碍物
SENSOR_LOST // 丢失轨迹
} SensorState;
9.4 主状态机实现
typedef struct {
CarMode mode;
CarMotion motion;
SensorState sensor;
// 传感器数据
uint16_t track_sensors;
float obstacle_distance;
uint8_t bluetooth_cmd;
// 状态机上下文
uint32_t state_enter_time;
uint32_t last_action_time;
} CarContext;
static CarContext car = {0};
// 模式状态机
void car_mode_state_machine(CarContext *ctx) {
switch (ctx->mode) {
case MODE_STANDBY:
mode_standby_handle(ctx);
break;
case MODE_MANUAL:
mode_manual_handle(ctx);
break;
case MODE_AUTO_TRACK:
mode_auto_track_handle(ctx);
break;
case MODE_AUTO_AVOID:
mode_auto_avoid_handle(ctx);
break;
case MODE_ERROR:
mode_error_handle(ctx);
break;
}
}
// 待机模式
void mode_standby_handle(CarContext *ctx) {
motor_stop();
led_set(LED_BLUE);
// 检查蓝牙命令
if (ctx->bluetooth_cmd == CMD_MANUAL_MODE) {
ctx->mode = MODE_MANUAL;
ctx->state_enter_time = HAL_GetTick();
} else if (ctx->bluetooth_cmd == CMD_AUTO_TRACK) {
ctx->mode = MODE_AUTO_TRACK;
ctx->state_enter_time = HAL_GetTick();
} else if (ctx->bluetooth_cmd == CMD_AUTO_AVOID) {
ctx->mode = MODE_AUTO_AVOID;
ctx->state_enter_time = HAL_GetTick();
}
}
// 手动模式
void mode_manual_handle(CarContext *ctx) {
led_set(LED_GREEN);
switch (ctx->bluetooth_cmd) {
case CMD_STOP:
ctx->motion = MOTION_STOPPED;
break;
case CMD_FORWARD:
ctx->motion = MOTION_FORWARD;
break;
case CMD_BACKWARD:
ctx->motion = MOTION_BACKWARD;
break;
case CMD_LEFT:
ctx->motion = MOTION_TURN_LEFT;
break;
case CMD_RIGHT:
ctx->motion = MOTION_TURN_RIGHT;
break;
case CMD_STANDBY:
ctx->mode = MODE_STANDBY;
return;
}
// 执行动作
execute_motion(ctx->motion);
}
// 自动循迹模式
void mode_auto_track_handle(CarContext *ctx) {
led_set(LED_YELLOW);
// 读取循迹传感器
ctx->track_sensors = read_track_sensors();
switch (ctx->sensor) {
case SENSOR_IDLE:
ctx->sensor = SENSOR_TRACKING;
break;
case SENSOR_TRACKING:
if (ctx->track_sensors == 0b0000) {
ctx->sensor = SENSOR_LOST;
} else if (ctx->track_sensors == 0b1111) {
// 十字路口,继续直行
ctx->motion = MOTION_FORWARD;
} else if (ctx->track_sensors & 0b0011) {
// 偏左,右转
ctx->motion = MOTION_TURN_RIGHT;
} else if (ctx->track_sensors & 0b1100) {
// 偏右,左转
ctx->motion = MOTION_TURN_LEFT;
} else {
ctx->motion = MOTION_FORWARD;
}
break;
case SENSOR_LOST:
// 丢失轨迹,尝试旋转找回
ctx->motion = MOTION_ROTATE;
if (ctx->track_sensors != 0b0000) {
ctx->sensor = SENSOR_TRACKING;
}
// 超时处理
if (HAL_GetTick() - ctx->state_enter_time > 5000) {
ctx->mode = MODE_ERROR;
}
break;
}
// 检查退出命令
if (ctx->bluetooth_cmd == CMD_STANDBY) {
ctx->mode = MODE_STANDBY;
return;
}
execute_motion(ctx->motion);
}
// 自动避障模式
void mode_auto_avoid_handle(CarContext *ctx) {
led_set(LED_RED);
// 读取超声波距离
ctx->obstacle_distance = ultrasound_get_distance();
if (ctx->obstacle_distance < 15.0f) {
// 前方有障碍物
motor_stop();
HAL_Delay(200);
// 扫描左右距离
servo_set_angle(0); // 左转
HAL_Delay(500);
float left_dist = ultrasound_get_distance();
servo_set_angle(180); // 右转
HAL_Delay(500);
float right_dist = ultrasound_get_distance();
servo_set_angle(90); // 回正
// 选择更宽的路径
if (left_dist > right_dist && left_dist > 20.0f) {
motor_turn_left();
HAL_Delay(500);
} else if (right_dist > 20.0f) {
motor_turn_right();
HAL_Delay(500);
} else {
// 两边都有障碍,后退
motor_backward();
HAL_Delay(500);
motor_turn_left();
HAL_Delay(1000);
}
} else {
motor_forward();
}
// 检查退出命令
if (ctx->bluetooth_cmd == CMD_STANDBY) {
ctx->mode = MODE_STANDBY;
return;
}
}
// 错误模式
void mode_error_handle(CarContext *ctx) {
motor_stop();
led_blink(LED_RED, 200); // 快速闪烁
// 尝试恢复
if (ctx->bluetooth_cmd == CMD_RESET) {
ctx->mode = MODE_STANDBY;
ctx->sensor = SENSOR_IDLE;
}
}
9.5 中断处理
// 蓝牙串口中断
void USART1_IRQHandler(void) {
if (USART_GetITStatus(USART1, USART_IT_RXNE)) {
uint8_t cmd = USART_ReceiveData(USART1);
car.bluetooth_cmd = cmd;
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
// 定时器中断(每10ms)
void TIM3_IRQHandler(void) {
if (TIM_GetITStatus(TIM3, TIM_IT_Update)) {
// 运行状态机
car_mode_state_machine(&car);
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}
}
9.6 改进版本:使用HSM
// 使用层次状态机重构
HsmState state_standby;
HsmState state_manual;
HsmState state_auto;
HsmState state_auto_tracking;
HsmState state_auto_avoiding;
HsmState state_error;
// 自动模式(父状态)
bool state_auto_handle(Event event) {
switch (event) {
case EVENT_CMD_STANDBY:
hsm_transition(¤t_state, &state_standby);
return true;
case EVENT_OBSTACLE_DETECTED:
hsm_transition(¤t_state, &state_auto_avoiding);
return true;
default:
return false;
}
}
void state_auto_entry(void) {
motor_forward();
}
HsmState state_auto = {
.name = "Auto",
.handle = state_auto_handle,
.entry = state_auto_entry
};
// 循迹子状态
bool state_tracking_handle(Event event) {
switch (event) {
case EVENT_TRACK_LOST:
motor_rotate();
return true;
case EVENT_TRACK_FOUND:
motor_forward();
return true;
default:
return false; // 冒泡到父状态
}
}
HsmState state_auto_tracking = {
.name = "Tracking",
.parent = &state_auto,
.handle = state_tracking_handle
};
第十章:嵌入式设计模式总结
10.1 常用设计模式回顾
10.1.1 单例模式
typedef struct {
int config_value;
char device_name[32];
} SystemConfig;
static SystemConfig *g_config = NULL;
SystemConfig* system_config_get(void) {
if (g_config == NULL) {
static SystemConfig instance;
g_config = &instance;
// 初始化默认值
g_config->config_value = 100;
strcpy(g_config->device_name, "MyDevice");
}
return g_config;
}
10.1.2 观察者模式
#define MAX_OBSERVERS 10
typedef void (*ObserverCallback)(void *data, void *user_data);
typedef struct {
ObserverCallback callbacks[MAX_OBSERVERS];
void *user_data[MAX_OBSERVERS];
int count;
} Subject;
void subject_init(Subject *sub) {
sub->count = 0;
}
void subject_attach(Subject *sub, ObserverCallback cb, void *user_data) {
if (sub->count < MAX_OBSERVERS) {
sub->callbacks[sub->count] = cb;
sub->user_data[sub->count] = user_data;
sub->count++;
}
}
void subject_notify(Subject *sub, void *data) {
for (int i = 0; i < sub->count; i++) {
sub->callbacks[i](data, sub->user_data[i]);
}
}
10.1.3 工厂模式
typedef enum {
SENSOR_TYPE_TEMP,
SENSOR_TYPE_HUMIDITY,
SENSOR_TYPE_PRESSURE
} SensorType;
typedef struct {
SensorType type;
float (*read)(void);
void (*init)(void);
} Sensor;
Sensor* sensor_create(SensorType type) {
Sensor *sensor = malloc(sizeof(Sensor));
switch (type) {
case SENSOR_TYPE_TEMP:
sensor->type = type;
sensor->read = temp_sensor_read;
sensor->init = temp_sensor_init;
break;
case SENSOR_TYPE_HUMIDITY:
sensor->type = type;
sensor->read = humidity_sensor_read;
sensor->init = humidity_sensor_init;
break;
// ...
}
if (sensor->init) {
sensor->init();
}
return sensor;
}
10.2 设计模式选择指南
| 场景 | 推荐模式 | 原因 |
|---|---|---|
| 全局配置管理 | 单例 | 确保唯一实例 |
| 事件通知 | 观察者 | 解耦发布者和订阅者 |
| 算法切换 | 策略 | 运行时切换算法 |
| 对象创建 | 工厂 | 封装创建逻辑 |
| 复杂状态管理 | 状态机 | 清晰的状态转换 |
| 硬件抽象 | 适配器 | 统一不同硬件接口 |
10.3 代码组织建议
project/
├── core/ # 核心状态机框架
│ ├── hsm.c/h
│ └── event.c/h
├── drivers/ # 硬件驱动
│ ├── motor.c/h
│ ├── sensor.c/h
│ └── uart.c/h
├── modules/ # 功能模块
│ ├── tracker.c/h
│ ├── avoider.c/h
│ └── remote.c/h
├── app/ # 应用层
│ └── main.c
└── utils/ # 工具函数
├── fifo.c/h
└── timer.c/h
第十一章:参考资源与开源项目
11.1 国内优秀博文
-
嵌入式系统5大状态机设计模式
- 来源:火龙果UML
- 链接:http://www.uml.org.cn/embeded/202509304.asp
- 内容:系统介绍了5种状态机设计模式
-
嵌入式状态机架构,一文讲透!
- 来源:知乎/CSDN
- 内容:从基础到进阶,包含开源项目分析
-
分层状态机(HSM)在嵌入式中的落地
- 来源:CSDN
- 内容:HSM原理和实战案例
-
串口中断接收与环形缓冲区设计实战解析
- 来源:CSDN
- 内容:详细的FIFO和中断实现
-
C语言实现面向对象状态机
- 来源:电子工程专辑
- 内容:用C语言模拟OOP实现状态机
11.2 开源项目推荐
-
Zephyr SMF
- 地址:https://github.com/zephyrproject-rtos/zephyr
- 路径:subsys/smf
- 特点:极简纯C状态机,支持HSM
-
QP/C
- 地址:https://github.com/QuantumLeaps/qpc
- 特点:专业级HSM框架,航空航天级
-
FreeRTOS
- 地址:https://github.com/FreeRTOS/FreeRTOS-Kernel
- 特点:任务状态管理本身就是状态机
-
lwIP
- 地址:https://github.com/lwip-tcpip/lwip
- 特点:TCP状态机实现
11.3 学习路径建议
-
入门阶段
- 学习基础FSM概念
- 用switch-case实现简单状态机
- 理解事件驱动编程
-
进阶阶段
- 学习函数指针实现
- 掌握FIFO和中断
- 尝试用C模拟OOP
-
高级阶段
- 学习HSM
- 阅读开源框架源码
- 在实际项目中应用
11.4 常见问题解答
Q: 状态机一定要用面向对象吗? A: 状态机的思想是面向对象的,但C语言可以通过结构体和函数指针实现类似效果。
Q: 什么时候用HSM? A: 当状态数量超过5个,且存在多个状态共享相同行为时,考虑使用HSM。
Q: 如何避免状态爆炸? A: 使用层次状态机、正交区域、提取共性行为到父状态。
Q: 中断里可以执行状态机吗? A: 中断里应该只做快速的数据搬运,状态机逻辑放在主循环或任务中执行。
结语
状态机是嵌入式开发中最重要的设计模式之一。通过本文的系统学习,读者应该能够:
- 理解状态机的理论基础
- 掌握多种状态机实现方式
- 在C语言中实现面向对象的状态机
- 将状态机与RTOS结合使用
- 应用HSM解决复杂问题
- 在实际项目中灵活运用
记住,好的状态机设计应该具备:清晰的状态定义、明确的状态转换、合理的层次结构、易于测试和维护。
希望本文能帮助你在嵌入式开发的道路上更进一步!
文档版本 :v1.0
最后更新 :2026年2月
字数统计:约55000字