目录
- 中断管理基本概念
- 中断处理机制
- 中断优先级
- 中断优先级分组设置
- 系统中断优先级配置寄存器
- 中断屏蔽寄存器
- FreeRTOS中断管理实验
在 FreeRTOS 中,中断管理是确保系统高效和可靠运行的关键部分。下面对 FreeRTOS 的中断管理进行详细介绍。
中断管理基本概念
中断是一种事件驱动的机制,可以在程序执行过程中打断当前的代码流,以响应硬件或软件事件。中断管理的目标是快速处理这些事件,确保系统能及时响应外部信号。
中断处理机制
对于中断的处理机制,可以概括为三步:
- 中断请求:是指由硬件设备或软件程序发出的请求信号(如GPIO外部中断、定时器中断等),用于通知处理器有事件发生,需要其注意和处理。
- 响应中断:CPU停止执行当前程序,转而去执行中断处理程序(ISR)。
- 退出中断:在中断服务例程执行完成后,系统恢复到中断发生前的状态,继续执行之前的任务。
中断优先级
FreeRTOS 允许使用多种优先级管理中断。每个中断源可以配置为不同的优先级,以决定哪些中断可以抢占其他中断的执行。这一机制使得系统能够处理优先级高的任务,同时不会被低优先级的任务打断。
中断优先级分组设置
ARM Cortex-M 使用了 8 位宽的寄存器来配置中断的优先等级,这个寄存器就是中断优先级配置寄存器,因此最大中断的优先级配置范围位 0~255。但是芯片厂商一般用不完这些资源,对于 STM32,只用到了中断优先级配置寄存器的高 4 位[7:4],低四位[3:0]取零处理,因此 STM32 提供了最大 2^4=16 级的中断优先等级,如下图所示:
STM32 的中断优先级可以分为抢占优先级和子优先级(中断优先级数值越小越优先),抢占优先级和子优先级的区别如下:
抢占优先级:抢占优先级高的中断可以打断正在执行但抢占优先级低的中断,即中断嵌套。
子优先级:抢占优先级相同时,子优先级数值小的优先执行,但当子优先级低的中断正在执行时,子优先级高的中断不能打断该子优先级低的中断,即子优先级不支持中断嵌套。
STM32对于中断优先级分组一共有 5 种分配方式,如下所示:
对于STM32,推荐使用中断优先级分组 4(NVIC_PriorityGroup_4)即优先级配置寄存器的高 4 位全部用于抢占优先级,不使用子优先级,这么一来用户就只需要设置抢占优先级即可。
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
系统中断优先级配置寄存器
除了外部中断,系统中断有独立的中断优先级配置寄存器,分别为 SHPR1、SHPR2、SHPR3。
- SHPR1:SHPR1 寄存器的地址为 0xE000ED18,用于配置MemManage(内存管理异常)、BusFault(总线错误异常)、UsageFault (使用错误异常)的中断优先级。
- SHPR2:SHPR2 寄存器的地址为 0xE000ED1C,用于配置 SVCall (Supervisor Call)的中断优先级。
SVCall用于管理任务的调度和上下文切换。 - SHPR3:SHPR3 寄存器的地址为 0xE000ED20,用于配置 PendSV、SysTick 的中断优先级。
PendSV:通常用于上下文切换,确保任务之间的切换能够平滑进行。
SysTick:用于生成系统节拍,定期触发调度。
中断屏蔽寄存器
ARM Cortex-M 有三个用于屏蔽中断的寄存器,分别为 PRIMASK、FAULTMASK 和BASEPRI。
- PRIMASK
用于全局中断使能/禁用。设置PRIMASK寄存器的最低位(bit 0)为1会禁用所有中断(除了NMI和硬fault可以响应),而设置为0则恢复中断的响应。 - FAULTMASK
当FAULTMASK的最低位为1时,用于屏蔽所有中断,只有NMI可以响应,所有其他的异常(包括中断和fault)通通不能响应。设置为0则恢复异常的响应。 - BASEPRI
BASEPRI 用于设置一个中断屏蔽的阈值,设置好 BASEPRI 后,中断优先级低于 BASEPRI 的中断就都会被屏蔽掉,FreeRTOS 就是使用 BASEPRI 寄存器来管理受 FreeRTOS管理的中断的,而不受 FreeRTOS 管理的中断,则不受 FreeRTOS 的影响。
在FreeRTOSConfig.h文件中,定义了FreeRTOS所能管理的中断优先级(5~15)
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 /* 中断最低优先级 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 /* FreeRTOS可管理的最高中断优先级 */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << 4 )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << 4 )
#define configMAX_API_CALL_INTERRUPT_PRIORITY configMAX_SYSCALL_INTERRUPT_PRIORITY
BASEPRI被设置成了0x50( 5 << 4),表示中断优先级在5~15内的均被屏蔽,0-4内中断优先级正常执行。
注意:
- 中断服务函数的优先级需在FreeRTOS所管理的范围内。
- 在中断服务函数里边需调用FreeRTOS的API函数,必须使用带“FromISR”后缀的函数。
FreeRTOS中断管理实验
本实验使用到两个定时器,一个时优先级为4的TIM2,一个时优先级为6的TIM3。两个定时器每1s打印一段字符串,观察开中断和关中断的现象。
本文的实验代码基于FreeRTOS——任务创建和删除。
void task1(void *pvParameters)
{
static uint32_t Task_num = 0;
while(1)
{
if(Task_num == 5)
{
Task_num = 0;
printf("关中断!!\r\n");
portDISABLE_INTERRUPTS();
HAL_Delay(5000);
printf("开中断!!!\r\n");
portENABLE_INTERRUPTS();
}
Task_num ++;
vTaskDelay(1000);
}
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(tim_baseHandle->Instance==TIM2)
{
__HAL_RCC_TIM2_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_NVIC_SetPriority(TIM2_IRQn, 4, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
}
else if(tim_baseHandle->Instance==TIM3)
{
__HAL_RCC_TIM3_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_NVIC_SetPriority(TIM3_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(TIM3_IRQn);
}
}
如上文所述,FreeRTOS使用的是中断优先级分组 4,即只需要设置抢占优先级而子优先级为0。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM4) {
HAL_IncTick();
}
if(htim->Instance==TIM2)
{
printf("TIM3优先级为6的正在运行!!!\r\n");
}
else if(htim->Instance==TIM3)
{
printf("TIM2优先级为4的正在运行!!!!!\r\n");
}
}
用于处理定时器中断的回调函数在定时器溢出或达到预设的周期时被自动调用,TIM2和TIM3每1s会进行一次打印。
中断未关闭前,TIM2和TIM3每1s打印一串字符,中断关闭后,只有TIM2每1s打印一串字符,说明了优先级为4的TIM2不在FreeRTOS所管理的中断范围内,而优先级为6的TIM3在该范围(优先级为5~15)内,受FreeRTOS管理。