0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

浅析嵌入式编程上下文切换及完美解耦的一种方法

FPGA之家 来源:CSDN技术社区 作者:NevermindZZT 2021-11-05 14:43 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

上下文快速切换 - 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为例

#defineCPOST_GET_TICK()HAL_GetTick()

2、配置处理进程

在mainloop调用cpostProcess函数

intmain(void)
{
...
while(1)
{
cpostProcess();
}
return0;
}

3、抛出任务

在中断等需要进行上下文切换的地方调用cpsot接口,使其在mainloop中运行

cpost(intHandler);

原理解析

cpost的原理其实很简单,其代码量也十分少,总共加起来就只有几十行代码,cpost维护了一个而全局的数组

CpostHandlercposhHandlers[CPOST_MAX_HANDLER_SIZE]={0};

其中,数组的每一个元素表示包含了需要执行的函数和参数,当调用cpost接口时,被post的函数和参数会被保存在这个数组中,然后mainloop中运行的cpostProcess函数会遍历这个数组,当满足条件时,执行对应的函数,从而达到上下文切换的目的

voidcpostProcess(void)
{
for(size_ti=0;i< CPOST_MAX_HANDLER_SIZE; i++)
    {
        if(cposhHandlers[i].handler)
{
if(cposhHandlers[i].time==0||CPOST_GET_TICK()>=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、定义初始化事件

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

#defineEVENT_INIT_STAGE10
#defineEVENT_INIT_STAGE21

2、初始化cevent,抛出事件

在main函数中初始化cevent,并抛出初始化事件

intmain(void)
{
...
ceventInit();

ceventPost(EVENT_INIT_STAGE1);
ceventPost(EVENT_INIT_STAGE2);
...
return0;
}

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的做法,也会增加耦合

intmain(void)
{
...

while(1)
{
//写在mainloop中的模块逻辑
shellTask(&shell);
LedProcess();
...
}
return0;
}

通过使用cevent,也可以很方便的消除这种耦合

1、定义mainloop事件

定义mainloop事件的值

#defineEVENT_MAIN_LOOP3

2、在mainloop中抛出事件

去掉mainloop中对其他模块的调用,改为排除mainloop事件

intmain(void)
{
...

while(1)
{
ceventPost(EVENT_MAIN_LOOP);
}
return0;
}

3、在各模块中注册事件监听

分别在各个模块中,注册对mainloop事件的监听

CEVENT_EXPORT(EVENT_MAIN_LOOP,shellTask,(void*)(&shell));
CEVENT_EXPORT(EVENT_MAIN_LOOP,LedProcess);

结语

cevent是一个非常小的模块,本身代码及其简单,但是,通过模仿广播机制,让cevent可以发挥很强大的功能,通过,还可以结合cpost,实现延迟事件等功能。

编辑:jq
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式
    +关注

    关注

    5186

    文章

    20143

    浏览量

    328668
  • 编程
    +关注

    关注

    90

    文章

    3707

    浏览量

    96736
  • 代码
    +关注

    关注

    30

    文章

    4940

    浏览量

    73116
  • 解耦
    +关注

    关注

    0

    文章

    43

    浏览量

    12167

原文标题:嵌入式编程上下文切换及完美解耦的一种方法

文章出处:【微信号:zhuyandz,微信公众号:FPGA之家】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    大语言模型如何处理上下文窗口中的输入

    本博客介绍了五个基本概念,阐述了大语言模型如何处理上下文窗口中的输入。通过明确的例子和实践中获得的见解,本文介绍了多个与上下文窗口有关的基本概念,如词元化、序列长度和注意力等。
    的头像 发表于 12-03 13:48 150次阅读
    大语言模型如何处理<b class='flag-5'>上下文</b>窗口中的输入

    执行脱离上下文的威胁分析与风险评估

    作为WITTENSTEIN high integrity system(WHIS)公司的核心产品,SAFERTOS专为安全关键型嵌入式系统设计,使其成为确保联网车辆环境可靠防护的理想选择。在本文
    的头像 发表于 11-28 09:11 245次阅读
    执行脱离<b class='flag-5'>上下文</b>的威胁分析与风险评估

    嵌入式与FPGA的区别

    器件的基础上进步发展的产物,是作为专用集成电路(ASIC)领域中的一种半定制电路而出现的,既.决了定制电路的不足,又克服了原有可编程器件门电路数有限的缺点。 FPGA是硬件电路设计
    发表于 11-20 07:12

    嵌入式和FPGA的区别

    ,芯片内部的门电路连接在出厂时就已固定,无法更改,它们的功能是通过软件编程也就是嵌入式软件来实现的。 FPGA(现场可编程门阵列) 则是一种编程
    发表于 11-19 06:55

    请问riscv中断还需要软件保存上下文和恢复吗?

    以下是我拷贝的文档里的说明,这个中断处理还需要软件来写上下文保存和恢复,在使用ARM核的单片机都不需要考虑这些的,使用过的小伙伴能解答吗? 3.8. 进出中断的上下文保存和恢复 RISC-V架构
    发表于 10-20 09:56

    入行嵌入式应该怎么准备?

    知识: 、C/C++编程C/C++是嵌入式系统开发中最常用的编程语言。熟练掌握C/C++语言将使你能够理解和编写底层驱动程序、操作系统以及与硬件交互的代码。对于
    发表于 08-06 10:34

    嵌入式和单片机,是同个东西吗?

    (Microcontroller,简称MCU),是一种集成了处理器、内存和外围设备的单芯片微型计算机。它通常作为嵌入式系统的核心控制单元,具有集成度高、成本低、易于编程和控制等优点。单片机的主要功能是处理输入信号,并依据预设
    发表于 07-09 10:20

    鸿蒙NEXT-API19获取上下文,在class中和ability中获取上下文,API迁移示例-解决无法在EntryAbility中无法使用最新版

    摘要:随着鸿蒙系统API升级至16版本(modelVersion5.1.1),多项API已废弃。获取上下文需使用UIContext,具体方法包括:在组件中使用getUIContext(),在类中使
    的头像 发表于 07-01 10:57 575次阅读
    鸿蒙NEXT-API19获取<b class='flag-5'>上下文</b>,在class中和ability中获取<b class='flag-5'>上下文</b>,API迁移示例-解决无法在EntryAbility中无法使用最新版

    Linux嵌入式和单片机嵌入式的区别?

    Linux嵌入式与单片机嵌入式在多个方面存在显著的区别,以下是详细的比较和归纳: 、基本概念 1. Linux嵌入式: 定义:将Linux操作系统运行在
    发表于 06-20 09:46

    嵌入式力矩电机的设计方法研究

    摘 要:介绍一种用于机床直驱部件的嵌入式力短电机的设计和电磁结构优化方法。为了使嵌入式力矩电机满足机床内部结构紧凑的要求,必须具有较高的磁密。通过计算分析,得到不同长径比、不同磁极对数
    发表于 06-11 15:08

    嵌入式编程设计模式

    嵌入式编程设计模式,介绍如何使用设计模式为嵌入式系统创建高效且优化的C语言设计。 纯分享贴,有需要可以直接下载附件获取完整资料! (如果内容有帮助可以关注、点赞、评论支持下哦~
    发表于 04-15 14:47

    如何成为嵌入式软件工程师?

    如何成为嵌入式软件工程师? 01明确岗位的角色与定位 嵌入式软件工程师主要负责开发运行在特定硬件平台上的软件,这些软件通常与硬件紧密集成,以实现特定的功能。 不仅需要精通编程语言
    发表于 04-15 14:37

    S32K在AUTOSAR中使用CAT1 ISR,是否需要执行上下文切换

    如果我们在 AUTOSAR 中使用 CAT1 ISR,是否需要执行上下文切换?另外,是否需要返回指令才能跳回到作系统?您有没有带有 CAT1 ISR 的 S32K3x4 微控制器的示例?
    发表于 03-27 07:34

    DeepSeek推出NSA机制,加速长上下文训练与推理

    的特性,专为超快速的长上下文训练和推理而设计。 NSA通过针对现代硬件的优化设计,显著加快了推理速度,并大幅度降低了预训练成本,同时保持了卓越的性能表现。这机制在确保效率的同时,并未牺牲模型的准确性或功能。 在广泛的基准测试、涉及长
    的头像 发表于 02-19 14:01 912次阅读

    新手怎么学嵌入式?

    基本的概念。嵌入式系统是一种将计算机技术嵌入到特定设备中的系统,它通常具有特定的功能和有限的资源。你需要学习些计算机基础知识,如数据结构、操作系统、计算机组成原理等。这些知识将帮助你
    发表于 12-12 10:51