中断概念
Cortex-M的NVIC最多支持240个IRQ(中断请求)、1个不可屏蔽中断(NMI)、1个Systick(滴答定时器)定时器中断和多个系统异常。
Cortex-M处理器有多个用于管中断和异常的可编程寄存器, 这些寄存器大多数都在 NVIC和系统控制块 (SCB)中, CMSIS将这些寄存器定义为结构体。以 STM32F103为例,打开core_cm3.h,有两个结构体, NVIC_Type和 SCB_Type。NVIC和SCB都位于系统控制空间(SCS)内,SCS的地址从0XE000E000开始。
对于FreeRTOS比较关注的是三个中断屏蔽寄存器:PRIMASK、FAULTMASK和BASEPRI。
优先级
Cortex-M处理器有三个固定优先级和256个可编程优先级,最多128个抢占优先级(因为抢占式最多只能占7bit,留一个给子优先级),但实际的优先级数量是由芯片决定的,大多数为8、16、32级等,比如STM32只有16级(这里用了4bit,所以2的4次是16),在设计芯片时会裁掉表达优先级低几位,下面是STM32使用Cortex-M的高四位表示优先级:
使用4位表达优先级
Bit7 |
Bit6 |
Bit5 |
Bit4 |
Bit3 |
Bit2 |
Bit1 |
Bit0 |
用于表达优先级 |
没有实现,读回0 |
STM32支持的5组优先级
优先级分组 |
抢占优先级 |
子优先级 |
描述 |
NVIC_PriorityGroup_0 |
0 |
0-15 |
主-0bit,子-4bit |
NVIC_PriorityGroup_1 |
0-1 |
0-7 |
主-1bit,子-3bit |
NVIC_PriorityGroup_2 |
0-3 |
0-3 |
主-2bit,子-2bit |
NVIC_PriorityGroup_3 |
0-7 |
0-1 |
主-3bit,子-1bit |
NVIC_PriorityGroup_4 |
0-15 |
0 |
主-4bit,子-0bit |
移植FreeRTOS只能配置组4(只有抢占优先级),因为FreeRTOS中断处理没有子优先级这种情况
优先级设置
每个外部中断都有一个对应的优先级寄存器,每个寄存器占8bit,最大宽度8bit,最小3bit。4个相邻的寄存器可以拼成一个32bit寄存器。在FreeRTOS设置PendSV和Systick的中断优先级的时候都是直接操作地址0xE000ED20
FreeRTOS中断的屏蔽和开启
(1)FreeRTOS在STM32中中断关闭调用 BASEPRI寄存器。
①只屏蔽优先级低于某一个阈值的中断
1)MOV R0, #0X60
2)MSR BASEPRI, R0
②开启中断
1)FreeRTOS中开启中断是通过对BASEPRI寄存器写0进行的
2)MOV R0, #R0
3)MSR BASEPRI, R0
portENABLE_INTERRUPTS ()//开中断
portDISABLE_INTERRUPTS()//关中断
FreeRTOS中断配置宏
configPRIO_BITS
设置MCU使用几位优先级,STM32使用4位
configLIBRARY_LOWEST_INTERRUPT_PRIORITY
设置最低优先级,因为STM32使用了4bit,这里也配置了组4,所以只有抢占优先级0-15,这里15就是最低优先级。
configKERNEL_INTERRUPT_PRIORITY
用来设置内核中断优先级,主要是PendSV和Systick中断优先级,这里设置为0xF0(最低优先级是configLIBRARY_LOWEST_INTERRUPT_PRIORITY==15,即0x0F,这里要左移4位,得到0xF0),最低的中断优先级。这里写0xFF也行
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
设置FreeRTOS系统可管理的最大优先级,就是BASEPRI寄存器的阈值。现在的范围是0~15,如果设置为5,也就是高于5的优先级(0~5)不归FreeRTOS管理。
configMAX_SYSCALL_INTERRUPT_PRIORITY
是configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY左移4bit来的,低于configKERNEL_INTERRUPT_PRIORITY设置好的阈值,可以安全调用FreeRTOS的API函数,但未被FreeRTOS管理的中断是不能禁止的(就是调用FreeRTOS开关中断时,对这些中断不受影响),其中断函数不能调用FreeRTOS的API函数
临界段代码
指必须完整运行,不能被打断的代码(代码要保证精简),比如有的初始化过程不能被打算。一般都是采用开关中断的方式。FreeRTOS在进入临界段代码时需要关闭中断,处理完临界段代码再打开中断。分以下两种:
任务级临界段代码保护
taskENTER_CRITICAL()和 taskEXIT_CRITICAL()是任务级的临界代码保护,一个进入是任务级的临界段,一个是退出临界段,成对使用。可能会发生临界段嵌套,所以看源码需要一个全局变量uxCriticalNesting++;来保证成对使用。
中断级临界段代码保护
taskENTER_CRITICAL_FROM_ISR()和 taskEXIT_CRITICAL_FROM_ISR()
在中断服务程序使用的
这个中断服务程序的优先级一定要低于configMAX_SYSCALL_INTERRUPT_PRIORITY,因为高于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断不能调用FreeRTOS的API,避免造成系统紊乱。
以 “FromISR” 结尾的 FreeRTOS 函数是具有中断调用保护的(执行这些函数会进入临界区),但是就算是这些函数,也不可以被逻辑优先级高于 configMAX_SYSCALL_INTERRUPT_PRIORITY
的中断服务函数调用