2.3. CEvent模块化框架
2.3.1. 简介
对于模块化编程来说,如何实现各模块间的解耦一直是一个比较令人头疼的问题,特别是对于嵌入式编程,由于控制逻辑复杂,并且对程序体积有控制,经常容易写出各独立模块之间相互调用的问题。 由此,CEvent模块化框架,通过模仿Android系统中的广播机制,提供了一种非常简单的模块间解耦实现。
2.3.2. 设计理念
cevent借鉴的是Android系统的广播机制,一方面,各模块在工作的时候,都会有多个具体的事件点,在高耦合的编程中,可能会在这些地方调用其他模块的功能,比如说,在通信模块接收到指令的时候,需要闪烁一下指示灯 使用cevent,我们可以在这些地方抛出一个事件,当前模块不需要关心在这各地方需要执行哪些其他模块的逻辑,由其他模块,或者用户定义一个事件监听,当具体的事件发生时,执行相应的动作
CEvent 框架的核心设计理念包括:
合理利用底层编译原理:利用宏定义,在编译期完成生产者和消费者的绑定
事件驱动:模块间通过事件进行通信,减少直接依赖。
松耦合:模块之间的接口通过事件定义,降低模块间的耦合度。
可扩展性:易于添加新模块或修改现有模块,而不影响其他部分。
2.3.3. 框架结构
CEvent 框架主要包含以下组件:
事件定义(Event Definition)
模块结构(Module Structure)
事件分发器(Event Dispatcher)
模块管理器(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. 优势
模块化设计:每个功能均能依赖cevent进行模块化设计,不在互相调用。
高内聚:使每个模块专注于特定功能并且能独立编译,提高代码的可读性和可维护性。
可扩展性:易于添加新模块或修改现有模块,而不影响其他部分。
段技巧:利用段操作技巧,解耦方法相当于直接调用,效率极高。
2.3.7. 结论
CEvent 模块化框架为嵌入式系统提供了一种灵活、可扩展的软件架构方案。通过采用这种框架,开发者可以更好地组织代码,提高系统的可维护性和可扩展性。 在实际应用中,应根据具体项目需求和硬件限制来调整和优化框架的实现。