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

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

3天内不再提示

Cortex-M裸机环境下临界区保护的三种实现

Dp1040 来源:痞子衡嵌入式 2023-07-07 09:06 次阅读

今天给大家分享的是Cortex-M裸机环境下,临界区保护的三种实现。

嵌入式玩过 RTOS 的小伙伴,想必都对 OS_ENTER_CRITICAL()、OS_EXIT_CRITICAL() 这个功能代码特别眼熟,在 RTOS 里常常会有多任务(进程)处理,有些情况下一些特殊操作(比如 XIP 下 Flash 擦写、低功耗模式切换)不能被随意打断,或者一些共享数据区不能被无序访问(A 任务正在读,B 任务却要写),这时候就要用到临界区保护策略了。

所谓临界区保护策略,简单说就是系统中硬件临界资源或者软件临界资源,多个任务必须互斥地对它们进行访问。RTOS 环境下有现成的临界区保护接口函数,而裸机系统里其实也有这种需求。在裸机系统里,临界区保护主要就是跟系统全局中断控制有关。

在之前的文章《嵌入式MCU中通用的三重中断控制设计》中,介绍的第三重也是最顶层的中断控制是系统全局中断控制,今天就从这个系统全局中断控制使用入手给大家介绍三种临界区保护做法:

一、临界区保护测试场景

关于临界区保护的测试场景无非两种。第一种场景是受保护的多个任务间并无关联,也不会互相嵌套,如下面的代码所示,task1 和 task2 是按序被保护的,因此 enter_critical() 和 exit_critical() 这两个临界区保护函数总是严格地成对执行:

voidcritical_section_test(void)
{
//进入临界区
enter_critical();
//做受保护的任务1
do_task1();
//退出临界区
exit_critical();

//进入临界区
enter_critical();
//做受保护的任务2,与任务1无关联
do_task2();
//退出临界区
exit_critical();
}
第二种场景就是多个任务间可能有关联,会存在嵌套情况,如下面的代码所示,task2 是 task1 的一个子任务,这种情况下,你会发现实际上是先执行两次 enter_critical(),然后再执行两次 exit_critical()。

需要注意的是,task1 里面的子任务 task3 虽然没有像子任务 task2 那样被主动加一层保护,但由于主任务 task1 整体是受保护的,因此子任务 task3 也应该是受保护的。

voiddo_task1(void)
{
//进入临界区
enter_critical();
//做受保护的任务2,是任务1中的子任务
do_task2();
//退出临界区
exit_critical();

//做任务3
do_task3();
}

voidcritical_section_test(void)
{
//进入临界区
enter_critical();
//做受保护的任务1
do_task1();
//退出临界区
exit_critical();
}

二、临界区保护三种实现

上面的临界区保护测试场景很清楚了,现在到 enter_critical()、exit_critical() 这对临界区保护函数的实现环节了:

2.1 入门做法

首先是非常入门的做法,直接就是对系统全局中断控制函数 __disable_irq()、__enable_irq() 的封装。回到上一节的测试场景里,这种实现可以很好地应对非嵌套型任务的保护,但对于互相嵌套的任务保护就失效了。上一节测试代码里,task3 应该也要受到保护的,但实际上并没有被保护,因为紧接着 task2 后面的 exit_critical() 直接就打开了系统全局中断。

voidenter_critical(void)
{
//关闭系统全局中断
__disable_irq();
}

voidexit_critical(void)
{
//打开系统全局中断
__enable_irq();
}

2.2 改进做法

针对入门做法,可不可以改进呢?当然可以,我们只需要加一个全局变量 s_lockObject 来实时记录当前已进入的临界区保护的次数,即如下代码所示。每调用一次 enter_critical() 都会直接关闭系统全局中断(保证临界区一定是受保护的),并记录次数,而调用 exit_critical() 时仅当当前次数是 1 时(即当前不是临界区保护嵌套情况),才会打开系统全局中断,否则只是抵消一次进入临界区次数而已。改进后的实现显然可以保护上一节测试代码里的 task3 了。

staticuint32_ts_lockObject;

voidinit_critical(void)
{
__disable_irq();
//清零计数器
s_lockObject=0;
__enable_irq();
}

voidenter_critical(void)
{
//关闭系统全局中断
__disable_irq();
//计数器加1
++s_lockObject;
}

voidexit_critical(void)
{
if(s_lockObject<= 1)
    {
        // 仅当计数器不大于 1 时,才打开系统全局中断,并清零计数器
        s_lockObject = 0;
        __enable_irq();
    }
    else
{
//当计数器大于1时,直接计数器减1即可
--s_lockObject;
}
}

2.3 终极做法

上面的改进做法虽然解决了临界区任务嵌套保护的问题,但是增加了一个全局变量和一个初始化函数,实现不够优雅,并且嵌入式系统里全局变量极容易被篡改,存在一定风险,有没有更好的实现呢?

当然有,这要借助 Cortex-M 处理器内核的特殊屏蔽寄存器 PRIMASK,下面是 PRIMASK 寄存器位定义(取自 ARMv7-M 手册),其仅有最低位 PM 是有效的,当 PRIMASK[PM] 为 1 时,系统全局中断是关闭的(将执行优先级提高到 0x0/0x80);当 PRIMASK[PM] 为 0 时,系统全局中断是打开的(对执行优先级无影响)。

398b30f0-1c5e-11ee-962d-dac502259ad0.png

看到这,你应该明白了 __disable_irq()、__enable_irq() 功能其实就是操作 PRIMASK 寄存器实现的。既然 PRIMASK 寄存器控制也保存了系统全局中断的开关状态,我们可以通过获取 PRIMASK 值来替代上面改进做法里的全局变量 s_lockObject 的功能,代码实现如下:

uint32_tenter_critical(void)
{
//保存当前PRIMASK值
uint32_tregPrimask=__get_PRIMASK();
//关闭系统全局中断(其实就是将PRIMASK设为1)
__disable_irq();

returnregPrimask;
}

voidexit_critical(uint32_tprimask)
{
//恢复PRIMASK
__set_PRIMASK(primask);
}
因为 enter_critical()、exit_critical() 函数原型有所变化,因此使用上也要相应改变下:
voidcritical_section_test(void)
{
//进入临界区
uint32_tprimask=enter_critical();
//做受保护的任务
do_task();
//退出临界区
exit_critical(primask);

//...
}

附录、PRIMASK寄存器设置函数在各 IDE 下实现

//////////////////////////////////////////////////////
//IAR环境下实现(见cmsis_iccarm.h文件)
#define__set_PRIMASK(VALUE)(__arm_wsr("PRIMASK",(VALUE)))
#define__get_PRIMASK()(__arm_rsr("PRIMASK"))

//////////////////////////////////////////////////////
//Keil环境下实现(见cmsis_armclang.h文件)
__STATIC_FORCEINLINEvoid__set_PRIMASK(uint32_tpriMask)
{
__ASMvolatile("MSR primask,%0"::"r"(priMask):"memory");
}

__STATIC_FORCEINLINEuint32_t__get_PRIMASK(void)
{
uint32_tresult;

__ASMvolatile("MRS%0,primask":"=r"(result));
return(result);
}
至此,Cortex-M裸机环境下临界区保护的三种实现便介绍完毕了,感谢大家阅读。





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

    关注

    21

    文章

    809

    浏览量

    119358
  • 中断控制器
    +关注

    关注

    0

    文章

    59

    浏览量

    9429
  • Cortex-M
    +关注

    关注

    2

    文章

    227

    浏览量

    29704
  • 裸机
    +关注

    关注

    0

    文章

    39

    浏览量

    6329

原文标题:单片机要这样保护临界区

文章出处:【微信号:玩点嵌入式,微信公众号:玩点嵌入式】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    MDK Eclipse 环境搭建 cortex-M Cortex-A 多核混合调试

    MDK Eclipse 环境搭建 cortex-M Cortex-A 多核混合调试
    发表于 03-15 16:11

    cortex-m各种微架构的区别是什么?

    cortex-m单片机在arm产品中的位置是哪里?cortex-m 单片机的类别有哪些?cortex-m各种微架构的区别是什么?
    发表于 11-04 06:00

    ARM Cortex-M内核的相关资料推荐

      大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是ARM Cortex-M功能模块,不过侧重点是款安全特性处理器。  ARM Cortex-M处理器家族发展至今(2020),已有
    发表于 12-27 07:21

    分享的是Cortex-M中断向量表原理及其重定向方法

    大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是Cortex-M中断向量表原理及其重定向方法。接着前文《嵌入式Cortex-M裸机环境
    发表于 01-25 06:47

    ARM Cortex-M 开发实战指南入门篇(

    Cortex-M下载程序及调试Cortex-M三种启动模式对应的存储介质均是芯片内置的,它们是:1)用户闪存 = 芯片内置的Flash。2)SRAM = 芯片内置的RAM,就是内存
    发表于 04-20 17:35

    介绍Cortex-A和Cortex-M的TrustZone之间的差异

    相信关注安全和嵌入式的开发者对TrustZone都不陌生,最近看到有网友在问Cortex-A和Cortex-M的TrustZone之间的差异,我们来简单介绍。Arm在2003年的Armv6开始
    发表于 07-13 14:45

    Arm Cortex-M处理器—Cortex-M85介绍

    Arm发布了新一代的Cortex-M处理器,Cortex-M85。简单粗暴的打个比方:Cortex-M85 ≈ Cortex-M7TrustZoneHelium(
    发表于 07-15 14:59

    关于Cortex-M 调试应用的介绍

    Cortex-M 调试应用
    的头像 发表于 07-10 00:56 2577次阅读

    米尔科技Cortex-M Prototyping System +介绍

    经济实惠的主板,作为ARM®Versatile™Express系列开发板的一部分。他们提供两FPGA的选择,用于原型设计基于Cortex-M的设计和一系列不同的调试选项。它提供了一系列实用的外设,包括
    的头像 发表于 11-14 10:45 1823次阅读
    米尔科技<b class='flag-5'>Cortex-M</b> Prototyping System +介绍

    Cortex-M内核的MPU内存保护单元

    讲讲Cortex-M内核的MPU内存保护单元
    的头像 发表于 03-04 11:17 3592次阅读
    <b class='flag-5'>Cortex-M</b>内核的MPU内存<b class='flag-5'>保护</b>单元

    Cortex-M裸机环境临界保护三种实现

    今天给大家分享的是Cortex-M裸机环境临界保护
    的头像 发表于 09-08 09:23 3119次阅读
    <b class='flag-5'>Cortex-M</b><b class='flag-5'>裸机</b><b class='flag-5'>环境</b><b class='flag-5'>下</b><b class='flag-5'>临界</b><b class='flag-5'>区</b><b class='flag-5'>保护</b>的<b class='flag-5'>三种</b><b class='flag-5'>实现</b>

    Cortex-M中断向量表原理及其重定向方法~

    大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是Cortex-M中断向量表原理及其重定向方法。接着前文《嵌入式Cortex-M裸机环境
    发表于 12-01 12:21 9次下载
    <b class='flag-5'>Cortex-M</b>中断向量表原理及其重定向方法~

    痞子衡嵌入式:嵌入式Cortex-M中断向量表原理及其重定向方法

      大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是Cortex-M中断向量表原理及其重定向方法。  接着前文 《嵌入式Cortex-M裸机环境
    发表于 12-01 12:36 8次下载
    痞子衡嵌入式:嵌入式<b class='flag-5'>Cortex-M</b>中断向量表原理及其重定向方法

    分享一Cortex-M裸机环境临界保护的几种实现方法

    RTOS有临界裸机依然有临界。今天给大家分享一Cor
    发表于 06-13 09:08 675次阅读
    分享一<b class='flag-5'>下</b><b class='flag-5'>Cortex-M</b><b class='flag-5'>裸机</b><b class='flag-5'>环境</b><b class='flag-5'>下</b><b class='flag-5'>临界</b><b class='flag-5'>区</b><b class='flag-5'>保护</b>的几种<b class='flag-5'>实现</b>方法

    Cortex-M位带操作的原理

    Cortex-M位带操作的原理
    的头像 发表于 10-24 15:27 811次阅读
    <b class='flag-5'>Cortex-M</b>位带操作的原理