电子产业一站式赋能平台

PCB联盟网

搜索
查看: 392|回复: 0
收起左侧

分享一套嵌入式编程上下文切换模块(附源码说明)

[复制链接]

344

主题

344

帖子

3148

积分

四级会员

Rank: 4

积分
3148
发表于 2024-9-25 11:45:00 | 显示全部楼层 |阅读模式
关注+星标公众,不错过精彩内容
?
?
?来源 | 网络?
?
有操作系统的情况下,可以通过系统调度实现上下文切换,但没有操作系统情况下,很多人都是通过全局变量(标志位)flag来进行判断处理,但这种方式存在诸多弊端。下面给大家分享一个简单的用于嵌入式编程上下文切换的模块cpost,开源地址:
  • https://github.com/NevermindZZT/cpost关于cpostcpost是一个c语言编写的,它可以特别方便的进行上下文的切换,减少模块耦合。cpost包含cpost和cevent两个工具,cpost用于c语言的上下文切换,cevent用于程序模块之间的解耦。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 if (cposhHandlers.handler)
            {
                if (cposhHandlers.time == 0 || CPOST_GET_TICK() >= cposhHandlers.time)
                {
                    cposhHandlers.handler(cposhHandlers.param);
                    cposhHandlers.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系统初始化时,调用ceventInitceventInit();
    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,实现延迟事件等功能。声明:本文素材来源网络,版权归原作者所有。如涉及作品版权问题,请与我联系删除。
    ------------ END ------------

    5l3vdfmdje16406079205.gif

    5l3vdfmdje16406079205.gif


    ●专栏《嵌入式工具
    ●专栏《嵌入式开发》
    ●专栏《Keil教程》
    ●嵌入式专栏精选教程

    关注公众号回复“加群”按规则加入技术交流群,回复“1024”查看更多内容。
    点击“阅读原文”查看更多分享。
  • 回复

    使用道具 举报

    发表回复

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则


    联系客服 关注微信 下载APP 返回顶部 返回列表