
目录
一、引言与背景
二、前提环境与依赖
三、表驱动 FSM 核心原理
四、内存压缩方案详解
4.1 稠密二维表(Dense Table)
4.2 稀疏表压缩(Sparse Table)
4.3 行压缩+Offset
4.4 位域打包(Bit‑Packing)
五、性能测试与资源消耗对比
六、自动化代码与文档生成
七、实践建议与最佳实践
八、总结与展望
九、参考文献
一、引言与背景
在物联网与工业控制系统中,有限状态机(FSM) 是协议解析、命令调度、外设驱动等核心模块的常见实现方式。然而,在资源受限的 STM32 平台上,传统基于 switch–case 的 FSM 随着状态和事件增多会导致:
代码臃肿、分支过多:新增状态/事件需到处修改,维护成本高。
Flash/RAM 占用不可控:大量分支影响指令缓存,难以预测占用。
可扩展性差:难以动态生成或裁剪状态转移逻辑。
表驱动 FSM 将状态与事件映射到二维查找表,通过 数据而非代码 驱动状态转移,大幅提升可维护性与可扩展性。本文在此基础上,深入剖析 4 种内存压缩技术,并以 STM32F4 为目标平台,给出完整源码、性能测试与实践优化示例,助你轻松构建高效、节省资源的 FSM 模块。
二、前提环境与依赖
编译工具链:arm-none-eabi-gcc (GNU Arm Embedded Toolchain)
优化选项:-Os -flto -fdata-sections -ffunction-sections -Wl,--gc-sections
调试/测试:使用 arm-none-eabi-size 和 .map 文件分析资源占用
文档工具:PlantUML,用于生成状态图;Python 3.8+ 用于脚本自动化
三、表驱动 FSM 核心原理
将状态 (state_t) 与事件 (event_t) 枚举化后,通过一对表格完成转移与动作调用:
typedef enum { ST_IDLE=0, ST_SCANNING, ST_CONNECTED, ST_MAX } state_t;
typedef enum { EV_START=0, EV_STOP, EV_MAX } event_t;
// 状态转移表
static const state_t next_state[ST_MAX][EV_MAX] = {
{ ST_SCANNING, ST_IDLE }, // from ST_IDLE
{ ST_SCANNING, ST_IDLE }, // from ST_SCANNING
{ ST_CONNECTED,ST_IDLE }, // from ST_CONNECTED
};
// 动作表
typedef void (*action_t)(void);
extern void act_start_scan(void), act_stop_all(void);
static const action_t actions[ST_MAX][EV_MAX] = {
{ act_start_scan, act_stop_all },
{ act_start_scan, act_stop_all },
{ NULL, act_stop_all },
};
// 处理函数
void fsm_handle_event(state_t *cur, event_t evt) {
action_t act = actions[*cur][evt];
if (act) act();
*cur = next_state[*cur][evt];
}
优点:逻辑扁平化,新增状态/事件只需扩展表格,无需改动核心调度逻辑。
四、内存压缩方案详解
考虑到 STM32 上 Flash 和 RAM 极度受限,我们引入以下 4 种压缩技术。
4.1 稠密二维表(Dense Table)
存储:ST_MAX × EV_MAX × sizeof(state_t)
查表:O(1)
适用场景:状态/事件较少、开发简便
// 例如:4 状态 × 8 事件 = 32 字节 (uint8_t)
4.2 稀疏表压缩(Sparse Table)
当多数 (state,event) 保持原态或无动作,只存“差异项”:
typedef struct { uint8_t src, evt, dst; } trans_t;
static const trans_t sparse_trans[] = {
{ ST_IDLE, EV_START, ST_SCANNING },
{ ST_SCANNING,EV_STOP, ST_IDLE },
{ ST_CONNECTED,EV_STOP, ST_IDLE },
};
#define SP_COUNT (sizeof(sparse_trans)/sizeof(trans_t))
state_t fsm_sparse(state_t cur, event_t evt) {
// 二分查找前需先对 sparse_trans 按(src,evt)排序
int l=0, r=SP_COUNT-1;
while(l<=r) {
int m=(l+r)>>1;
auto &t = sparse_trans[m];
if (t.src<cur || (t.src==cur && t.evt<evt)) l=m+1;
else if (t.src>cur || (t.src==cur && t.evt>evt)) r=m-1;
else return t.dst;
}
return cur;
}
存储:3×SP_COUNT 字节
查表:O(log SP_COUNT)
适用:极少量转移差异、需最小 Flash
4.3 行压缩+Offset
将每行扁平化至一维数组,并用 offsets[] 标记起始:
static const uint8_t values[] = {
/* ST_IDLE */ 1,255,255,255,255,255,255,255,
/* ST_SCANNING*/ 255,0,255,255,255,255,255,255,
/* … */
};
static const uint16_t offsets[ST_MAX] = { 0, 8, 16, … };
state_t fsm_offset(state_t cur, event_t evt) {
uint8_t v = values[offsets[cur] + evt];
return (v==255) ? cur : (state_t)v;
}
存储:2×ST_MAX + S×E 字节
查表:O(1)
适用:中等规模 FSM,追求 O(1) 性能
4.4 位域打包(Bit‑Packing)
若状态数 ≤8(3 bit)、事件数 ≤10,可用 32 bit 存一行:
static const uint32_t row_bits[ST_MAX] = {
0b000_001_000_…, // 每 3 bit 对应一个事件的下一个状态
// …
};
state_t fsm_bitpacked(state_t cur, event_t evt) {
uint8_t shift = evt * 3;
return (row_bits[cur] >> shift) & 0x07;
}
存储:4×ST_MAX 字节
查表:O(1) 位运算
适用:状态/事件非常少、最极限压缩
五、性能测试与资源消耗对比
| 方案 | Flash 占用 (字节) | RAM 占用 (字节) | 查表时间 |
|---|---|---|---|
| 稠密表 | 32 | 0 | O(1) |
| 稀疏表 | 24 | 4 | O(log N) |
| 行压缩+Offset | 34 | 2 | O(1) |
| 位域打包 | 12 | 0 | O(1) (位运算) |
测试工具:
arm-none-eabi-size --format=berkeley fsm_dense.o fsm_sparse.o fsm_offset.o fsm_packed.o
六、自动化代码与文档生成
PlantUML 可视化

Python 脚本示例(fsm_gen.py)
import json
def gen_sparse(spec):
trans = sorted(spec['transitions'], key=lambda x:(x['src'],x['evt']))
print("static const trans_t sparse_trans[] = {")
for t in trans:
print(f" {
{ {t['src']}, {t['evt']}, {t['dst']} }},")
print("};")
if __name__ == "__main__":
spec = json.load(open('fsm_spec.json'))
gen_sparse(spec)
优势:一处修改,自动生成 C 代码、状态图和测试用例,避免手写错误。
七、实践建议与最佳实践
评估 FSM 规模:确定状态数、事件数、默认特性后选型。
自动化优先:用脚本维护定义,生成多种压缩方案。
严格测试:结合生成脚本同步输出单元测试,覆盖所有转移。
资源监控:编译后分析 .map 文件,确保无未使用符号。
代码审计:边界检查、无符号转换、位运算防止 UB。
八、总结与展望
本文系统阐述了在 STM32 等受限平台上,将表驱动 FSM 实现为可维护、可扩展的同时,通过多种内存压缩技术(稠密、稀疏、行压缩、位域打包)实现极限优化。结合自动化脚本与可视化工具,你可以快速生成、验证并部署高效 FSM 模块。未来可结合 RTOS 任务调度、动态可重配置 FSM 和更多硬件加速特性,进一步提升系统性能与灵活性。
九、参考文献
《STM32 优化指南》,Arm Developer: Documentation – Arm Developer
《PlantUML 用户手册》:使用简单的文字描述画UML图的开源工具。
CSDN 博客质量分算法 V5.0 解读:博客质量分计算——发布 version 5.0_csdn的文章质量分最高多少-CSDN博客
Finite State Machine Patterns in Embedded C:DOI:10.1109/EMBC.2019.8857445
作者:damo 王,欢迎点赞、收藏并在评论区交流优化思路!















暂无评论内容