1、什么是中断
2、中断优先级分组设置
3、中断相关寄存器
正点原子ppt
STM32F1 FreeRTOS开发手册
7.1.1 中断简介
中断是微控制器一个很常见的特性,中断由硬件产生,当中断产生以后 CPU 就会中断当前 的流程转而去处理中断服务,Cortex-M 内核的 MCU 提供了一个用于中断管理的嵌套向量中断 控制器(NVIC)。 Cotex-M3 的 NVIC 最多支持 240 个 IRQ(中断请求)、1 个不可屏蔽中断(NMI)、1 个 Systick(滴 答定时器)定时器中断和多个系统异常。
7.1.2 中断管理简介
Cortex-M 处理器有多个用于管理中断和异常的可编程寄存器,这些寄存器大多数都在 NVIC 和系统控制块(SCB)中,CMSIS 将这些寄存器定义为结构体。以 STM32F103 为例,打开 core_cm3.h,有两个结构体,NVIC_Type 和 SCB_Type。重点注意的是三个中断屏蔽寄存 器:PRIMASK、FAULTMASK 和 BASEPRI。
正点原子ppt、STM32F1 FreeRTOS开发手册
当多个中断来临的时候处理器应该响应哪一个中断是由中断的优先级来决定的,高优先级 的中断(优先级编号小)肯定是首先得到响应,而且高优先级的中断可以抢占低优先级的中断, 这个就是中断嵌套。Cortex-M 处理器的有些中断是具有固定的优先级的,比如复位、NMI、 HardFault,这些中断的优先级都是负数,优先级也是最高的。
Cortex-M 处理器有三个固定优先级和 256(2^8) 个可编程的优先级。而绝大多数的芯片都会精简设计的,STM32 就只有 16 级优先级。在设计芯片的时候会裁掉表达优先级的几个低端有效位,以减少优先级数。使用MSB对齐方式(MSB对齐(Most Significant Bit Alignment)指的是将数据在存储或传输过程中,使得最高有效位(Most Significant Bit)与特定位置对齐的方式)。
Cortex-M 处理器还把 256 个优先级 按位分为高低两段:抢占优先级(分组优先级)和亚优先级(子优先级)
实现的方法就是对NVIC 中的一个寄存器“应用程序中断及复位控制寄存器(AIRCR)”进行操作,AIRCR 寄存器里面有个位段名为“优先级组”,对这个位段进行相应的赋值即可对抢占优先级和子优先级的位段进行划分。
寄存器的位段划分
对该位段赋值后对中断优先级的影响
#define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
4 bits for subpriority */
#define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
3 bits for subpriority */
#define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
2 bits for subpriority */
#define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
1 bits for subpriority */
#define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
0 bits for subpriority */
而移植 FreeRTOS 的时候我们配置的就是组 4,此时4 位优先级就都全是抢占优先级
这里在main.c中
/**********************************************************************************************************\
4 个相临的优先级寄存器拼成一个 32 位寄存器。
0xE000_ED20~0xE000_ED23 这四个寄存器就可以拼接成一个地址为 0xE000_ED20 的 32 位寄 存器。这一点很重要!因为 FreeRTOS 在设置 PendSV 和 SysTick 的中断优先级的时候都是直接 操作的地址 0xE000_ED20。
三个系统中断优先级配置寄存器,分别为 SHPR1、 SHPR2、 SHPR3
SHPR1寄存器地址:0xE000ED18
SHPR2寄存器地址:0xE000ED1C
SHPR3寄存器地址:0xE000ED20
FreeRTOS所使用的中断管理就是利用的BASEPRI这个寄存器
BASEPRI:屏蔽优先级低于某一个阈值的中断。比如: BASEPRI设置为0x50,代表中断优先级在5~15内的均被屏蔽,0~4的中断优先级正常执行
在 BASEPRI 寄存器中,屏蔽优先级低于某一个阈值的中断,当阈值设置为0时,则不关闭任何中断不过 。如果向 BASEPRI 写 0 的话就会停止屏蔽中断。比如,我们要屏蔽优先级不高于 0X60 的中断, 则可以使用如下汇编编程:
MOV R0, #0X60
MSR BASEPRI, R0
如果需要取消 BASEPRI 对中断的屏蔽,可以使用如下代码:
MOV R0, #0
MSR BASEPRI, R0
注意!FreeRTOS 的开关中断就是操作 BASEPRI 寄存器来实现的!它可以关闭低于某个阈 值的中断,高于这个阈值的中断就不会被关闭!
FreeRTOS 开关中断函数为 portENABLE_INTERRUPTS ()和 portDISABLE_INTERRUPTS(), 这两个函数其实是宏定义,在 portmacro.h 中有定义,如下:
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
#define portENABLE_INTERRUPTS() vPortSetBASEPRI(0)
可以看出开关中断实际上是通过函数 vPortSetBASEPRI(0)和 vPortRaiseBASEPRI()来实现 的,这两个函数如下:
界定FreeRTOS屏蔽中断的范围
#define __NVIC_PRIO_BITS 4 /*!< STM32 uses 4 Bits for the Priority Levels */
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 //系统可管理的最高中断优先级
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
这里其实就是对BASEPRI 寄存器的高四位赋值为5
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
}
}
这段代码是实现了一个函数vPortRaiseBASEPRI
,用于提高BASEPRI
寄存器的优先级,以实现临界区(critical section)的效果。
在这段代码中,首先定义了一个变量ulNewBASEPRI
,其值为 configMAX_SYSCALL_INTERRUPT_PRIORITY
,即最大的系统调用中断优先级。
然后,使用汇编指令msr
将ulNewBASEPRI
的值加载到BASEPRI
寄存器中,以设置新的中断优先级。接着,使用dsb
和isb
指令来确保指令执行的顺序和同步。
总的来说,这段代码的作用是将BASEPRI
寄存器的值设置为最大系统调用中断优先级,从而禁止其他优先级低于或等于该值的中断请求,形成一个临界区,确保关键代码的原子性执行,避免并发执行带来的竞争条件和不确定性。
开启所有中断
static portFORCE_INLINE void vPortClearBASEPRIFromISR( void )
{
__asm
{
/* Set BASEPRI to 0 so no interrupts are masked. This function is only
used to lower the mask in an interrupt, so memory barriers are not
used. */
msr basepri, #0
}
}
这段代码实现了一个函数vPortClearBASEPRIFromISR
,用于在中断服务程序(ISR)中清除BASEPRI
寄存器,解除对中断的屏蔽。
在这段代码中,使用汇编指令msr
将立即数#0
加载到BASEPRI
寄存器中,将BASEPRI
的值设置为0。这样,没有中断会被屏蔽,并允许所有优先级的中断请求正常处理。
需要注意的是,此函数用于在中断服务程序(ISR)中,因此没有使用内存屏障(memory barrier)来确保指令执行的顺序和同步。通常,在中断服务程序中,由于运行在单一上下文中,不需要进行数据同步和内存屏障操作。