event1

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);  // 投递完就走
}