cpost的使用及原理解析

嵌入式编程的需求千变万化,要做到系统稳定,又要代码可复用,就要做到高内聚低耦合。  
前言 我们通常认为,在中断中,不能执行耗时的操作,否则会影响系统的稳定性,尤其对于嵌入式编程。对于带操作系统的程序而言,可以通过操作系统的调度,将中断处理分成两个部分,耗时的操作可以放到线程中去执行,但是对于没有操作系统的情况,又应该如何处理呢
比较常见的,我们可能会定义一些全局变量,作为flag,然后在mainloop中不停的判断这些flag,再在中断中修改这些flag,最后在mainloop中执行具体的逻辑,但是这样,无疑会增加耦合,增加程序维护成本。
cpost cpost正是应用在这种情况下的一个简单但又十分方便的工具,它可以特别方便的进行上下文的切换,减少模块耦合。
cpost链接:
https://github.com/nevermindzzt/cpost cpost借鉴的android的handler机制,通过在mainloop中跑一个任务,然后在其他地方,可以是中断,也可以是模块逻辑中,直接抛出需要执行的函数,使其脱离调用处的上下文,运行在mainloop中。cpost还支持延迟处理,可以指定函数在抛出后多久执行
使用 cpost的使用十分简单,这里以使用在嵌入式无操作系统中为例,主要用作中断延迟处理的情况
1、配置系统tick 配置cpost.h中的宏cpost_get_tick(),配置成获取系统tick,以stm32 hal为例
#define     cpost_get_tick()            hal_gettick() 2、配置处理进程 在mainloop调用cpostprocess函数int main(void){    ...    while (1)    {        cpostprocess();    }    return 0;} 3、抛出任务 在中断等需要进行上下文切换的地方调用cpsot接口,使其在mainloop中运行
cpost(inthandler); 原理解析 cpost的原理其实很简单,其代码量也十分少,总共加起来就只有几十行代码,cpost维护了一个而全局的数组
cposthandler cposhhandlers[cpost_max_handler_size] = {0}; 其中,数组的每一个元素表示包含了需要执行的函数和参数,当调用cpost接口时,被post的函数和参数会被保存在这个数组中,然后mainloop中运行的cpostprocess函数会遍历这个数组,当满足条件时,执行对应的函数,从而达到上下文切换的目的
void cpostprocess(void){    for (size_t i = 0; i = cposhhandlers[i].time)            {                cposhhandlers[i].handler(cposhhandlers[i].param);                cposhhandlers[i].handler = null;            }        }    }} 其实,cpost的方式,和一开始提到的使用全局的flag进行上下文切换的方法很像,只不过,cpost通过一个数组的维护和直接post函数的方式,省去了维护flag的成本,也不需要将需要执行的函数耦合到mianloop中,从而变得简单易用。
完美解耦 - cevent应用 对于模块化编程来说,如何实现各模块间的解耦一直是一个比较令人头疼的问题,特别是对于嵌入式编程,由于控制逻辑复杂,并且对程序体积有控制,经常容易写出各独立模块之间相互调用的问题。由此,cpost中的cevent组件,通过模仿android系统中的广播机制,提供了一种非常简单的模块间解耦实现。
原理 cevent借鉴的是android系统的广播机制,一方面,各模块在工作的时候,都会有多个具体的事件点,在高耦合的编程中,可能会在这些地方调用其他模块的功能,比如说,在通信模块接收到指令的时候,需要闪烁一下指示灯。
使用cevent,我们可以在这些地方抛出一个事件,当前模块不需要关心在这各地方需要执行哪些其他模块的逻辑,由其他模块,或者用户定义一个事件监听,当具体的事件发生时,执行相应的动作。
使用 cevent使用注册的方式监听事件,会依赖于编译环境,目前支持keil,iar,和gcc,对于gcc,需要修改链接文件(.ld),在只读数据区添加:
_cevent_start = .;keep (*(cevent))_cevent_end = .; 1、初始化cevent 系统初始化时,调用ceventinit
ceventinit(); 2、注册cevent事件监听 在c文件中,调用cevent_export导出事件监听
cevent_export(0, handler, (void *)param); 3、发送cevent事件 在事件发生的地方,调用ceventpost抛出事件
ceventpost(0); 使用cevent解耦模块初始化 嵌入式编程中,我们习惯会在程序启动的时候,调用各个模块的初始化函数,其实这也是一种耦合,会造成main函数中出现很长的初始化代码,借助cevent,我们可以对初始化进行优化解耦。
1、定义初始化事件 定义初始化事件的值,对于初始化,有些模块可能会依赖于其他模块的初始化,会有一个先后顺序要求,所以这里我们可以把初始化分成两个阶段,定义两个事件,当然,如果有更复杂的要求,可以再多分几个阶段,只需要多定义几个事件就行
#define     event_init_stage1       0#define     event_init_stage2       1 2、初始化cevent,抛出事件 在main函数中初始化cevent,并抛出初始化事件
int main(void){    ...    ceventinit();    ceventpost(event_init_stage1);    ceventpost(event_init_stage2);    ...    return 0;} 3、注册事件监听 对所有需要初始化的函数注册事件监听,这里我以对letter-shell注册事件监听为例,分为两个部分,初始化串口和初始化shell。
在serial模块中,将串口初始化注册到初始化第一阶段,cevent支持将不大于7个的参数直接传递到注册的监听函数中,下面的注册方式,相当于在event_init_stage1事件发生的地方,也就是main函数中对应的位置,调用serialinit(&debugserial)
cevent_export(event_init_stage1, serialinit, (void *)(&debugserial)); 然后再shell模块中,将shell初始化函数注册到初始化第二阶段。
cevent_export(event_init_stage1, shellinit); 使用cevent解耦mainloop 再无操作系统的嵌入式编程中,我们如果同时希望运行多个模块的逻辑,通常是在mainloop中循环调用,这种将函数写入mainloop的做法,也会增加耦合
int main(void){    ...    while (1)    {        // 写在mainloop中的模块逻辑        shelltask(&shell);        ledprocess();        ...    }    return 0;} 通过使用cevent,也可以很方便的消除这种耦合
1、定义mainloop事件 定义mainloop事件的值
#define     event_main_loop         3 2、在mainloop中抛出事件 去掉mainloop中对其他模块的调用,改为排除mainloop事件
int main(void){    ...    while (1)    {        ceventpost(event_main_loop);    }    return 0;} 3、在各模块中注册事件监听 分别在各个模块中,注册对mainloop事件的监听
cevent_export(event_main_loop, shelltask, (void *)(&shell)); cevent_export(event_main_loop, ledprocess); 结语 cevent是一个非常小的模块,本身代码及其简单,但是,通过模仿广播机制,让cevent可以发挥很强大的功能,通过,还可以结合cpost,实现延迟事件等功能。


手机短信点播内容的广播方式及其原理
ALVA Systems将精彩亮相第十届中国移动全球合作伙伴大会
水冷式冷热冲击试验箱常见故障的解决办法
手机玩游戏为何选骁龙?Elite Gaming整体实现体验加分
桥式起重机和门式起重机区别
cpost的使用及原理解析
vivo比较受欢迎的手机有哪些
高分辨率时间-数字转换器具备CMOS输入和紧凑封装优势
测量误差的来源有哪些
硫化物固态电解质与氧化物正极的热稳定性
一加9RT正式发布:搭载ColorOS 12系统亮点十足
魅族魅蓝note6发布会正在直播!魅蓝Note6重新出发后的首款产品,给人感觉真的不一样了!
利用PLC-Recorde软件对三菱各型PLC进行录波的操作方法
三星Galaxy Note 9:全视曲面屏, 6GB+128GB版售价约7800元
工控机遇见的问题比较多,常见的问题有哪些
有了石墨烯,OLED时代还远吗?
【世说芯品】DS323x系列实时时钟(RTC)芯片性能比较
电平触发器、脉冲触发器、边缘触发器有什么不一样?
新型量子点红外上转换器件,可应用于生物医学成像领域
Apple Watch平台认知与产品设计