2.3. CEvent模块化框架

2.3.1. 简介

对于模块化编程来说,如何实现各模块间的解耦一直是一个比较令人头疼的问题,特别是对于嵌入式编程,由于控制逻辑复杂,并且对程序体积有控制,经常容易写出各独立模块之间相互调用的问题。 由此,CEvent模块化框架,通过模仿Android系统中的广播机制,提供了一种非常简单的模块间解耦实现。

2.3.2. 设计理念

cevent借鉴的是Android系统的广播机制,一方面,各模块在工作的时候,都会有多个具体的事件点,在高耦合的编程中,可能会在这些地方调用其他模块的功能,比如说,在通信模块接收到指令的时候,需要闪烁一下指示灯 使用cevent,我们可以在这些地方抛出一个事件,当前模块不需要关心在这各地方需要执行哪些其他模块的逻辑,由其他模块,或者用户定义一个事件监听,当具体的事件发生时,执行相应的动作

CEvent 框架的核心设计理念包括:

  1. 合理利用底层编译原理:利用宏定义,在编译期完成生产者和消费者的绑定

  2. 事件驱动:模块间通过事件进行通信,减少直接依赖。

  3. 松耦合:模块之间的接口通过事件定义,降低模块间的耦合度。

  4. 可扩展性:易于添加新模块或修改现有模块,而不影响其他部分。

2.3.3. 框架结构

CEvent 框架主要包含以下组件:

  1. 事件定义(Event Definition)

  2. 模块结构(Module Structure)

  3. 事件分发器(Event Dispatcher)

  4. 模块管理器(Module Manager)

2.3.4. 应用举例1

嵌入式编程中,我们习惯会在程序启动的时候,调用各个模块的初始化函数, 其实这也是一种耦合,会造成main函数中出现很长的初始化代码,借助cevent,我们可以对初始化进行优化解耦

1. 定义初始化事件

定义初始化事件的值,对于初始化,有些模块可能会依赖于其他模块的初始化,会有一个先后顺序要求,所以这里我们可以把初始化分成两个阶段,定义两个事件,当然,如果有更复杂的要求,可以再多分几个阶段,只需要多定义几个事件就行

事件是模块间通信的基本单位,通常定义为一个宏类型:

#define PRINT_SYSTEM_INITIALIZATION  0          //打印系统初始化
#define ON_CHIP_AND_OFF_CHIP_INITIALIZATION  1  //片内外设初始化
#define INITIALIZATION_OF_ONBOARD_PERIPHERALS 2 //板上外设初始化
#define SOFTWARE_INITIALIZATION 3  //软件系统初始化

2. 注册打印串口驱动初始化事件

第一个函数为初始化函数,重点关注后面的 CEVENT_EXPORT

en_result_t UART_PrintfInit(M4_USART_TypeDef *UARTx,
                        uint32_t u32Baudrate,
                        void (*PortInit)(void))
{
    en_result_t enRet = ErrorInvalidParameter;

    if (IS_VALID_UART(UARTx) && (0ul != u32Baudrate) && (NULL != PortInit))
    {
        /* Initialize port */
        PortInit();

        /* Enable clock */
        UART_EnableClk(UARTx);

        /* Initialize USART */
        UARTx->CR1_f.ML = 0ul;      /* LSB */
        UARTx->CR1_f.MS = 0ul;      /* UART mode */
        UARTx->CR1_f.OVER8 = 1ul;   /* 8bit sampling mode */
        UARTx->CR1_f.M = 0ul;       /* 8 bit data length */
        UARTx->CR1_f.PCE = 0ul;     /* no parity bit */

        /* Set baudrate */
        if (Ok != SetUartBaudrate(UARTx, u32Baudrate))
        {
            enRet = Error;
        }
        else
        {
            UARTx->CR2 = 0ul;       /* 1 stop bit, single uart mode */
            UARTx->CR3 = 0ul;       /* CTS disable, Smart Card mode disable */
            UARTx->CR1_f.TE = 1ul;  /* TX enable */

            m_PrintfDevice = UARTx;
            m_u32PrintfTimeout = (SystemCoreClock / u32Baudrate);
        }
    }

    return enRet;
}

CEVENT_EXPORT(PRINT_SYSTEM_INITIALIZATION, UART_PrintfInit,
            BSP_PRINTF_DEVICE, BSP_PRINTF_BAUDRATE, BSP_PRINTF_PortInit);

3. 在main函数中解耦调用事件

如下代码, 在 ceventPost 调用的地方会自动将打印串口初始化的函数从 段内 取出来进行调用,main函数不在依赖特定的接口

int main(void)
 {
     ceventInit();

     ceventPost(PRINT_SYSTEM_INITIALIZATION);
     ...
     for (;;)
     {
         ...
     }

     return 0;
 }

2.3.5. 应用举例2

使用 cevent 解耦 mainloop

在无操作系统的嵌入式编程中,我们如果同时希望运行多个模块的逻辑,通常是在mainloop中循环调用,这种将函数写入mainloop的做法,也会增加耦合。

在线程中的调用任务接口也是一样的道理。通过 cevent 就能解决这种痛点。

1. 定义mainloop事件

#define     EVENT_MAIN_LOOP         4

2. 在mainloop中调用所有 CEVENT_EXPORT 注册为 EVENT_MAIN_LOOP 的事件

int main(void)
 {
     ...

     while (1)
     {
         ceventPost(EVENT_MAIN_LOOP);
     }
     return 0;
 }

2.3.6. 优势

  1. 模块化设计:每个功能均能依赖cevent进行模块化设计,不在互相调用。

  2. 高内聚:使每个模块专注于特定功能并且能独立编译,提高代码的可读性和可维护性。

  3. 可扩展性:易于添加新模块或修改现有模块,而不影响其他部分。

  4. 段技巧:利用段操作技巧,解耦方法相当于直接调用,效率极高。

2.3.7. 结论

CEvent 模块化框架为嵌入式系统提供了一种灵活、可扩展的软件架构方案。通过采用这种框架,开发者可以更好地组织代码,提高系统的可维护性和可扩展性。 在实际应用中,应根据具体项目需求和硬件限制来调整和优化框架的实现。