最近项目用到FreeRTOS,在实际调试中发现我自己的一段代码本来好用的(在无RTOS的情况下),但是当我在带RTOS的情况下把代码放到一个单独的任务中运行时我发现本来好用的代码莫名其妙的出现问题,有一定的概率会失败,考虑到应该是内核发生了调度导致代码中时序比较严格的地方被打断因此会出现时好时不好的现象,因此我对时序严格的地方调用了taskENTER_CRITICAL();和taskEXIT_CRITICAL();进行任务切换保护和中断但是结果还是一样,由此一来这个问题困扰了好久,我就开始怀疑freeRTOS源码的问题,仔细看了源码中关于进入临界和退出临界的函数。

进入临界区的宏函数是:taskENTER_CRITICAL();而又一层宏是portENTER_CRITICAL()最后才是函数vPortEnterCritical()。

void vPortEnterCritical( void )
{
portDISABLE_INTERRUPTS();
uxCriticalNesting++; /* This is not the interrupt safe version of the enter critical function so
assert() if it is being called from an interrupt context. Only API
functions that end in "FromISR" can be used in an interrupt. Only assert if
the critical nesting count is 1 to protect against recursive calls if the
assert function also uses a critical section. */
if( uxCriticalNesting == 1 )
{
configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
}
}

注意函数中不仅仅将临界区深度加1同时还调用了宏函数:portDISABLE_INTERRUPTS();这个宏的实体函数是vPortRaiseBASEPRI()

static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY; __asm
{
/* Set BASEPRI to the max syscall priority to effect a critical
section. */
msr basepri, ulNewBASEPRI
dsb
isb
}
}

这里参考M3权威指南中关于异常掩蔽寄存器,PRIMASK, FAULTMASK 以及 BASEPRI的功能描述。

PRIMASK 用于除能在 NMI 和硬 fault 之外的所有异常,它有效地把当前优先级改为 0(可
编程优先级中的最高优先级)。该寄存器可以通过 MRS 和 MSR 以下例方式访问:

关中断
MOV R0, #1
MSR PRIMASK, R0 开中断
MOV R0, #0
MSR PRIMASK, R0

FAULTMASK则更加绝对他可以关闭除NMI以外的所有中断,使用其专用的指令访问方式及功能如下:

FAULTMASK=1,关异常
CPSID F ; FAULTMASK=0,开异常
CPSIE F ;

最后也就是本次将说明关于FreeRTOS系统中临界区用到的BASEPRI特殊功能寄存器,这个寄存器在内核指南中用了一个“细腻”的描述,意思这个寄存器可以更加细分的控制中断屏蔽的问题。这个寄存器的作用就是如果你向其中写入 A则中断优先级数大于等于A的所有中断将被除能。同时如果你给其中写0则表示不屏蔽任何优先级的中断。回到上面说的FreeRTOS的临界区问题,在进入临界区函数内调用了portFORCE_INLINE 函数这个函数内部是用汇编写的但是内容比较浅显易懂其实就是将configMAX_SYSCALL_INTERRUPT_PRIORITY 写入BASEPRI因此这将屏蔽优先级不高于configMAX_SYSCALL_INTERRUPT_PRIORITY (中断优先级数大于等于configMAX_SYSCALL_INTERRUPT_PRIORITY )的中断,本次我遇到的问题比较LOW啊,我在配置文件中如下配置

#define configMAX_SYSCALL_INTERRUPT_PRIORITY     5

在实际工程中我将中断配置为中断优先级分组“组4”也就是全部用于抢占优先级共可以分16(0~~~15)级优先级,配置文件中我配置为5,但是在使用时发现,调用进入临界函数后调度器虽然不会再进行调度,但是中断还是会执行,这我就奇怪了。

继续看内核指南,M3内核可以支持256级中断,且BASEPRI寄存器最多可以到9位,具体几位和芯片设计的优先级表示位数相关,这就是此次问题的原因了,STM32F1只使用了8位优先级中的高四位,低位未使用,因此实际效果如下(灰色表示未使用写会被忽略,读回永远是0)

当我给BASEPRI寄存器中写入configMAX_SYSCALL_INTERRUPT_PRIORITY = 5  时实际写入的效果是 0000 0101因此此时对于STM32芯片来说,实际写入完成后的寄存器实际值是0x00,因为对于低四位功能未实现当写入时会被忽略,读取会一直是0,所以会导致出现FreeRTOS临界区无法屏蔽中断的问题。同样对于如果不使用优先级组4时,同理将抢占优先级和子优先级合并后和BASEPRI寄存器中的之比较如果大于则会被屏蔽。这里注意不管使用多少位都是MSB对齐的,查阅内核指南的一段话:通过让优先级以 MSB 对齐,可以简化程序的跨器件移植。比如,如果一个程序早先在支持 4 位优先级的器件上运行,在移植到只支持 3 位优先级的器件后,其功能不受影响。但若是对齐到 LSB,则会使 MSB 丢失,导致数值大于 7 的低优先级一下子升高了,甚至会反转小于等于 7 的高优先级。如,8 号优先级因为损失了 MSB,现在反而变成 0 号了!这一点实际点也就是如图所示的意义:

完事了。。。细节真的很重要啊!!!

记一次FreeRTOS错误配置导致无法进入临界区的更多相关文章

  1. jenkins用户权限配置错误,导致登录时提示:没有Overall/read权限

    jenkins用户权限配置错误,导致登录时提示:没有Overall/read权限 由于初次接触jenkins,于是在搭建好jenkins以后,想要对用户进行管理,于是乎开始在系统管理->conf ...

  2. node name配置错误,导致grid日志在报警

    [root@aipdb ContentsXML]# cat inventory.xml <?xml version="1.0" standalone="yes&qu ...

  3. ubuntu18.04错误配置变量环境导致无法进入系统

    1.问题描述 错误配置环境变量(直接在/etc/profile文件末尾添加了export xxx),关机后一直在登录界面循环无法进入系统. ###环境变量的添加是在原有变量之后以冒号(:)分隔加入,并 ...

  4. 记pytorch版faster rcnn配置运行中的一些坑

    记pytorch版faster rcnn配置运行中的一些坑 项目地址 https://github.com/jwyang/faster-rcnn.pytorch 一般安装配置参考README.md文件 ...

  5. 记一次因证书问题导致请求失败问题SSLHandshakeException

    记一次因证书问题导致请求失败问题SSLHandshakeException 转载请注明出处:https://www.cnblogs.com/funnyzpc/p/10989813.html 最近接一外 ...

  6. Nginx常见的错误配置

    Blog:博客园 个人 翻译自Common Nginx misconfigurations that leave your web server open to attack Nginx是当前主流的W ...

  7. Oracle登录时提示错误,导致用户无法登录

    Oracle登录时提示错误,导致用户无法登录,错误如下 ------------------------------------------------------------------------ ...

  8. 【初级坑跳跳跳】[NULLException] findViewById() id 引用错误,导致空指针

    在学习Intent页面切换,几个页面切换,导致view id 写错,写成另一个xml里的id去了,导致空指针异常 setContentView(R.layout.activity_second); B ...

  9. FreeRTOS中断优先级配置(重要)

    FreeRTOS中断优先级配置(重要) 本章节为大家讲解FreeRTOS中断优先级配置,此章节非常重要,初学者经常在这里犯迷糊.对于初学者来说,本章节务必要整明白.12.1 NVIC基础知识12.2  ...

随机推荐

  1. uni-app开发经验分享三: Vuex实现登录和用户信息留存

    在做用户登录的过程中,其实最重要的是登录成功后的数据要怎么储存,储存到哪里,这里我分享一个利用vuex来实现用户登录和用户数据留存的方法 vuex代码如下: //引入vue和vuex import V ...

  2. centos&linux

    who am i 查看是哪一个用户 init 0关机 ifconfig用于配置网络或显示当前网络接口的状态 eth0是网卡的名字 第一行:flags后面的up指的是网卡处于运行状态,running连接 ...

  3. JVM有哪些垃圾回收器

    JVM 的垃圾回收器 目录 JVM 的垃圾回收器 经典垃圾收集器 Serial 收集器 ParNew 收集器 Parallel Scavenge 收集器 Serial Old 收集器 Parallel ...

  4. HA工作机制及namenode向QJM写数据流程

    HA工作机制 (配置HA高可用传送门:https://www.cnblogs.com/zhqin/p/11904317.html) HA:高可用(7*24小时不中断服务) 主要的HA是针对集群的mas ...

  5. Profile Guided Optimization Link Time Optimization

    https://github.com/python/cpython Profile Guided Optimization PGO takes advantage of recent versions ...

  6. SpringCloud配置刷新机制的简单分析[nacos为例子]

    SpringCloud Nacos 本文主要分为SpringCloud Nacos的设计思路 简单分析一下触发刷新事件后发生的过程以及一些踩坑经验 org.springframework.cloud. ...

  7. 我的刷题单(8/37)(dalao珂来享受切题的快感

    P2324 [SCOI2005]骑士精神 CF724B Batch Sort CF460C Present CF482A Diverse Permutation CF425A Sereja and S ...

  8. 【题解】CF952F 2 + 2 != 4

    题目传送门 首先这道题没有翻译,这是很奇怪的,经过了(bai)查(du)字(fan)典(yi)之后,你会发现,什么用都没有-- 楼下的 dalao 们给的解释非常的模糊(果然还是我太弱了),于是我自己 ...

  9. 反弹SHELL介绍及原理

    如果我们需要到服务器上执行 Shell 命令,但是因为防火墙等原因,无法由客户端主动发起连接的情况,就可以使用反弹 Shell 来满足登陆和操作的需求. 什么是反弹Shell 正常情况下,我们登陆服务 ...

  10. Hive 使用总结

    1 带分区列的表更改列类型 常见的一个场景是Hive里面一个带分区的表,原来是int类型的字段,后来发现数据超过了int的最大值,要改成bigint.或者是bigint要改string或decimal ...