在嵌入式开发中,事件调度、消息分发是非常核心的开发思想。无论是按键检测、串口消息、传感器数据上报,还是外设中断处理,本质都是「产生事件 - 匹配处理函数 - 执行逻辑」的流程。
很多新手写代码习惯用大量 if-else、switch-case 堆砌事件逻辑,代码臃肿、扩展性极差,新增事件就要改主逻辑,极易出错。
一、框架整体设计思路
这套事件调度框架的核心逻辑只有三步:
- 定义事件:枚举所有系统需要的事件(event0~event9)
- 绑定处理函数:每个事件对应一个独立的处理函数
- 统一调度分发:通过总调度函数,根据事件类型自动匹配、执行对应逻辑
彻底告别冗长的 switch-case,做到高内聚、低耦合,新增事件只需要加枚举、写处理函数、注册数组即可,无需改动核心调度逻辑。
二、完整源码展示
以下是可直接编译运行的完整嵌入式事件调度代码,适配单片机、RTOS、裸机开发场景:
#include <stdio.h>
#include <stdint.h>
#define maxHandler 10
typedef enum {
event0 = 0,
event1,
event2,
event3,
event4,
event5,
event6,
event7,
event8,
event9
} Event;
// 事件结构体:存储事件所有信息
typedef struct Event_struct {
Event event_t; // 事件类型
uint32_t timestamp; // 事件时间戳
uint16_t datalen; // 事件数据长度
void *data; // 事件私有数据指针
};
// 事件处理函数指针类型
typedef void (*Handler)(Event_struct *Es);
// ------------------- 各个事件处理函数 -------------------
static void event0_handler(Event_struct *Es) {
printf("--------------[event0]--------------\n");
printf(" key=%d\n", Es->event_t);
printf(" timestamp=%d\n", Es->timestamp);
printf(" datalen=%d\n", Es->datalen);
}
static void event1_handler(Event_struct *Es) {
printf("--------------[event1]--------------\n");
printf(" key=%d\n", Es->event_t);
printf(" timestamp=%d\n", Es->timestamp);
printf(" datalen=%d\n", Es->datalen);
}
static void event2_handler(Event_struct *Es) {
printf("--------------[event2]--------------\n");
printf(" key=%d\n", Es->event_t);
printf(" timestamp=%d\n", Es->timestamp);
printf(" datalen=%d\n", Es->datalen);
}
static void event3_handler(Event_struct *Es) {
printf("--------------[event3]--------------\n");
printf(" key=%d\n", Es->event_t);
printf(" timestamp=%d\n", Es->timestamp);
printf(" datalen=%d\n", Es->datalen);
}
static void event4_handler(Event_struct *Es) {
printf("--------------[event4]--------------\n");
printf(" key=%d\n", Es->event_t);
printf(" timestamp=%d\n", Es->timestamp);
printf(" datalen=%d\n", Es->datalen);
}
static void event5_handler(Event_struct *Es) {
printf("--------------[event5]--------------\n");
printf(" key=%d\n", Es->event_t);
printf(" timestamp=%d\n", Es->timestamp);
printf(" datalen=%d\n", Es->datalen);
}
static void event6_handler(Event_struct *Es) {
printf("--------------[event6]--------------\n");
printf(" key=%d\n", Es->event_t);
printf(" timestamp=%d\n", Es->timestamp);
printf(" datalen=%d\n", Es->datalen);
}
static void event7_handler(Event_struct *Es) {
printf("--------------[event7]--------------\n");
printf(" key=%d\n", Es->event_t);
printf(" timestamp=%d\n", Es->timestamp);
printf(" datalen=%d\n", Es->datalen);
}
static void event8_handler(Event_struct *Es) {
printf("--------------[event8]--------------\n");
printf(" key=%d\n", Es->event_t);
printf(" timestamp=%d\n", Es->timestamp);
printf(" datalen=%d\n", Es->datalen);
}
static void event9_handler(Event_struct *Es) {
printf("--------------[event9]--------------\n");
printf(" key=%d\n", Es->event_t);
printf(" timestamp=%d\n", Es->timestamp);
printf(" datalen=%d\n", Es->datalen);
}
// 事件-处理函数 映射表(核心数组)
Handler Handler_function_arr[maxHandler] = {
[event0] = event0_handler,
[event1] = event1_handler,
[event2] = event2_handler,
[event3] = event3_handler,
[event4] = event4_handler,
[event5] = event5_handler,
[event6] = event6_handler,
[event7] = event7_handler,
[event8] = event8_handler,
[event9] = event9_handler
};
// 统一事件调度分发函数
void dispatch(Event_struct *Es) {
// 边界校验,防止非法事件
if (Es == NULL || Es->event_t < 0 || Es->event_t > maxHandler) {
printf("[dispatch] 无效事件类型 %d\n", Es->event_t);
}
// 根据事件类型,直接调用对应处理函数
Handler_function_arr[Es->event_t](Es);
}
int main() {
Event_struct es;
es.datalen = 12;
es.event_t = event0;
es.timestamp = 1324759;
dispatch(&es);
return 0;
}
三、核心代码逐块解析
1. 事件枚举定义
通过枚举统一管理所有事件,让事件类型标准化、可读性极强,杜绝魔法数字:
定义了 event0~event9 共10个事件,可根据项目需求自由增删。
2. 事件信息结构体
Event_struct 是事件的载体,存储一次事件的所有完整信息:
- event_t:事件类型(对应枚举)
- timestamp:事件触发时间戳,用于时序判断、日志排查
- datalen:自定义数据长度
- data:万能数据指针,可传递任意类型自定义数据(传感器值、报文数据等)
这是嵌入式事件框架的标准结构体设计,兼容性极强。
3. 函数指针封装
typedef void (*Handler)(Event_struct *Es)
定义事件处理函数的统一格式,所有事件处理函数必须遵循「入参为事件结构体、无返回值」的规范,保证调度统一。
4. 事件处理函数
每个事件独立对应一个 xxx_handler 函数,各司其职:
- 逻辑解耦:不同事件的业务逻辑完全隔离,互不干扰
- 方便维护:修改某一个事件逻辑,不会影响其他功能
- 便于调试:每个事件独立日志,问题定位精准
5. 核心:函数映射数组
这是整个框架的灵魂!
通过数组下标和事件枚举一一对应,实现事件ID → 处理函数的精准映射。
相比 switch-case 判断,这种方式:
- 执行效率更高(直接数组下标寻址,O(1)复杂度)
- 扩展性拉满,新增事件只需新增枚举、新增函数、注册数组
- 代码整洁,逻辑清晰
6. 统一调度函数 dispatch
所有事件的入口,承担分发和安全校验功能:
- 做非空判断、事件范围校验,防止非法参数导致程序崩溃
- 根据事件类型,自动调用对应的处理函数
业务层只需要「填充事件结构体 + 调用dispatch」,无需关心具体执行哪个函数。
四、代码运行效果
编译运行程序后,输出结果如下:
--------------[event0]--------------
key=0
timestamp=1324759
datalen=12
成功匹配 event0 事件,并打印事件所有参数。
五、框架核心优势(嵌入式开发刚需)
1. 彻底解耦,扩展性极强
传统写法:新增事件 → 修改switch主逻辑 → 容易改错、漏改。
本框架:新增事件 → 加枚举、写handler、注册数组 → 零侵入修改。
2. 执行效率更高
switch-case 是分支判断(O(n)),函数数组是直接下标寻址(O(1)),事件越多,优势越明显。
3. 安全性高
自带空指针、非法事件校验,有效避免裸机、RTOS中常见的指针崩溃、越界问题。
4. 适配多数嵌入式场景
可直接用于:按键事件、串口报文、CAN总线消息、传感器中断、定时器事件、APP状态交互等所有场景。
六、总结
事件调度是嵌入式高级编程的核心思想,从裸机开发到RTOS、Linux嵌入式开发,这种「枚举+结构体+函数指针数组」的分发模型无处不在。
摒弃臃肿的 if/switch 分支,用标准化的事件框架写代码,不仅代码更优雅,项目可维护性、稳定性都会大幅提升,也是面试高频考点!
Hits: 0






