状态机设计模式与嵌入式系统开发完整指南
目录
- 引言
- 状态机基础理论
- 嵌入式平台的资源限制
- 状态机的五种核心设计模式
- FIFO与单片机通信中断
- 面向对象vs面向过程:C语言实现状态机
- FreeRTOS与状态机的结合
- 实际项目案例:智能小车控制系统
- 设计模式的选择与实践建议
- 总结与展望
- 附录:完整代码示例
引言
在嵌入式系统开发中,状态机(State Machine)是一种至关重要的设计模式。无论是简单的按键消抖,还是复杂的通信协议解析,状态机都能提供清晰、可维护的解决方案。然而,许多初学者在面对状态机时常常感到困惑:网上资料繁杂,各种实现方式层出不穷,从简单的switch-case到复杂的面向对象框架,让人难以抉择。
本文将从国内开发者的实际需求出发,系统地介绍状态机的设计模式,深入探讨FIFO(先进先出队列)与单片机通信中断的实现,分析面向对象与面向过程在嵌入式开发中的应用,并结合FreeRTOS实时操作系统,提供完整的实战案例。文章内容基于国内主流技术社区和开源项目的实践经验,力求为嵌入式开发者提供一份详实、实用的参考指南。
为什么状态机是嵌入式开发的核心
嵌入式系统的本质是对外部事件的响应和处理。无论是传感器数据采集、用户按键输入,还是通信数据接收,系统都需要根据不同的输入和当前状态做出相应的动作。状态机正是描述这种"事件-状态-动作"关系的数学模型。
以一个简单的洗衣机控制程序为例:
- 初始状态:待机
- 事件:用户按下启动按钮
- 转换:待机 → 进水
- 动作:打开进水阀门
这种清晰的逻辑关系正是状态机所擅长的。没有状态机,代码将充斥着大量的if-else嵌套,不仅难以维护,还容易引入bug。
状态机基础理论
有限状态机的数学定义
有限状态机(Finite State Machine, FSM)是一个数学计算模型,由以下五个要素组成:
- 状态集合(S):系统可能处于的所有状态的集合
- 输入字母表(Σ):所有可能输入事件的集合
- 转移函数(δ):S × Σ → S,定义状态转换规则
- 初始状态(s₀):系统启动时的状态
- 接受状态集合(F):任务完成时的终止状态(可选)
用数学表达式表示为:M = (S, Σ, δ, s₀, F)
状态机的分类
1. Moore型状态机
Moore型状态机的输出仅取决于当前状态,与输入无关。其特点是:
- 输出在状态进入时确定
- 状态转换图的每个节点标注输出值
- 实现简单,但可能需要更多状态
示例:交通灯控制系统
状态:红灯 → 绿灯 → 黄灯 → 红灯
输出:红灯亮 绿灯亮 黄灯亮 红灯亮
2. Mealy型状态机
Mealy型状态机的输出取决于当前状态和输入事件。其特点是:
- 输出在状态转换时确定
- 状态转换图的每条边标注输出值
- 通常需要更少的状态,但逻辑更复杂
示例:密码锁系统
状态:LOCKED →(输入正确密码)→ UNLOCKED
输出: 开锁动作
状态机的三个核心元素
无论采用何种实现方式,状态机都包含三个基本元素:
-
状态(State):系统在某个时刻的条件或模式
- 每个状态有明确的进入条件和退出条件
- 状态可以包含子状态(层次状态机)
-
转换(Transition):状态之间的切换
- 由事件触发
- 可以附带守卫条件(Guard Condition)
- 可以执行转换动作
-
动作(Action):状态转换时执行的操作
- 进入动作(Entry Action):进入状态时执行
- 退出动作(Exit Action):退出状态时执行
- 转换动作(Transition Action):状态转换时执行
- 持续活动(Do Activity):在状态内持续执行
OR状态与AND状态
OR状态(互斥状态)
在传统的有限状态机中,系统在任一时刻只能处于一个状态。这种状态关系称为OR状态,因为系统"或者在这个状态,或者在那个状态,但不能同时在多个状态"。
特点:
- 状态之间互斥
- 通过事件触发转换
- 每个状态有独立的进入/退出动作
AND状态(正交状态)
与状态(AND状态)允许一个独立的状态拥有多个正交(独立)的状态区域。系统可以同时处于多个并行的子状态中。
示例:汽车控制系统
主状态:行驶中
├─ 发动机状态:启动/停止
├─ 灯光状态:开/关
├─ 空调状态:制冷/制热/关闭
└─ 音响状态:播放/暂停
这些子状态相互独立,可以同时变化。例如,发动机启动时,灯光可以独立地打开或关闭。
嵌入式平台的资源限制
为什么嵌入式平台不能使用某些高级特性
嵌入式系统与PC端开发有着本质的区别。PC端由于性能充裕,可以使用各种高级语言特性,如C++的异常处理(try-catch-throw)、运行时类型识别(RTTI)、自动类型推导(auto)等。然而,在资源受限的嵌入式平台上,这些特性往往无法使用或需要谨慎使用。
1. 异常处理机制的限制
PC端:
try {
// 可能抛出异常的代码
risky_operation();
} catch (const std::exception& e) {
// 异常处理
handle_error(e);
}
嵌入式端的问题:
- 代码膨胀:异常处理需要额外的运行时支持,增加ROM占用
- 性能开销:异常处理涉及堆栈展开,增加执行时间
- 确定性问题:异常处理的执行时间不确定,不适合实时系统
- 资源消耗:需要维护异常处理表,增加RAM占用
替代方案:
- 使用错误码返回机制
- 采用状态机处理错误状态
- 使用断言(assert)进行调试期检查
2. 运行时类型识别(RTTI)的限制
C++的typeid和dynamic_cast需要运行时类型信息,这会:
- 增加可执行文件大小
- 降低执行效率
- 增加内存占用
替代方案:
- 使用枚举类型标识对象类型
- 采用虚函数实现多态
- 在状态机中使用状态ID
3. 标准容器的限制
STL容器(如std::vector、std::map)在嵌入式平台上的限制:
- 动态内存分配:可能导致内存碎片
- 代码体积大:模板实例化增加代码量
- 异常安全:容器操作可能抛出异常
替代方案:
- 使用固定大小的数组
- 实现专用的环形缓冲区(FIFO)
- 使用内存池管理动态分配
嵌入式平台的设计原则
1. 确定性(Determinism)
嵌入式系统,特别是实时系统,要求代码的执行时间是可预测的。这意味着:
- 避免动态内存分配
- 禁用异常处理
- 限制递归调用
- 使用固定大小的数据结构
2. 资源效率(Resource Efficiency)
嵌入式平台的资源(RAM、ROM、CPU)都非常有限:
- RAM:通常只有几KB到几百KB
- ROM:通常只有几十KB到几MB
- CPU:主频通常在几十MHz到几百MHz
设计时需要:
- 最小化代码体积
- 优化内存使用
- 减少CPU占用
3. 可维护性(Maintainability)
虽然资源受限,但代码的可维护性仍然重要:
- 使用模块化设计
- 遵循编码规范
- 添加必要的注释
- 使用状态机等设计模式提高可读性
设计模式在嵌入式中的适用性
| 设计模式 | PC端适用性 | 嵌入式适用性 | 限制因素 |
|---|---|---|---|
| 单例模式 | 高 | 高 | 无 |
| 工厂模式 | 高 | 中 | 动态内存分配 |
| 观察者模式 | 高 | 高 | 回调函数数量 |
| 状态机模式 | 高 | 高 | 状态数量 |
| 策略模式 | 高 | 中 | 函数指针开销 |
| 异常处理 | 高 | 低 | 资源消耗 |
| RTTI | 高 | 低 | 代码体积 |
状态机的五种核心设计模式
根据《嵌入式编程设计模式》一书的分类,状态机的实现可以分为五种核心模式。每种模式都有其适用场景和优缺点。
模式一:单事件接收器模式(Single Event Receptor State Machine, SERSM)
核心思想
单事件接收器模式依赖于单一事件接收器在客户与状态机间提供接口。无论当前处于什么状态,所有事件都通过这一个入口处理,内部再根据事件类型和当前状态决定下一步动作。
问题与解决方案
没有此模式时:
// 到处if/else,代码混乱
switch(curState) {
case IDLE: if(eBTN_DOWN) ...; break;
case RUNNING: if(eBTN_DOWN) ...; break;
case ERROR: if(e==BTN_DOWN) ...; break;
}
使用此模式后:
// 一件事一个"专员"
void eventDispatch(Event e) {
switch(e.type) {
case BTN_DOWN: handle_BTN_DOWN(state, e.data); break;
case TIMEOUT: handle_TIMEOUT(state, e.data); break;
// ...
}
}
模式结构
// 事件类型定义
typedef enum {
EV_BTN_DOWN,
EV_BTN_UP,
EV_TIMEOUT,
EV_DATA_RECEIVED
} EventType;
// 事件结构体
typedef struct {
EventType type;
void* data;
} Event;
// 状态枚举
typedef enum {
STATE_IDLE,
STATE_RUNNING,
STATE_ERROR
} State;
// 状态机上下文
typedef struct {
State currentState;
// 其他状态相关数据
} StateMachine;
// 单事件接收器 - 核心分发函数
void eventDispatch(StateMachine* sm, Event e) {
switch(e.type) {
case EV_BTN_DOWN:
handleBtnDown(sm, e.data);
break;
case EV_TIMEOUT:
handleTimeout(sm, e.data);
break;
// ...
}
}
同步vs异步
同步实现:
- 客户直接调用
eventDispatch - 立即执行,简单直接
- 适用于单线程环境
异步实现:
- 客户将事件塞进
EventQueue - 后台任务从队列取出事件并处理
- 需要Mutex保证线程安全
- 适用于多线程或中断环境
优缺点分析
优势:
- 事件处理集中,易于理解
- 支持同步和异步两种模式
- 实现简单,适合小型状态机
劣势:
- 事件分发函数可能变得庞大
- 状态转换逻辑分散在各个事件处理器中
- 添加新状态需要修改多个事件处理器
模式二:多事件接收器模式(Multiple Event Receptor State Machine, MERSM)
核心思想
多事件接收器模式是对单事件接收器模式的改进。核心思想是为每个状态创建独立的事件处理器,而不是将所有逻辑集中在一个巨大的事件分发函数中。给每个状态单独配一把"钥匙"(事件处理器),谁来敲门就用哪把钥匙开门。
实现方式
// 状态A的事件处理器
void handleStateA_Event(Event e) {
switch(e.type) {
case EV_EVENT_X: /* 处理X */ break;
case EV_EVENT_Y: /* 处理Y */ break;
}
}
// 状态B的事件处理器
void handleStateB_Event(Event e) {
switch(e.type) {
case EV_EVENT_X: /* 处理X */ break;
case EV_EVENT_Y: /* 处理Y */ break;
}
}
// 主分发器
void eventDispatch(StateMachine* sm, Event e) {
switch(sm->currentState) {
case STATE_A: handleStateA_Event(e); break;
case STATE_B: handleStateB_Event(e); break;
}
}
完整示例:计算器分词器
// ================== 状态和事件类型定义 ================
typedef enum TSTATETYPE {
NULL_STATE,
NONUMBER_STATE, // 无数字状态
GOTNUMBER_STATE // 有数字状态
} TSTATETYPE;
typedef enum TSUBSTATETYPE {
NULL_SSTATE,
PROCESSINGWHOLEPART_SSTATE, // 处理整数部分
PROCESSINGFRACTIONALPART_SSTATE // 处理小数部分
} TSUBSTATETYPE;
// ================ 分词器结构体 ================
typedef struct TokenizerMultiReceptor {
char ch;
double result;
TSTATETYPE stateID;
TSUBSTATETYPE subStateID;
double tensPlace;
struct Mutex* itsMutex;
} TokenizerMultiReceptor;
// ================ 多事件接收器(核心改进) ==================
// 每个事件类型有独立的处理函数
// 处理数字事件
void TokenizerMultiReceptor_evDigit(TokenizerMultiReceptor* const me, char c) {
Mutex_lock(me->itsMutex);
switch(me->stateID) {
case NONUMBER_STATE:
// 从无数字状态转换到有数字状态
TokenizerMultiReceptor_exit_NoNumber(me);
me->ch = c;
TokenizerMultiReceptor_enter_GotNumber(me);
me->stateID = GOTNUMBER_STATE;
TokenizerMultiReceptor_enter_ProcessingWholePart(me);
me->subStateID = PROCESSINGWHOLEPART_SSTATE;
printf("Current value of result: %g\n", me->result);
break;
case GOTNUMBER_STATE:
// 已经在有数字状态,根据子状态处理
switch(me->subStateID) {
case PROCESSINGWHOLEPART_SSTATE:
TokenizerMultiReceptor_exit_ProcessingWholePart(me);
me->ch = c;
TokenizerMultiReceptor_enter_ProcessingWholePart(me);
printf("Current value of result: %g\n", me->result);
break;
case PROCESSINGFRACTIONALPART_SSTATE:
TokenizerMultiReceptor_exit_ProcessingFractionalPart(me);
me->ch = c;
me->result += digit(me->ch) / me->tensPlace;
me->tensPlace *= 10.0;
TokenizerMultiReceptor_enter_ProcessingFractionalPart(me);
printf("Current value of result: %g\n", me->result);
break;
}
break;
}
Mutex_release(me->itsMutex);
}
// 处理小数点事件
void TokenizerMultiReceptor_evDot(TokenizerMultiReceptor* const me) {
Mutex_lock(me->itsMutex);
me->ch = '.';
switch(me->stateID) {
case NONUMBER_STATE:
TokenizerMultiReceptor_exit_NoNumber(me);
TokenizerMultiReceptor_enter_GotNumber(me);
me->stateID = GOTNUMBER_STATE;
TokenizerMultiReceptor_enter_ProcessingFractionalPart(me);
me->subStateID = PROCESSINGFRACTIONALPART_SSTATE;
break;
case GOTNUMBER_STATE:
switch(me->subStateID) {
case PROCESSINGWHOLEPART_SSTATE:
TokenizerMultiReceptor_exit_ProcessingWholePart(me);
TokenizerMultiReceptor_enter_ProcessingFractionalPart(me);
me->subStateID = PROCESSINGFRACTIONALPART_SSTATE;
break;
case PROCESSINGFRACTIONALPART_SSTATE:
// 第二个小数点是错误
TokenizerMultiReceptor_exit_ProcessingFractionalPart(me);
break;
}
break;
}
Mutex_release(me->itsMutex);
}
// 处理空白字符事件
void TokenizerMultiReceptor_evWhiteSpace(TokenizerMultiReceptor* const me) {
Mutex_lock(me->itsMutex);
switch(me->stateID) {
case GOTNUMBER_STATE:
switch(me->subStateID) {
case PROCESSINGWHOLEPART_SSTATE:
TokenizerMultiReceptor_exit_ProcessingWholePart(me);
break;
case PROCESSINGFRACTIONALPART_SSTATE:
TokenizerMultiReceptor_exit_ProcessingFractionalPart(me);
break;
}
TokenizerMultiReceptor_exit_GotNumber(me);
printf("Number: %g\n", me->result);
TokenizerMultiReceptor_enter_NoNumber(me);
me->stateID = NONUMBER_STATE;
break;
}
Mutex_release(me->itsMutex);
}
// ================== 状态进入和退出动作 ==================
void TokenizerMultiReceptor_enter_GotNumber(TokenizerMultiReceptor* const me) {
me->tensPlace = 10.0;
}
void TokenizerMultiReceptor_enter_NoNumber(TokenizerMultiReceptor* const me) {
me->result = 0.0;
}
void TokenizerMultiReceptor_enter_ProcessingWholePart(TokenizerMultiReceptor* const me) {
me->result = me->result * 10 + digit(me->ch);
}
void TokenizerMultiReceptor_exit_NoNumber(TokenizerMultiReceptor* const me) {
// 清理动作
}
void TokenizerMultiReceptor_exit_ProcessingWholePart(TokenizerMultiReceptor* const me) {
// 清理动作
}
void TokenizerMultiReceptor_exit_ProcessingFractionalPart(TokenizerMultiReceptor* const me) {
// 清理动作
}
// ================== 使用示例 ==================
void TMRClient_example() {
TokenizerMultiReceptor* tokenizer = TokenizerMultiReceptor_Create();
// 处理 "12.34 " 输入序列
TokenizerMultiReceptor_evDigit(tokenizer, '1');
TokenizerMultiReceptor_evDigit(tokenizer, '2');
TokenizerMultiReceptor_evDot(tokenizer);
TokenizerMultiReceptor_evDigit(tokenizer, '3');
TokenizerMultiReceptor_evDigit(tokenizer, '4');
TokenizerMultiReceptor_evWhiteSpace(tokenizer);
// 输出: Number: 12.34
TokenizerMultiReceptor_Destroy(tokenizer);
}
优缺点分析
优势:
- 模块化:每个事件处理器独立,易于理解和维护
- 可扩展:添加新事件只需添加新函数
- 可读性:避免嵌套过深的switch-case
- 测试友好:可以独立测试每个事件处理器
劣势:
- 代码重复:不同事件处理器可能有相似逻辑
- 状态逻辑分散:需要查看多个函数才能理解完整的状态行为
- 函数数量增多:每个事件类型需要一个处理函数
模式三:状态表模式(State Table Pattern)
核心思想
状态表模式通过二维表格管理状态转换,用表格数据代替嵌套的switch-case语句。表格的行代表状态,列代表事件,单元格包含动作和新状态。
模式结构
// 查表代替条件判断
TableEntry te = table[currentState][eventType];
// 标准的动作执行序列
if (guardCondition) {
exitAction(); // 退出当前状态
transitionAction(); // 执行转换动作
entryAction(); // 进入新状态
currentState = newState;
}
完整实现
// ================== 状态表项结构 ================
typedef struct TableEntryType {
ActionPtr entryActionPtr; // 进入动作
ActionPtr exitActionPtr; // 退出动作
GuardType guardPtr; // 守卫条件
TSTATETYPE newState; // 新状态
ActionPtr transActionPtr; // 转换动作
} TableEntryType;
// ================ 分词器结构体 ================
typedef struct TokenizerStateTable {
char ch;
double result;
TSTATETYPE stateID;
// 状态表:[状态数][事件数]的二维数组
TableEntryType table[GN_PROCESSINGFRACTIONALPART_STATE+1][EVENDOFSTRING+1];
double tensPlace;
struct Mutex* itsMutex;
} TokenizerStateTable;
// ================ 初始化状态表 ==================
void TokenizerStateTable_Init(TokenizerStateTable* const me) {
me->ch = 'x';
me->result = 0.0;
me->tensPlace = 10.0;
me->itsMutex = NULL;
TSTATETYPE st;
EventType ev;
TableEntryType te;
// 初始化表格为空状态
for(st = NULL_STATE; st <= GN_PROCESSINGFRACTIONALPART_STATE; st++) {
for(ev = EVDIGIT; ev <= EVENDOFSTRING; ev++) {
me->table[st][ev].newState = NULL_STATE;
me->table[st][ev].guardPtr = (GuardType)NULL;
me->table[st][ev].exitActionPtr = NULL;
me->table[st][ev].transActionPtr = NULL;
me->table[st][ev].entryActionPtr = NULL;
}
}
// 配置具体的状态转换
// 1. NONUMBER_STATE -> GN_PROCESSINGWHOLEPART_STATE (数字事件)
te.guardPtr = (GuardType)NULL;
te.exitActionPtr = (ActionPtr)malloc(sizeof(ActionPtr));
te.exitActionPtr->nParams = 0;
te.exitActionPtr->aPtr.a0 = (ActionPtr0)TokenizerStateTable_exit_NoNumber;
te.transActionPtr = (ActionPtr)malloc(sizeof(ActionPtr));
te.transActionPtr->nParams = 1;
te.transActionPtr->aPtr.a1 = (ActionPtr1)TokenizerStateTable_NoNum2GN;
te.entryActionPtr = (ActionPtr)malloc(sizeof(ActionPtr));
te.entryActionPtr->nParams = 0;
te.entryActionPtr->aPtr.a0 = (ActionPtr0)TokenizerStateTable_enter_ProcessingWholePart;
te.newState = GN_PROCESSINGWHOLEPART_STATE;
me->table[NONUMBER_STATE][EVDIGIT] = te;
// 2. GN_PROCESSINGWHOLEPART_STATE 自转换 (数字事件)
// ... 类似配置
// 初始化为NONUMBER_STATE状态
me->stateID = NONUMBER_STATE;
TokenizerStateTable_enter_NoNumber(me);
}
// ================== 核心函数:事件分发器 ==================
void TokenizerStateTable_eventDispatch(TokenizerStateTable* const me, Event e) {
int takeTransition = 0;
Mutex_lock(me->itsMutex);
// 在表格边界内检查
if(me->stateID >= NULL_STATE && me->stateID <= GN_PROCESSINGFRACTIONALPART_STATE) {
if(e.eType >= EVDIGIT && e.eType <= EVENDOFSTRING) {
// 存在当前状态和事件的有效转换吗?
if(me->table[me->stateID][e.eType].newState != NULL_STATE) {
// 是否有守卫条件?
if(me->table[me->stateID][e.eType].guardPtr == NULL) {
takeTransition = TRUE;
} else {
takeTransition = me->table[me->stateID][e.eType].guardPtr(me);
}
if(takeTransition) {
// 执行退出动作
if(me->table[me->stateID][e.eType].exitActionPtr != NULL) {
if(me->table[me->stateID][e.eType].exitActionPtr->nParams == 0) {
me->table[me->stateID][e.eType].exitActionPtr->aPtr.a0(me);
} else {
me->table[me->stateID][e.eType].exitActionPtr->aPtr.a1(me, e.ed.c);
}
}
// 执行转换动作
if(me->table[me->stateID][e.eType].transActionPtr != NULL) {
if(me->table[me->stateID][e.eType].transActionPtr->nParams == 0) {
me->table[me->stateID][e.eType].transActionPtr->aPtr.a0(me);
} else {
me->table[me->stateID][e.eType].transActionPtr->aPtr.a1(me, e.ed.c);
}
}
// 执行进入动作
if(me->table[me->stateID][e.eType].entryActionPtr != NULL) {
if(me->table[me->stateID][e.eType].entryActionPtr->nParams == 0) {
me->table[me->stateID][e.eType].entryActionPtr->aPtr.a0(me);
} else {
me->table[me->stateID][e.eType].entryActionPtr->aPtr.a1(me, e.ed.c);
}
}
// 更新到新状态
me->stateID = me->table[me->stateID][e.eType].newState;
}
}
}
}
Mutex_release(me->itsMutex);
}
优缺点分析
优势:
- 清晰的结构:所有状态转换逻辑集中在一个表格中
- 易于维护:添加新状态只需增加表格的行,添加新事件只需增加表格的列
- 性能优势:O(1)时间复杂度的查表操作
- 支持高级特性:守卫条件、进入/退出/转换动作分离
劣势:
- 内存占用:需要存储整个状态表
- 初始化复杂:需要填充整个表格
- 灵活性受限:状态转换必须在编译时确定
模式四:状态模式(State Pattern)
核心思想
状态模式通过创建状态类对象来实现状态机。每个状态一个类,拥有状态机的类称为Context,维护这些状态对象的列表,通过一个内部变量识别这些状态中的哪一个是当前状态。
把状态从"变量"升级成"对象":每个状态都是一个迷你机器人,它自己知道该做什么、下一步该变成谁,Context只负责把事件交给当前这位机器人,其他一概不管。
C语言实现(模拟面向对象)
// ================== 状态动作集 ================
typedef struct StateActionSet {
ActionPtr0 entryAction;
ActionPtr1 evDigitHandler;
ActionPtr0 evDotHandler;
ActionPtr0 evEOSHandler;
ActionPtr0 evWSHandler;
ActionPtr0 exitAction;
} StateActionSet;
// ================ Context类 ================
typedef struct Context {
char ch;
TSTATETYPE currentState;
double result;
// 关键:持有所有状态类的指针
struct NoNumberState* stateList[GN_PROCESSINGFRACTIONALPART_STATE+1];
double tensPlace;
struct Mutex* itsMutex;
} Context;
// ================ NoNumberState类 ==================
typedef struct NoNumberState {
struct StateActionSet aSet;
struct Context* itsContext;
} NoNumberState;
// NoNumberState的方法
void NoNumberState_Init(NoNumberState* const me) {
StateActionSet_Init(&me->aSet);
me->itsContext = NULL;
// 设置该状态的动作函数指针
me->aSet.evDigitHandler = (ActionPtr1)NoNumberState_evDigit;
me->aSet.evDotHandler = (ActionPtr0)NoNumberState_evDot;
me->aSet.evWSHandler = (ActionPtr0)NoNumberState_evWhiteSpace;
me->aSet.evEOSHandler = (ActionPtr0)NoNumberState_evEndOfString;
me->aSet.entryAction = (ActionPtr0)NoNumberState_entryAction;
me->aSet.exitAction = (ActionPtr0)NoNumberState_exitAction;
}
int NoNumberState_evDigit(NoNumberState* const me, char c) {
NoNumberState_exitAction(me);
NoNumberState_NoNum2GN(me, c);
// 关键:设置新状态
me->itsContext->currentState = GN_PROCESSINGWHOLEPART_STATE;
return 1; // 处理了事件
}
int NoNumberState_evDot(NoNumberState* const me) {
return 0; // 未处理事件
}
void NoNumberState_entryAction(NoNumberState* const me) {
me->itsContext->result = 0.0;
}
void NoNumberState_exitAction(NoNumberState* const me) {
// 清理动作
}
// ================== Context的方法 ==================
void Context_Init(Context* const me) {
me->ch = 'x';
me->currentState = NULL_STATE;
me->result = 0.0;
me->tensPlace = 10.0;
me->itsMutex = NULL;
// 关键:创建所有状态对象
me->stateList[NONUMBER_STATE] = NoNumberState_Create();
me->stateList[NONUMBER_STATE]->itsContext = me;
me->stateList[GN_PROCESSINGWHOLEPART_STATE] =
(NoNumberState*)GN_ProcessingWholeState_Create();
me->stateList[GN_PROCESSINGWHOLEPART_STATE]->itsContext = me;
me->stateList[GN_PROCESSINGFRACTIONALPART_STATE] =
(NoNumberState*)GN_ProcessingFractionState_Create();
me->stateList[GN_PROCESSINGFRACTIONALPART_STATE]->itsContext = me;
// 进入初始状态
me->stateList[NONUMBER_STATE]->aSet.entryAction(
me->stateList[NONUMBER_STATE]);
}
// 事件处理 - 委托给当前状态对象
void Context_evDigit(Context* const me, char c) {
Mutex_lock(me->itsMutex);
// 调用当前状态的evDigit处理器
if(me->stateList[me->currentState] != NULL) {
me->stateList[me->currentState]->aSet.evDigitHandler(
me->stateList[me->currentState], c);
}
Mutex_release(me->itsMutex);
}
void Context_evDot(Context* const me) {
Mutex_lock(me->itsMutex);
if(me->stateList[me->currentState] != NULL) {
me->stateList[me->currentState]->aSet.evDotHandler(
me->stateList[me->currentState]);
}
Mutex_release(me->itsMutex);
}
优缺点分析
优势:
- 高度模块化:每个状态是独立的类,易于理解和维护
- 符合开闭原则:添加新状态只需添加新类,不修改现有代码
- 消除条件语句:通过多态替代switch-case
- 状态转换清晰:每个状态类明确定义了可能的转换
劣势:
- 类数量增多:每个状态需要一个类
- 内存开销:需要创建和维护多个状态对象
- 性能略低:函数指针调用的开销
- 初始化复杂:需要创建所有状态对象
模式五:分解与状态模式(Decomposition and State Pattern)
核心思想
分解与状态模式带有拥有状态机的对象,并且将它分解为一组交互的对象。主要的对象拥有全局状态机,并且其他的对象拥有各自的与状态(AND状态)。
把一个又大又乱的状态机拆成多个小状态机,每个小机器只管自己的一摊子事(正交区域),然后大家一起协作,主机器只负责"搭台子"。
应用场景
以洗衣机为例:
主洗衣机(全局状态)
├─ 洗涤子机(只关心洗涤)
├─ 脱水子机(只关心脱水)
├─ 烘干子机(只关心烘干)
└─ 门锁子机(只关心门锁)
每个子机只处理自己的状态;主洗衣机只协调"谁在干活"。
实现结构
// 抽象AND状态基类
typedef struct AbstractAndStateClass {
// 虚函数表(函数指针)
struct AbstractAndStateClass* (*event1)(struct AbstractAndStateClass* me);
struct AbstractAndStateClass* (*event2)(struct AbstractAndStateClass* me);
// ... 其他事件
} AbstractAndStateClass;
// 具体AND状态类
typedef struct ConcreteAndStateClass {
AbstractAndStateClass base;
Context* context_;
// 特定数据
} ConcreteAndStateClass;
// Context类 - 管理所有并发状态域
typedef struct Context {
// 维护多个并发的状态域
AbstractAndStateClass* stateRegion1_; // 状态域1
AbstractAndStateClass* stateRegion2_; // 状态域2
// ... 可以有更多状态域
} Context;
// 事件分发函数 - 将事件广播到所有状态域
void Context_handleEvent1(Context* me) {
// 同时更新多个状态域
AbstractAndStateClass* newState1 = me->stateRegion1_->event1(me->stateRegion1_);
if(newState1 != me->stateRegion1_) {
// 删除旧状态,更新为新状态
me->stateRegion1_ = newState1;
}
AbstractAndStateClass* newState2 = me->stateRegion2_->event1(me->stateRegion2_);
if(newState2 != me->stateRegion2_) {
me->stateRegion2_ = newState2;
}
}
完整示例:灯光控制器
// 状态枚举定义
typedef enum HighLevelLightStates {
NULL_STATE,
OFF_STATE,
ON_STATE,
EMERGENCY_STATE
} HighLevelLightStates;
typedef enum ErrorStatesType {
NULL_ERROR_STATE,
OK_STATE,
WARNING_STATE,
ERROR_STATE
} ErrorStatesType;
typedef enum ModeStatesType {
NULL_MODE_STATE,
OPERATIONAL_STATE,
STARTINGUP_STATE,
SHUTTINGDOWN_STATE
} ModeStatesType;
// AND状态列表管理器
typedef struct AndStateList {
#define MAX_ANDSTATES 2
ErrorStateClass* andStates[MAX_ANDSTATES];
int nAndStates;
} AndStateList;
void AndStateList_Create(AndStateList* me, LightController* lc) {
// 创建两个并发状态域
me->andStates[0] = ErrorStateClass_Create(lc); // 错误状态域
me->andStates[1] = ModeStateClass_Create(lc); // 运行模式域
me->nAndStates = 2;
}
void AndStateList_evError(AndStateList* me) {
// 将错误事件广播到所有状态域
for(int i = 0; i < me->nAndStates; i++) {
if(me->andStates[i]) {
me->andStates[i]->evError(me->andStates[i]);
}
}
}
void AndStateList_evRun(AndStateList* me) {
// 将运行事件广播到所有状态域
for(int i = 0; i < me->nAndStates; i++) {
if(me->andStates[i]) {
me->andStates[i]->evRun(me->andStates[i]);
}
}
}
// 主控制器
typedef struct LightController {
HighLevelLightStates currentState_;
AndStateList* asList_;
Klaxon* itsKlaxon_;
Light* itsLight_;
Mutex* itsMutex_;
} LightController;
void LightController_Init(LightController* me) {
me->currentState_ = OFF_STATE;
me->asList_ = AndStateList_Create();
me->itsKlaxon_ = Klaxon_Create();
me->itsLight_ = Light_Create();
me->itsMutex_ = Mutex_Create();
LightController_enter_OffState(me, OFF_STATE);
}
// 事件处理函数
void LightController_evEnable(LightController* me) {
HighLevelLightStates newState;
Mutex_lock(me->itsMutex_);
switch(me->currentState_) {
case OFF_STATE:
LightController_exit_OffState(me);
newState = ON_STATE;
LightController_enter_OnState(me, newState);
me->currentState_ = newState;
// 创建并初始化所有并发状态域
AndStateList_Create(me->asList_, me);
for(int i = 0; i < me->asList_->nAndStates; i++) {
me->asList_->andStates[i]->Init(me->asList_->andStates[i]);
}
break;
case ON_STATE:
case EMERGENCY_STATE:
// 在这些状态下忽略enable事件
break;
}
Mutex_release(me->itsMutex_);
}
void LightController_evError(LightController* me) {
Mutex_lock(me->itsMutex_);
if(me->currentState_ ON_STATE) {
// 将事件委托给AND状态处理
AndStateList_evError(me->asList_);
}
Mutex_release(me->itsMutex_);
}
void LightController_evRun(LightController* me) {
Mutex_lock(me->itsMutex_);
if(me->currentState_ ON_STATE) {
AndStateList_evRun(me->asList_);
}
Mutex_release(me->itsMutex_);
}
// 使用示例
int main() {
LightController* controller = LightController_Create();
// 1. 启动系统
LightController_evEnable(controller);
// OFF -> ON (进入StartingUp + OK状态)
// 2. 系统运行中
LightController_evRun(controller);
// StartingUp -> Operational
// 3. 出现警告
LightController_evWarning(controller);
// OK -> Warning (黄灯)
// 4. 警告升级为错误
LightController_evError(controller);
// Warning -> Error (红灯)
// 5. 问题解决
LightController_evOk(controller);
// Error -> OK (绿灯)
// 6. 准备关闭
LightController_evShutDown(controller);
// Operational -> ShuttingDown
// 7. 关闭系统
LightController_evDisable(controller);
// ON -> OFF
LightController_Destroy(controller);
return 0;
}
优缺点分析
优势:
- 支持并发状态:可以同时管理多个正交状态
- 模块化程度高:每个AND状态独立管理
- 可扩展性好:可以动态添加或移除状态域
- 代码复用:子状态机可以在不同上下文中复用
劣势:
- 复杂性高:需要管理多个状态域的交互
- 调试困难:并发状态的调试比较复杂
- 资源消耗:需要维护多个状态对象
FIFO与单片机通信中断
为什么需要FIFO
在单片机通信中,数据的接收和发送往往以突发方式进行。如果没有缓冲区,每收到一个字节就需要立即处理,这会导致:
- CPU占用率高:频繁中断主程序
- 数据丢失风险:如果处理不及时,新数据会覆盖旧数据
- 实时性差:必须等待当前数据处理完成
FIFO(First In First Out,先进先出队列)提供了一种缓冲机制,允许:
- 异步处理:接收和数据处理解耦
- 突发数据吸收:短时间内接收大量数据不会丢失
- 平滑数据流:以稳定速率处理数据
环形缓冲区的原理
环形缓冲区(Ring Buffer)是FIFO的一种高效实现。它使用固定大小的数组,通过两个指针(写指针和读指针)管理数据的存入和取出。
数学模型
缓冲区大小:N
写指针:write_idx (0 <= write_idx < N)
读指针:read_idx (0 <= read_idx < N)
已用空间:(write_idx - read_idx + N) % N
可用空间:N - 1 - 已用空间
关键操作
写入数据:
if (fifo_full()) return ERROR;
buffer[write_idx] = data;
write_idx = (write_idx + 1) % N;
读取数据:
if (fifo_empty()) return ERROR;
data = buffer[read_idx];
read_idx = (read_idx + 1) % N;
线程安全的FIFO实现
在嵌入式系统中,FIFO通常被中断服务程序(ISR)和主程序同时访问。为了保证数据一致性,需要实现线程安全机制。
禁用中断方案(单生产者单消费者)
typedef struct {
volatile uint8_t* buffer;
volatile uint16_t head; // 写指针
volatile uint16_t tail; // 读指针
volatile uint16_t size; // 缓冲区大小
} RingBuffer;
// 初始化FIFO
void RingBuffer_Init(RingBuffer* rb, uint8_t* buffer, uint16_t size) {
rb->buffer = buffer;
rb->head = 0;
rb->tail = 0;
rb->size = size;
}
// 检查是否为空
uint8_t RingBuffer_IsEmpty(RingBuffer* rb) {
return (rb->head rb->tail);
}
// 检查是否已满
uint8_t RingBuffer_IsFull(RingBuffer* rb) {
return ((rb->head + 1) % rb->size rb->tail);
}
// 写入数据(在主程序中调用)
uint8_t RingBuffer_Write(RingBuffer* rb, uint8_t data) {
// 禁用中断
__disable_irq();
if(RingBuffer_IsFull(rb)) {
__enable_irq();
return 0; // 失败
}
rb->buffer[rb->head] = data;
rb->head = (rb->head + 1) % rb->size;
__enable_irq();
return 1; // 成功
}
// 读取数据(在主程序中调用)
uint8_t RingBuffer_Read(RingBuffer* rb, uint8_t* data) {
__disable_irq();
if(RingBuffer_IsEmpty(rb)) {
__enable_irq();
return 0; // 失败
}
*data = rb->buffer[rb->tail];
rb->tail = (rb->tail + 1) % rb->size;
__enable_irq();
return 1; // 成功
}
// 写入数据(在中断中调用)
uint8_t RingBuffer_Write_ISR(RingBuffer* rb, uint8_t data) {
// 中断中已禁用中断,无需额外操作
if(RingBuffer_IsFull(rb)) {
return 0;
}
rb->buffer[rb->head] = data;
rb->head = (rb->head + 1) % rb->size;
return 1;
}
// 读取数据(在中断中调用)
uint8_t RingBuffer_Read_ISR(RingBuffer* rb, uint8_t* data) {
if(RingBuffer_IsEmpty(rb)) {
return 0;
}
*data = rb->buffer[rb->tail];
rb->tail = (rb->tail + 1) % rb->size;
return 1;
}
单片机串口通信中的FIFO应用
场景:STM32串口接收大量数据
在STM32中,使用DMA+空闲中断+FIFO可以实现高效的串口数据接收。
// DMA接收缓冲区
#define UART_RX_BUF_SIZE 256
uint8_t uartRxBuf[UART_RX_BUF_SIZE];
// FIFO缓冲区
#define FIFO_SIZE 512
uint8_t fifoBuffer[FIFO_SIZE];
RingBuffer uartFifo;
// 初始化
void UART_Init(void) {
// 初始化FIFO
RingBuffer_Init(&uartFifo, fifoBuffer, FIFO_SIZE);
// 配置UART DMA接收
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, uartRxBuf, UART_RX_BUF_SIZE);
// 显式启用DMA半满和全满中断
__HAL_DMA_ENABLE_IT(&hdma_usart1_rx, DMA_IT_HT | DMA_IT_TC);
}
// DMA中断回调(半满/全满/空闲)
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {
if(huart->Instance == USART1) {
switch(HAL_UARTEx_GetRxEventType(huart)) {
case HAL_UART_RXEVENT_TC: // 全满中断
// DMA缓冲区已满,将数据转移到FIFO
for(uint16_t i = 0; i < UART_RX_BUF_SIZE; i++) {
RingBuffer_Write_ISR(&uartFifo, uartRxBuf[i]);
}
break;
case HAL_UART_RXEVENT_HT: // 半满中断
// DMA缓冲区半满,转移前半部分数据
for(uint16_t i = 0; i < UART_RX_BUF_SIZE / 2; i++) {
RingBuffer_Write_ISR(&uartFifo, uartRxBuf[i]);
}
break;
case HAL_UART_RXEVENT_IDLE: // 空闲中断
// 计算已接收数据长度
uint16_t recvLen = UART_RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(huart->hdmarx);
// 转移数据到FIFO
for(uint16_t i = 0; i < recvLen; i++) {
RingBuffer_Write_ISR(&uartFifo, uartRxBuf[i]);
}
// 触发应用层回调
appDataReceivedCallback();
break;
}
}
}
// 主程序中处理数据
void ProcessReceivedData(void) {
uint8_t data;
while(RingBuffer_Read(&uartFifo, &data)) {
// 处理每个字节
StateMachine_ProcessByte(&protocolSM, data);
}
}
FIFO的高级特性
1. 多生产者多消费者支持
使用信号量或互斥锁实现:
typedef struct {
uint8_t* buffer;
volatile uint16_t head;
volatile uint16_t tail;
uint16_t size;
SemaphoreHandle_t mutex; // 互斥锁
SemaphoreHandle_t dataAvail; // 数据可用信号量
SemaphoreHandle_t spaceAvail; // 空间可用信号量
} ThreadSafeFIFO;
uint8_t ThreadSafeFIFO_Write(ThreadSafeFIFO* fifo, uint8_t data, uint32_t timeout) {
// 等待空间可用
if(xSemaphoreTake(fifo->spaceAvail, timeout) != pdTRUE) {
return 0;
}
// 获取互斥锁
if(xSemaphoreTake(fifo->mutex, timeout) != pdTRUE) {
xSemaphoreGive(fifo->spaceAvail);
return 0;
}
// 写入数据
fifo->buffer[fifo->head] = data;
fifo->head = (fifo->head + 1) % fifo->size;
xSemaphoreGive(fifo->mutex);
xSemaphoreGive(fifo->dataAvail);
return 1;
}
uint8_t ThreadSafeFIFO_Read(ThreadSafeFIFO* fifo, uint8_t* data, uint32_t timeout) {
// 等待数据可用
if(xSemaphoreTake(fifo->dataAvail, timeout) != pdTRUE) {
return 0;
}
// 获取互斥锁
if(xSemaphoreTake(fifo->mutex, timeout) != pdTRUE) {
xSemaphoreGive(fifo->dataAvail);
return 0;
}
// 读取数据
*data = fifo->buffer[fifo->tail];
fifo->tail = (fifo->tail + 1) % fifo->size;
xSemaphoreGive(fifo->mutex);
xSemaphoreGive(fifo->spaceAvail);
return 1;
}
2. 动态扩容FIFO
对于需要动态调整大小的场景:
typedef struct {
uint8_t* buffer;
uint16_t head;
uint16_t tail;
uint16_t size;
uint16_t capacity;
} DynamicFIFO;
uint8_t DynamicFIFO_Resize(DynamicFIFO* fifo, uint16_t newCapacity) {
uint8_t* newBuffer = (uint8_t*)malloc(newCapacity);
if(!newBuffer) return 0;
// 复制现有数据
uint16_t count = 0;
uint8_t data;
while(DynamicFIFO_Read(fifo, &data) && count < newCapacity) {
newBuffer[count++] = data;
}
// 释放旧缓冲区
free(fifo->buffer);
// 更新结构
fifo->buffer = newBuffer;
fifo->head = count;
fifo->tail = 0;
fifo->size = count;
fifo->capacity = newCapacity;
return 1;
}
面向对象vs面向过程:C语言实现状态机
面向对象的核心概念
面向对象编程(OOP)的核心是:
- 封装:将数据和操作数据的代码打包在一起
- 继承:子类继承父类的属性和方法
- 多态:同一接口,不同实现
C语言模拟面向对象
虽然C语言不是面向对象语言,但可以通过结构体和函数指针模拟OOP特性。
1. 封装
// 用结构体封装数据和方法
typedef struct {
// 数据成员
int pin;
int state;
// 方法成员(函数指针)
void (*press)(void);
void (*release)(void);
} Key;
// 具体实现
void key1_press() {
LED_On();
}
void key1_release() {
LED_Off();
}
// 使用
Key key1 = {
.pin = GPIO_PIN_0,
.state = KEY_STATE_IDLE,
.press = key1_press,
.release = key1_release
};
key1.press(); // 调用方法
2. 继承
// 基类
typedef struct {
int value;
int (*getValue)(struct GenericSensor*);
void (*setValue)(struct GenericSensor*, int);
} GenericSensor;
// 派生类
typedef struct {
GenericSensor base; // 嵌套基类结构体
float temperature; // 特有属性
float offset; // 校准偏移
} TemperatureSensor;
// 派生类方法
float TemperatureSensor_getCelsius(TemperatureSensor* me) {
return me->base.value * 0.1f + me->offset;
}
// 使用
TemperatureSensor tempSensor;
tempSensor.base.getValue = GenericSensor_getValue;
tempSensor.base.setValue = GenericSensor_setValue;
tempSensor.base.setValue(&tempSensor.base, 250); // 设置25.0度
3. 多态
// 统一接口
typedef struct {
void (*init)(void*);
int (*read)(void*);
void (*deinit)(void*);
} SensorInterface;
// 不同传感器实现
typedef struct {
SensorInterface interface;
// 特定数据
} TemperatureSensor;
typedef struct {
SensorInterface interface;
// 特定数据
} PressureSensor;
// 多态调用
void processSensor(SensorInterface* sensor) {
sensor->init(sensor);
int value = sensor->read(sensor);
sensor->deinit(sensor);
}
// 使用
TemperatureSensor temp;
PressureSensor pressure;
processSensor(&temp.interface); // 调用温度传感器实现
processSensor(&pressure.interface); // 调用压力传感器实现
面向对象状态机的实现
// 状态机基类
typedef struct StateMachine {
int currentState;
void (*transition)(struct StateMachine*, int);
void (*handleEvent)(struct StateMachine*, int);
void (*entryAction)(struct StateMachine*);
void (*exitAction)(struct StateMachine*);
} StateMachine;
// 具体状态机
typedef struct {
StateMachine base;
// 特定数据
int retryCount;
uint32_t timeout;
} CommunicationStateMachine;
// 方法实现
void CommunicationStateMachine_handleEvent(StateMachine* sm, int event) {
CommunicationStateMachine* csm = (CommunicationStateMachine*)sm;
switch(sm->currentState) {
case STATE_IDLE:
if(event EV_CONNECT) {
sm->transition(sm, STATE_CONNECTING);
}
break;
case STATE_CONNECTING:
if(event EV_CONNECTED) {
sm->transition(sm, STATE_CONNECTED);
} else if(event == EV_TIMEOUT) {
csm->retryCount++;
if(csm->retryCount < MAX_RETRY) {
sm->transition(sm, STATE_CONNECTING); // 重试
} else {
sm->transition(sm, STATE_ERROR);
}
}
break;
// ... 其他状态
}
}
// 使用
CommunicationStateMachine commSM;
commSM.base.currentState = STATE_IDLE;
commSM.base.handleEvent = CommunicationStateMachine_handleEvent;
commSM.retryCount = 0;
commSM.timeout = 5000;
commSM.base.handleEvent(&commSM.base, EV_CONNECT);
面向过程vs面向对象的对比
| 特性 | 面向过程 | 面向对象(C模拟) |
|---|---|---|
| 代码组织 | 函数+全局变量 | 结构体+函数指针 |
| 封装性 | 弱 | 强 |
| 可维护性 | 一般 | 好 |
| 可扩展性 | 一般 | 好 |
| 代码体积 | 小 | 稍大 |
| 运行时开销 | 低 | 函数指针调用开销 |
| 调试难度 | 简单 | 稍复杂 |
| 适用场景 | 简单系统 | 复杂系统 |
选择建议
使用面向过程:
- 系统简单,状态数量少
- 资源极度受限(RAM < 1KB)
- 对性能要求极高
- 团队不熟悉OOP概念
使用面向对象(C模拟):
- 系统复杂,状态数量多
- 需要良好的模块化
- 代码需要长期维护
- 团队有OOP经验
FreeRTOS与状态机的结合
FreeRTOS任务状态
FreeRTOS本身就是一个基于状态机的调度系统。每个任务在其生命周期中会经历以下状态:
typedef enum {
eRunning = 0, // 运行态:当前占用CPU
eReady, // 就绪态:等待CPU调度
eBlocked, // 阻塞态:等待事件或延时
eSuspended, // 挂起态:被显式暂停
eDeleted, // 删除态:等待内核清理资源
eInvalid // 无效状态
} eTaskState;
状态转换关系
就绪态(Ready) ←──────┬──────→ 运行态(Running)
↑ │ │
│ │ │ 阻塞API调用
│ │ ↓
│ └──────── 阻塞态(Blocked)
│ │
│ │ 事件发生
│ │
└─────────────────────────────┘
运行态/就绪态/阻塞态 ──vTaskSuspend()──→ 挂起态(Suspended)
挂起态 ──vTaskResume()──→ 就绪态
在FreeRTOS任务中实现状态机
基本框架
// 定义任务状态
typedef enum {
TASK_STATE_INIT,
TASK_STATE_PROCESSING,
TASK_STATE_WAITING,
TASK_STATE_ERROR,
TASK_STATE_SHUTDOWN
} TaskState;
// 任务数据结构
typedef struct {
TaskState state;
QueueHandle_t eventQueue;
uint32_t timeout;
int retryCount;
// 其他数据
} TaskContext;
// 任务主函数
void vStateMachineTask(void *pvParameters) {
TaskContext ctx;
TaskContext_Init(&ctx);
Event_t event;
for(;;) {
// 等待事件(带超时)
if(xQueueReceive(ctx.eventQueue, &event, ctx.timeout) pdTRUE) {
// 收到事件,处理状态转换
TaskState_HandleEvent(&ctx, &event);
} else {
// 超时,处理超时事件
TaskState_HandleTimeout(&ctx);
}
}
}
// 状态事件处理
void TaskState_HandleEvent(TaskContext* ctx, Event_t* event) {
switch(ctx->state) {
case TASK_STATE_INIT:
if(event->type EV_START) {
ctx->state = TASK_STATE_PROCESSING;
// 执行进入动作
DoProcessing();
}
break;
case TASK_STATE_PROCESSING:
if(event->type == EV_DATA_RECEIVED) {
ProcessData(event->data);
ctx->state = TASK_STATE_WAITING;
ctx->timeout = pdMS_TO_TICKS(1000);
} else if(event->type == EV_ERROR) {
ctx->state = TASK_STATE_ERROR;
HandleError();
}
break;
case TASK_STATE_WAITING:
if(event->type == EV_RESPONSE) {
ctx->state = TASK_STATE_PROCESSING;
ctx->retryCount = 0;
} else if(event->type == EV_TIMEOUT) {
ctx->retryCount++;
if(ctx->retryCount < MAX_RETRY) {
ctx->state = TASK_STATE_PROCESSING;
ResendRequest();
} else {
ctx->state = TASK_STATE_ERROR;
}
}
break;
case TASK_STATE_ERROR:
if(event->type == EV_RESET) {
ctx->state = TASK_STATE_INIT;
ctx->retryCount = 0;
}
break;
default:
break;
}
}
使用任务通知实现轻量级状态机
FreeRTOS的任务通知提供了一种轻量级的同步机制,可以用来实现简单状态机。
// 状态定义
typedef enum {
STATE_IDLE,
STATE_BUSY,
STATE_DONE
} WorkerState;
// 工作者任务
void vWorkerTask(void *pvParameters) {
WorkerState state = STATE_IDLE;
uint32_t notificationValue;
for(;;) {
switch(state) {
case STATE_IDLE:
// 等待工作请求
if(xTaskNotifyWait(0, 0xFFFFFFFF, ¬ificationValue, portMAX_DELAY) pdTRUE) {
if(notificationValue CMD_START_WORK) {
state = STATE_BUSY;
DoWork();
// 通知完成
xTaskNotify(xControllerTask, WORK_DONE, eSetValueWithOverwrite);
}
}
break;
case STATE_BUSY:
// 工作中,检查取消请求
if(xTaskNotifyWait(0, 0xFFFFFFFF, ¬ificationValue, 0) pdTRUE) {
if(notificationValue CMD_CANCEL) {
CancelWork();
state = STATE_IDLE;
}
}
// 工作完成
if(IsWorkComplete()) {
state = STATE_DONE;
}
break;
case STATE_DONE:
xTaskNotify(xControllerTask, WORK_COMPLETE, eSetValueWithOverwrite);
state = STATE_IDLE;
break;
}
}
}
使用事件标志组实现复杂状态机
对于需要管理多个并发事件的状态机,可以使用事件标志组。
// 事件定义
#define EV_BUTTON_PRESSED (1 << 0)
#define EV_DATA_READY (1 << 1)
#define EV_TIMEOUT (1 << 2)
#define EV_ERROR (1 << 3)
#define EV_ALL_EVENTS (EV_BUTTON_PRESSED | EV_DATA_READY | EV_TIMEOUT | EV_ERROR)
EventGroupHandle_t xEventGroup;
void vComplexStateMachine(void *pvParameters) {
State_t state = STATE_INIT;
EventBits_t events;
for(;;) {
// 等待任意事件
events = xEventGroupWaitBits(
xEventGroup,
EV_ALL_EVENTS,
pdTRUE, // 清除事件位
pdFALSE, // 等待任意事件
portMAX_DELAY
);
switch(state) {
case STATE_INIT:
if(events & EV_BUTTON_PRESSED) {
state = STATE_STARTING;
StartOperation();
}
break;
case STATE_STARTING:
if(events & EV_DATA_READY) {
state = STATE_RUNNING;
ProcessData();
} else if(events & EV_ERROR) {
state = STATE_ERROR;
HandleError();
}
break;
case STATE_RUNNING:
if(events & EV_DATA_READY) {
ProcessData();
} else if(events & EV_TIMEOUT) {
state = STATE_TIMEOUT;
HandleTimeout();
} else if(events & EV_ERROR) {
state = STATE_ERROR;
HandleError();
}
break;
// ... 其他状态
}
}
}
FreeRTOS状态机最佳实践
1. 任务优先级设计
// 定义任务优先级
#define PRIORITY_HIGH 3
#define PRIORITY_NORMAL 2
#define PRIORITY_LOW 1
#define PRIORITY_IDLE 0
// 创建不同优先级的状态机任务
xTaskCreate(vHighPrioritySM, "HighSM", 256, NULL, PRIORITY_HIGH, NULL);
xTaskCreate(vNormalPrioritySM, "NormalSM", 256, NULL, PRIORITY_NORMAL, NULL);
xTaskCreate(vLowPrioritySM, "LowSM", 256, NULL, PRIORITY_LOW, NULL);
2. 栈大小优化
// 使用uxTaskGetStackHighWaterMark监控栈使用
void vMonitorTask(void *pvParameters) {
for(;;) {
UBaseType_t highWaterMark;
highWaterMark = uxTaskGetStackHighWaterMark(xHighPrioritySMHandle);
printf("HighSM stack free: %u words\n", highWaterMark);
highWaterMark = uxTaskGetStackHighWaterMark(xNormalPrioritySMHandle);
printf("NormalSM stack free: %u words\n", highWaterMark);
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
3. 低功耗设计
void vLowPowerStateMachine(void *pvParameters) {
for(;;) {
// 等待事件,进入低功耗模式
EventBits_t events = xEventGroupWaitBits(
xEventGroup,
EV_ALL_EVENTS,
pdTRUE,
pdFALSE,
pdMS_TO_TICKS(1000) // 1秒超时
);
if(events == 0) {
// 超时,无事件发生,进入低功耗
EnterLowPowerMode();
} else {
// 处理事件
ProcessEvents(events);
}
}
}
实际项目案例:智能小车控制系统
项目概述
智能小车是一个典型的嵌入式系统,涉及电机控制、传感器读取、通信处理等多个模块。使用状态机可以将复杂的控制逻辑清晰地组织起来。
系统架构
智能小车系统
├─ 主控制状态机(全局协调)
│ ├─ 待机状态
│ ├─ 运行状态
│ │ ├─ 子状态机:运动控制
│ │ │ ├─ 停止
│ │ │ ├─ 前进
│ │ │ ├─ 后退
│ │ │ ├─ 左转
│ │ │ └─ 右转
│ │ ├─ 子状态机:避障控制
│ │ │ ├─ 正常行驶
│ │ │ ├─ 检测到障碍
│ │ │ ├─ 减速
│ │ │ └─ 转向避障
│ │ └─ 子状态机:循迹控制
│ │ ├─ 在线行驶
│ │ ├─ 偏离左
│ │ └─ 偏离右
│ ├─ 暂停状态
│ └─ 错误状态
├─ 通信状态机(蓝牙/WiFi)
├─ 传感器状态机(超声波、红外、编码器)
└─ 电池管理状态机
主控制状态机实现
// ================== 状态定义 ================
typedef enum {
CAR_STATE_IDLE,
CAR_STATE_RUNNING,
CAR_STATE_PAUSED,
CAR_STATE_ERROR,
CAR_STATE_SHUTDOWN
} CarMainState;
typedef enum {
MOTION_STATE_STOPPED,
MOTION_STATE_FORWARD,
MOTION_STATE_BACKWARD,
MOTION_STATE_TURNING_LEFT,
MOTION_STATE_TURNING_RIGHT
} CarMotionState;
typedef enum {
OBSTACLE_STATE_CLEAR,
OBSTACLE_STATE_DETECTED,
OBSTACLE_STATE_AVOIDING
} CarObstacleState;
// ================ 事件定义 ================
typedef enum {
EV_CMD_START,
EV_CMD_STOP,
EV_CMD_PAUSE,
EV_CMD_RESUME,
EV_CMD_FORWARD,
EV_CMD_BACKWARD,
EV_CMD_LEFT,
EV_CMD_RIGHT,
EV_OBSTACLE_DETECTED,
EV_OBSTACLE_CLEARED,
EV_BATTERY_LOW,
EV_ERROR_OCCURRED,
EV_ERROR_CLEARED
} CarEventType;
typedef struct {
CarEventType type;
void* data;
} CarEvent;
// ================ 上下文结构体 ================
typedef struct {
CarMainState mainState;
CarMotionState motionState;
CarObstacleState obstacleState;
QueueHandle_t eventQueue;
// 硬件接口
MotorDriver* motorDriver;
UltrasonicSensor* ultrasonic;
BluetoothModule* bluetooth;
BatteryMonitor* battery;
// 状态数据
uint8_t speed;
uint8_t obstacleDistance;
uint8_t batteryLevel;
// 标志位
uint8_t isEmergencyStop;
uint8_t isObstacleAvoidanceEnabled;
} CarContext;
// ================ 状态机初始化 ==================
void CarStateMachine_Init(CarContext* ctx) {
ctx->mainState = CAR_STATE_IDLE;
ctx->motionState = MOTION_STATE_STOPPED;
ctx->obstacleState = OBSTACLE_STATE_CLEAR;
ctx->eventQueue = xQueueCreate(20, sizeof(CarEvent));
ctx->motorDriver = MotorDriver_Create();
ctx->ultrasonic = UltrasonicSensor_Create();
ctx->bluetooth = BluetoothModule_Create();
ctx->battery = BatteryMonitor_Create();
ctx->speed = 0;
ctx->obstacleDistance = 255;
ctx->batteryLevel = 100;
ctx->isEmergencyStop = 0;
ctx->isObstacleAvoidanceEnabled = 1;
}
// ================== 主状态处理 ================
void CarStateMachine_HandleEvent(CarContext* ctx, CarEvent* event) {
switch(ctx->mainState) {
case CAR_STATE_IDLE:
CarState_Idle_HandleEvent(ctx, event);
break;
case CAR_STATE_RUNNING:
CarState_Running_HandleEvent(ctx, event);
break;
case CAR_STATE_PAUSED:
CarState_Paused_HandleEvent(ctx, event);
break;
case CAR_STATE_ERROR:
CarState_Error_HandleEvent(ctx, event);
break;
default:
break;
}
}
// ================ 空闲状态 ==================
void CarState_Idle_HandleEvent(CarContext* ctx, CarEvent* event) {
switch(event->type) {
case EV_CMD_START:
// 进入运行状态
ctx->mainState = CAR_STATE_RUNNING;
ctx->motionState = MOTION_STATE_STOPPED;
// 启动各个模块
MotorDriver_Enable(ctx->motorDriver);
UltrasonicSensor_Start(ctx->ultrasonic);
printf("Car started, entering RUNNING state\n");
break;
default:
// 在空闲状态,忽略其他事件
break;
}
}
// ================== 运行状态 ==================
void CarState_Running_HandleEvent(CarContext* ctx, CarEvent* event) {
switch(event->type) {
case EV_CMD_STOP:
// 停止所有运动
CarMotion_Stop(ctx);
ctx->mainState = CAR_STATE_IDLE;
printf("Car stopped, entering IDLE state\n");
break;
case EV_CMD_PAUSE:
CarMotion_Stop(ctx);
ctx->mainState = CAR_STATE_PAUSED;
printf("Car paused\n");
break;
case EV_CMD_FORWARD:
if(ctx->obstacleState != OBSTACLE_STATE_DETECTED) {
CarMotion_SetForward(ctx, 80); // 80%速度前进
}
break;
case EV_CMD_BACKWARD:
CarMotion_SetBackward(ctx, 60);
break;
case EV_CMD_LEFT:
CarMotion_TurnLeft(ctx, 60);
break;
case EV_CMD_RIGHT:
CarMotion_TurnRight(ctx, 60);
break;
case EV_OBSTACLE_DETECTED:
ctx->obstacleState = OBSTACLE_STATE_DETECTED;
if(ctx->isObstacleAvoidanceEnabled) {
// 自动避障
CarMotion_Stop(ctx);
vTaskDelay(pdMS_TO_TICKS(500));
CarMotion_SetBackward(ctx, 50);
vTaskDelay(pdMS_TO_TICKS(300));
CarMotion_TurnRight(ctx, 60);
vTaskDelay(pdMS_TO_TICKS(500));
CarMotion_SetForward(ctx, 80);
ctx->obstacleState = OBSTACLE_STATE_AVOIDING;
}
break;
case EV_OBSTACLE_CLEARED:
ctx->obstacleState = OBSTACLE_STATE_CLEAR;
printf("Obstacle cleared\n");
break;
case EV_BATTERY_LOW:
// 电量低,减速并提示
CarMotion_SetSpeed(ctx, ctx->speed / 2);
Bluetooth_SendAlert(ctx->bluetooth, "Battery Low!");
break;
case EV_ERROR_OCCURRED:
CarMotion_EmergencyStop(ctx);
ctx->mainState = CAR_STATE_ERROR;
ctx->isEmergencyStop = 1;
printf("Error occurred! Entering ERROR state\n");
break;
default:
break;
}
}
// ================== 暂停状态 ==================
void CarState_Paused_HandleEvent(CarContext* ctx, CarEvent* event) {
switch(event->type) {
case EV_CMD_RESUME:
ctx->mainState = CAR_STATE_RUNNING;
printf("Car resumed\n");
break;
case EV_CMD_STOP:
ctx->mainState = CAR_STATE_IDLE;
printf("Car stopped from PAUSED state\n");
break;
default:
break;
}
}
// ================== 错误状态 ==================
void CarState_Error_HandleEvent(CarContext* ctx, CarEvent* event) {
switch(event->type) {
case EV_ERROR_CLEARED:
if(!ctx->isEmergencyStop) {
ctx->mainState = CAR_STATE_IDLE;
printf("Error cleared, entering IDLE state\n");
}
break;
case EV_CMD_STOP:
ctx->mainState = CAR_STATE_SHUTDOWN;
printf("Shutting down...\n");
break;
default:
// 错误状态下,只接受有限的命令
break;
}
}
// ================== 运动控制函数 ==================
void CarMotion_SetForward(CarContext* ctx, uint8_t speed) {
ctx->motionState = MOTION_STATE_FORWARD;
ctx->speed = speed;
MotorDriver_SetDirection(ctx->motorDriver, DIR_FORWARD);
MotorDriver_SetSpeed(ctx->motorDriver, speed);
printf("Moving forward at %d%% speed\n", speed);
}
void CarMotion_SetBackward(CarContext* ctx, uint8_t speed) {
ctx->motionState = MOTION_STATE_BACKWARD;
ctx->speed = speed;
MotorDriver_SetDirection(ctx->motorDriver, DIR_BACKWARD);
MotorDriver_SetSpeed(ctx->motorDriver, speed);
printf("Moving backward at %d%% speed\n", speed);
}
void CarMotion_TurnLeft(CarContext* ctx, uint8_t speed) {
ctx->motionState = MOTION_STATE_TURNING_LEFT;
ctx->speed = speed;
MotorDriver_SetLeftSpeed(ctx->motorDriver, speed / 2);
MotorDriver_SetRightSpeed(ctx->motorDriver, speed);
printf("Turning left\n");
}
void CarMotion_TurnRight(CarContext* ctx, uint8_t speed) {
ctx->motionState = MOTION_STATE_TURNING_RIGHT;
ctx->speed = speed;
MotorDriver_SetLeftSpeed(ctx->motorDriver, speed);
MotorDriver_SetRightSpeed(ctx->motorDriver, speed / 2);
printf("Turning right\n");
}
void CarMotion_Stop(CarContext* ctx) {
ctx->motionState = MOTION_STATE_STOPPED;
ctx->speed = 0;
MotorDriver_Stop(ctx->motorDriver);
printf("Stopped\n");
}
void CarMotion_EmergencyStop(CarContext* ctx) {
ctx->motionState = MOTION_STATE_STOPPED;
ctx->speed = 0;
MotorDriver_EmergencyBrake(ctx->motorDriver);
printf("EMERGENCY STOP!\n");
}
// ================== 传感器任务 ==================
void vSensorTask(void *pvParameters) {
CarContext* ctx = (CarContext*)pvParameters;
for(;;) {
// 读取超声波距离
uint16_t distance = UltrasonicSensor_Read(ctx->ultrasonic);
ctx->obstacleDistance = distance;
// 检测障碍物
if(distance < 30 && ctx->obstacleState == OBSTACLE_STATE_CLEAR) {
CarEvent event = {EV_OBSTACLE_DETECTED, NULL};
xQueueSend(ctx->eventQueue, &event, 0);
} else if(distance >= 30 && ctx->obstacleState != OBSTACLE_STATE_CLEAR) {
CarEvent event = {EV_OBSTACLE_CLEARED, NULL};
xQueueSend(ctx->eventQueue, &event, 0);
}
// 读取电池电量
uint8_t battery = BatteryMonitor_GetLevel(ctx->battery);
ctx->batteryLevel = battery;
if(battery < 20) {
CarEvent event = {EV_BATTERY_LOW, NULL};
xQueueSend(ctx->eventQueue, &event, 0);
}
vTaskDelay(pdMS_TO_TICKS(100)); // 100ms采样周期
}
}
// ================== 蓝牙通信任务 ==================
void vBluetoothTask(void *pvParameters) {
CarContext* ctx = (CarContext*)pvParameters;
uint8_t rxData;
for(;;) {
if(Bluetooth_Receive(ctx->bluetooth, &rxData, 1, pdMS_TO_TICKS(100)) == pdTRUE) {
CarEvent event;
switch(rxData) {
case 'F': event.type = EV_CMD_FORWARD; break;
case 'B': event.type = EV_CMD_BACKWARD; break;
case 'L': event.type = EV_CMD_LEFT; break;
case 'R': event.type = EV_CMD_RIGHT; break;
case 'S': event.type = EV_CMD_STOP; break;
case 'P': event.type = EV_CMD_PAUSE; break;
case 'C': event.type = EV_CMD_RESUME; break;
case 'A': event.type = EV_CMD_START; break;
default: continue;
}
xQueueSend(ctx->eventQueue, &event, portMAX_DELAY);
}
}
}
// ================== 主控制任务 ================
void vMainControlTask(void *pvParameters) {
CarContext ctx;
CarStateMachine_Init(&ctx);
CarEvent event;
for(;;) {
// 等待事件
if(xQueueReceive(ctx.eventQueue, &event, pdMS_TO_TICKS(50)) pdTRUE) {
// 处理事件
CarStateMachine_HandleEvent(&ctx, &event);
}
// 周期性任务(如看门狗喂狗、状态上报等)
// ...
}
}
// ================== 主函数 ==================
int main(void) {
// 硬件初始化
HAL_Init();
SystemClock_Config();
// 创建任务
xTaskCreate(vMainControlTask, "MainCtrl", 512, NULL, 3, NULL);
xTaskCreate(vSensorTask, "Sensor", 256, NULL, 2, NULL);
xTaskCreate(vBluetoothTask, "Bluetooth", 256, NULL, 2, NULL);
// 启动调度器
vTaskStartScheduler();
// 不应该到达这里
for(;;);
}
状态机设计要点
- 层次化设计:主状态机管理高层状态,子状态机管理具体行为
- 事件驱动:所有状态转换通过事件触发,解耦状态逻辑
- 错误处理:独立的错误状态,确保系统安全
- 模块化:每个功能模块独立,便于维护和扩展
设计模式的选择与实践建议
五种模式的对比
| 模式 | 复杂度 | 内存占用 | 性能 | 可维护性 | 适用场景 |
|---|---|---|---|---|---|
| 单事件接收器 | 低 | 低 | 高 | 一般 | 简单状态机 |
| 多事件接收器 | 中 | 低 | 高 | 好 | 中等复杂度 |
| 状态表 | 中 | 中 | 高 | 很好 | 状态转换规则明确 |
| 状态模式 | 高 | 高 | 中 | 很好 | 复杂状态机 |
| 分解与状态 | 很高 | 高 | 中 | 极好 | 并发状态管理 |
选择建议
1. 简单系统(状态数 < 5)
推荐:单事件接收器模式
// 简单的LED控制状态机
typedef enum { LED_OFF, LED_ON, LED_BLINKING } LedState;
void LedStateMachine_HandleEvent(LedState* state, Event e) {
switch(e.type) {
case EV_BUTTON_PRESS:
switch(*state) {
case LED_OFF: *state = LED_ON; break;
case LED_ON: *state = LED_BLINKING; break;
case LED_BLINKING: *state = LED_OFF; break;
}
break;
}
}
2. 中等复杂度系统(5 < 状态数 < 15)
推荐:多事件接收器模式或状态表模式
// 使用状态表模式
typedef struct {
State currentState;
const TransitionTable* table;
} StateMachine;
void StateMachine_HandleEvent(StateMachine* sm, Event e) {
Transition t = sm->table[sm->currentState][e.type];
if(t.guard == NULL || t.guard()) {
if(t.exitAction) t.exitAction();
if(t.transitionAction) t.transitionAction();
sm->currentState = t.nextState;
if(t.entryAction) t.entryAction();
}
}
3. 复杂系统(状态数 > 15,有并发状态)
推荐:状态模式或分解与状态模式
// 使用状态模式
Context ctx;
Context_Init(&ctx);
// 事件处理委托给当前状态
ctx.currentState->handleEvent(&ctx, event);
实践建议
1. 从小开始,逐步重构
不要一开始就设计完美的状态机。先实现基本功能,然后根据需要重构。
// 第一阶段:简单的switch-case
void handleEvent(Event e) {
switch(state) {
case STATE_A: /* ... */ break;
case STATE_B: /* ... */ break;
}
}
// 第二阶段:提取函数
void handleStateA(Event e) { /* ... */ }
void handleStateB(Event e) { /* ... */ }
// 第三阶段:使用状态表
const TransitionTable table = {
// ...
};
2. 使用代码生成工具
对于复杂状态机,可以使用代码生成工具从状态图生成代码。
推荐工具:
- QM(QP框架的建模工具)
- Yakindu Statechart Tools
- MATLAB Stateflow
3. 添加调试支持
#ifdef DEBUG_STATE_MACHINE
#define SM_LOG(fmt, ...) printf("[SM] " fmt "\n", ##__VA_ARGS__)
#else
#define SM_LOG(fmt, ...)
#endif
void StateMachine_Transition(StateMachine* sm, State newState) {
SM_LOG("Transition: %s -> %s",
StateToString(sm->currentState),
StateToString(newState));
// ...
}
4. 单元测试
void test_StateMachine(void) {
StateMachine sm;
StateMachine_Init(&sm);
// 测试初始状态
TEST_ASSERT_EQUAL(STATE_IDLE, sm.currentState);
// 测试状态转换
StateMachine_HandleEvent(&sm, EV_START);
TEST_ASSERT_EQUAL(STATE_RUNNING, sm.currentState);
// 测试错误恢复
StateMachine_HandleEvent(&sm, EV_ERROR);
TEST_ASSERT_EQUAL(STATE_ERROR, sm.currentState);
StateMachine_HandleEvent(&sm, EV_RESET);
TEST_ASSERT_EQUAL(STATE_IDLE, sm.currentState);
}
总结与展望
核心要点回顾
-
状态机是嵌入式开发的核心模式:无论是简单控制还是复杂协议,状态机都能提供清晰的解决方案。
-
C语言可以实现面向对象状态机:通过结构体和函数指针,可以在资源受限的嵌入式平台上实现高内聚、低耦合的设计。
-
FIFO是通信的基础组件:环形缓冲区提供了高效的异步数据处理能力,是串口通信、DMA传输的必备组件。
-
FreeRTOS与状态机天然契合:任务状态、事件队列、信号量等机制为状态机实现提供了强大支持。
-
设计模式需要因地制宜:根据系统复杂度、资源限制、团队能力选择合适的状态机实现方式。
常见问题解答
Q1:状态机一定要用面向对象实现吗?
A:不一定。简单系统使用面向过程的switch-case即可。面向对象更适合复杂系统,可以提高可维护性。
Q2:如何处理状态机中的错误?
A:建议设计独立的错误状态,或者使用子状态机管理错误处理。错误恢复后应能回到正常状态流。
Q3:状态机性能如何优化?
A:
- 使用状态表模式,O(1)查表
- 减少动态内存分配
- 使用位域压缩状态变量
- 避免在状态机中使用浮点运算
Q4:如何调试复杂状态机?
A:
- 添加状态转换日志
- 使用状态图可视化工具
- 实现状态查询接口
- 添加断言检查非法状态
未来发展趋势
-
模型驱动开发(MDD):通过图形化建模工具自动生成状态机代码,提高开发效率。
-
形式化验证:使用数学方法证明状态机的正确性,确保系统安全。
-
AI辅助设计:利用机器学习优化状态机设计,自动发现最优状态划分。
-
云原生嵌入式:状态机设计与云端协同,实现远程监控和动态更新。
学习资源推荐
书籍
- 《嵌入式编程设计模式》(Bruce Powel Douglass)
- 《状态机:理论与实践》
- 《C语言嵌入式系统开发》
开源项目
- QP框架:专业的嵌入式状态机框架
- FreeRTOS:实时操作系统,包含状态机示例
- Arduino StateMachine:适合初学者的状态机库
在线资源
- 国内技术博客:CSDN、知乎、博客园
- 官方文档:FreeRTOS、STM32、ESP32
- 视频教程:B站嵌入式开发频道
附录:完整代码示例
附录A:轻量级状态机框架
// lightweight_sm.h
#ifndef LIGHTWEIGHT_SM_H
#define LIGHTWEIGHT_SM_H
#include <stdint.h>
typedef struct State State;
typedef struct Event Event;
typedef struct StateMachine StateMachine;
typedef void (*StateHandler)(StateMachine* sm, const Event* e);
typedef void (*Action)(StateMachine* sm);
struct State {
const char* name;
StateHandler handler;
Action entry;
Action exit;
};
struct Event {
uint32_t type;
void* data;
};
struct StateMachine {
const State* current;
const State* previous;
void* userData;
};
void SM_Init(StateMachine* sm, const State* initial, void* userData);
void SM_Dispatch(StateMachine* sm, const Event* e);
void SM_Transition(StateMachine* sm, const State* target);
const char* SM_GetStateName(const StateMachine* sm);
#endif
// lightweight_sm.c
#include "lightweight_sm.h"
void SM_Init(StateMachine* sm, const State* initial, void* userData) {
sm->current = initial;
sm->previous = NULL;
sm->userData = userData;
if(initial && initial->entry) {
initial->entry(sm);
}
}
void SM_Dispatch(StateMachine* sm, const Event* e) {
if(sm->current && sm->current->handler) {
sm->current->handler(sm, e);
}
}
void SM_Transition(StateMachine* sm, const State* target) {
if(target == sm->current) return;
// 退出当前状态
if(sm->current && sm->current->exit) {
sm->current->exit(sm);
}
sm->previous = sm->current;
sm->current = target;
// 进入新状态
if(target && target->entry) {
target->entry(sm);
}
}
const char* SM_GetStateName(const StateMachine* sm) {
return sm->current ? sm->current->name : "NULL";
}
附录B:线程安全FIFO实现
// thread_safe_fifo.h
#ifndef THREAD_SAFE_FIFO_H
#define THREAD_SAFE_FIFO_H
#include <stdint.h>
#include <stdbool.h>
typedef struct {
uint8_t* buffer;
volatile uint16_t head;
volatile uint16_t tail;
uint16_t size;
uint16_t count;
} ThreadSafeFIFO;
void TSFIFO_Init(ThreadSafeFIFO* fifo, uint8_t* buffer, uint16_t size);
bool TSFIFO_Write(ThreadSafeFIFO* fifo, uint8_t data);
bool TSFIFO_Read(ThreadSafeFIFO* fifo, uint8_t* data);
bool TSFIFO_IsEmpty(const ThreadSafeFIFO* fifo);
bool TSFIFO_IsFull(const ThreadSafeFIFO* fifo);
uint16_t TSFIFO_Count(const ThreadSafeFIFO* fifo);
// 中断安全版本
bool TSFIFO_WriteISR(ThreadSafeFIFO* fifo, uint8_t data);
bool TSFIFO_ReadISR(ThreadSafeFIFO* fifo, uint8_t* data);
#endif
// thread_safe_fifo.c
#include "thread_safe_fifo.h"
// 平台相关的临界区保护
#ifndef CRITICAL_SECTION_ENTER
#define CRITICAL_SECTION_ENTER() __disable_irq()
#endif
#ifndef CRITICAL_SECTION_EXIT
#define CRITICAL_SECTION_EXIT() __enable_irq()
#endif
void TSFIFO_Init(ThreadSafeFIFO* fifo, uint8_t* buffer, uint16_t size) {
fifo->buffer = buffer;
fifo->head = 0;
fifo->tail = 0;
fifo->size = size;
fifo->count = 0;
}
bool TSFIFO_Write(ThreadSafeFIFO* fifo, uint8_t data) {
CRITICAL_SECTION_ENTER();
bool result = false;
if(fifo->count < fifo->size) {
fifo->buffer[fifo->head] = data;
fifo->head = (fifo->head + 1) % fifo->size;
fifo->count++;
result = true;
}
CRITICAL_SECTION_EXIT();
return result;
}
bool TSFIFO_Read(ThreadSafeFIFO* fifo, uint8_t* data) {
CRITICAL_SECTION_ENTER();
bool result = false;
if(fifo->count > 0) {
*data = fifo->buffer[fifo->tail];
fifo->tail = (fifo->tail + 1) % fifo->size;
fifo->count--;
result = true;
}
CRITICAL_SECTION_EXIT();
return result;
}
bool TSFIFO_IsEmpty(const ThreadSafeFIFO* fifo) {
return fifo->count == 0;
}
bool TSFIFO_IsFull(const ThreadSafeFIFO* fifo) {
return fifo->count >= fifo->size;
}
uint16_t TSFIFO_Count(const ThreadSafeFIFO* fifo) {
return fifo->count;
}
// 中断安全版本(假设中断中已禁用中断)
bool TSFIFO_WriteISR(ThreadSafeFIFO* fifo, uint8_t data) {
if(fifo->count >= fifo->size) {
return false;
}
fifo->buffer[fifo->head] = data;
fifo->head = (fifo->head + 1) % fifo->size;
fifo->count++;
return true;
}
bool TSFIFO_ReadISR(ThreadSafeFIFO* fifo, uint8_t* data) {
if(fifo->count == 0) {
return false;
}
*data = fifo->buffer[fifo->tail];
fifo->tail = (fifo->tail + 1) % fifo->size;
fifo->count--;
return true;
}
本文完
本文基于国内主流技术社区的实践经验和开源项目整理而成,旨在为嵌入式开发者提供一份详实、实用的状态机设计指南。如有错误或建议,欢迎指正。