
1)事件定义
首先得有一个统一的事件表示。不用搞多复杂,一个类型加一个数据就够了:
typedef struct {
uint16_t type; /* 事件类型 */
uint16_t param; /* 附带参数 */
} event_t;
事件类型用枚举来管理:
enum {
EVT_NONE = 0,
EVT_KEY_PRESS,
EVT_KEY_RELEASE,
EVT_UART_RX,
EVT_TIMER_TICK,
EVT_ADC_DONE,
// ...
};
2)事件队列
队列本质上就是一个环形缓冲区,中断里往里塞事件,主循环里取出来处理:
#define EVT_QUEUE_SIZE 32
static event_t evt_queue[EVT_QUEUE_SIZE];
static volatile uint8_t head = 0;
static volatile uint8_t tail = 0;
/* 中断中调用:投递事件 */
void event_post(uint16_t type, uint16_t param)
{
uint8_t next = (head + 1) % EVT_QUEUE_SIZE;
if (next != tail) { /* 队列没满 */
evt_queue[head].type = type;
evt_queue[head].param = param;
head = next;
}
}
/* 主循环中调用:取出事件 */
bool event_get(event_t *evt)
{
if (tail == head)
return false; /* 队列为空 */
*evt = evt_queue[tail];
tail = (tail + 1) % EVT_QUEUE_SIZE;
return true;
}
3)事件分发
取出事件之后,怎么交给对应的处理函数?最简单的做法是用一张函数指针表:
typedef void (*event_handler_t)(uint16_t param);
/* 处理函数注册表 */
static event_handler_t handler_table[EVT_MAX] = { NULL };
void event_register(uint16_t type, event_handler_t handler)
{
if (type < EVT_MAX)
handler_table[type] = handler;
}
void event_dispatch(event_t *evt)
{
if (evt->type < EVT_MAX && handler_table[evt->type]) {
handler_table[evt->type](evt->param);
}
}
把这三部分组合起来,主循环就变得非常清爽了:
int main(void)
{
system_init();
/* 注册各模块的事件处理函数 */
event_register(EVT_KEY_PRESS, on_key_press);
event_register(EVT_UART_RX, on_uart_receive);
event_register(EVT_TIMER_TICK, on_timer_tick);
event_register(EVT_ADC_DONE, on_adc_done);
event_t evt;
while (1) {
if (event_get(&evt)) {
event_dispatch(&evt);
} else {
__WFI(); /* 没事件就睡觉,省电 */
}
}
}
/* 中断里只投递事件 */
void USART1_IRQHandler(void)
{
uint8_t data = USART1->DR;
event_post(EVT_UART_RX, data); // 投递完就走
}