FreeRTOS中断浅析
@firestaradmin 2020年11月27日16:06:56
FreeRTOS中断浅析
一、STM32中断屏蔽的特殊寄存器
我们在 STM32 上移植 FreeRTOS 的时候需要重点关注 PRIMASK、FAULTMASK 和 BASEPRI 这三个寄存器,本节就来学习一下这三个寄存器。
1 、PRIMASK 和 和 FAULTMASK 寄存器
在许多应用中,需要暂时屏蔽所有的中断一执行一些对时序要求严格的任务,这个时候就可以使用 PRIMASK 寄存器,PRIMASK 用于禁止除 NMI 和 HardFalut 外的所有异常和中断,汇编编程的时候可以使用 CPS(修改处理器状态)指令修改 PRIMASK 寄存器的数值:
CPSIE I; //清除 PRIMASK(使能中断)
CPSID I; //设置 PRIMASK(禁止中断)
PRIMASK 寄存器还可以通过 MRS 和 MSR 指令访问,如下:
MOVS R0, #1
MSR PRIMASK, R0 ;//将 1 写入 PRIMASK 禁止所有中断
以及:
MOVS R0, #0
MSR PRIMASK, R0 ;//将 0 写入 PRIMASK 以使能中断
UCOS 中的临界区代码代码保护就是通过开关中断实现的(UCOSIII 也可以使用禁止任务调度的方法来实现临界区代码保护,这里不讨论这种情况),而开关中断就是直接操作 PRIMASK寄存器的,所以在 UCOS 中关闭中断的时候时关闭了除复位、NMI 和 HardFault 以外的所有中断!
FAULTMASK 比 PRIMASK 更狠,它可以连 HardFault 都屏蔽掉,使用方法和 PRIMASK 类似,FAULTMASK 会在退出时自动清零。汇编编程的时候可以利用 CPS 指令修改 FAULTMASK 的当前状态:
CPSIE F ;清除 FAULTMASK
CPSID F ;设置 FAULTMASK
还可以利用 MRS 和 MSR 指令访问 FAULTMASK 寄存器:
MOVS R0, #1
MSR FAULTMASK, R0 ;将 1 写入 FAULTMASK 禁止所有中断
以及:
MOVS R0, #0
MSR FAULTMASK, R0 ;将 0 写入 FAULTMASK 使能中断
2 、BASEPRI 寄存器
PRIMASK 和 FAULTMASK 寄存器太粗暴了,直接关闭除复位、NMI 和 HardFault 以外的其他所有中断,但是在有些场合需要对中断屏蔽进行更细腻的控制,比如只屏蔽优先级低于某一个阈值的中断。那么这个作为阈值的优先级值存储在哪里呢?在 BASEPRI 寄存器中,不过如果向 BASEPRI 写 0 的话就会停止屏蔽中断。比如,我们要屏蔽优先级不高于 0X60 (NVIC组4的时候)的中断,则可以使用如下汇编编程:
MOV R0, #0X60
MSR BASEPRI, R0
如果需要取消 BASEPRI 对中断的屏蔽,可以使用如下代码:
MOV R0, #0
MSR BASEPRI, R0
注意!FreeRTOS 的开关中断就是操作 BASEPRI 寄存器来实现的!它可以关闭低于某个阈值的中断,高于这个阈值的中断就不会被关闭!
二、FreeRTOS 中断配置宏
configPRIO_BITS
此宏用来设置 MCU 使用几位优先级,STM32 使用的是 4 位,因此此宏为 4!
configLIBRARY_LOWEST_INTERRUPT_PRIORITY
此宏是用来设置最低优先级,STM32 优先级使用了 4 位,而且 STM32 配置的使用组 4,也就是 4 位都是抢占优先级。因此优先级数就是 16 个,最低优先级那就是 15。所以此宏就是 15,注意!不同的 MCU 此值不同,具体是多少要看所使用的 MCU 的架构,这里只针对 STM32 讲解!
configKERNEL_INTERRUPT_PRIORITY
此宏用来设置内核中断优先级,此宏定义如下:
#define configKERNEL_INTERRUPT_PRIORITY
( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
宏 configKERNEL_INTERRUPT_PRIORITY 为 , 宏configLIBRARY_LOWEST_INTERRUPT_PRIORITY 左移 8-configPRIO_BITS 位,也就是左移 4位。为什么要左移 4 位呢?前面我们说了,STM32 使用了 4 位作为优先级,而这 4 位是高 4 位,因 此 要 左 移 4 位 才 是 真 正 的 优 先 级 。 当 然 了 也 可 以 不 用 移 位 , 直 接 将 宏configLIBRARY_LOWEST_INTERRUPT_PRIORITY 定义为 0XF0!不过这样看起来不直观。
宏configKERNEL_INTERRUPT_PRIORITY用来设置PendSV和滴答定时器的中断优先级,port.c 中有如下定义:
```c
#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) <<16UL )
#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) <<24UL )- 可 以 看 出 , portNVIC_PENDSV_PRI 和 portNVIC_SYSTICK_PRI 都 是 使 用 了 宏configKERNEL_INTERRUPT_PRIORITY , 为 什 么 宏 portNVIC_PENDSV_PRI 是 宏configKERNEL_INTERRUPT_PRIORITY 左移 16 位呢?宏 portNVIC_SYSTICK_PRI 也同样是左移 24 位。的,这样一次写入的是个 32 位的数据, SysTick 和 PendSV 的优先级寄存器分别对应这个 32位数据的最高 8 位和次高 8 位,不就是一个左移 16 位,一个左移 24 位了。 **可以看出在FreeRTOS中 PendSV 和 SysTick 的中断优先级都是最低的!** ##### configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 此宏用来设置 FreeRTOS 系统可管理的最大优先级,也就是我们在前面讲解BASEPRI 寄存器说的那个阈值优先级,这个大家可以自由设置,这里我设置为了 5。也就是高于 5 的优先级(优先级的数字大小 小于 5的优先级)不归 FreeRTOS 管理! ##### configMAX_SYSCALL_INTERRUPT_PRIORITY 此宏是 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 左移 4 位而来的,原因和宏 configKERNEL_INTERRUPT_PRIORITY 一样。此宏设置好以后,低于此优先级的中断可以安全的调用 FreeRTOS 的 API 函数,高于此优先级的中断 FreeRTOS 是不能禁止的,中断服务函数也不能调用 FreeRTOS 的 API 函数! ##### configMAX_PRIORITIES 此宏用来定义FreeRTOS自己的任务的最大优先级,和32不同的是,数字越大优先级越高,32则是相反的。 在FreeRTOSConfig.h中有如下定义: ```c #define configMAX_PRIORITIES ( 7 )
例如:
以 STM32 为例,有 16 个优先级,0 为最高优先级,15 为最低优先级,配置如下:
- configMAX_SYSCALL_INTERRUPT_PRIORITY==5
- configKERNEL_INTERRUPT_PRIORITY==15
由于高于 configMAX_SYSCALL_INTERRUPT_PRIORITY 的优先级不会被 FreeRTOS 内核屏蔽,因此那些对实时性要求严格的任务就可以使用这些优先级,比如四轴飞行器中的壁障检测。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!