跟着正点原子学习一下中断管理,正好之间没有总结过,还有些地方不清楚。
中断的工作方式就不介绍了,一般ARM-Cortex-M使用的是8位的寄存器来配置中断的优先级,但是在STM32中,只使用了高4位来配置中断优先级,所以最大只有16级。
抢占优先级:高优先级可以打断低优先级的中断
子优先级:相同抢占优先级的两个中断同时发生的时候,子优先级数值小的先执行(数值小优先级高)(子优先级的中断之间不会抢占)
一般这个优先级设置在HAL_Init()函数中(HAl库),通过调用HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4)完成设置。
要点:
1、低于configMAX_SYSCALL_INTERRUPT_PRIORITY优先级的中断里才允许调用FreeRTOS 的API函数。
2、建议将所有优先级位指定为抢占优先级位,方便FreeRTOS管理(设置成分组4),HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4)。
3、中断优先级数值越小越优先,任务优先级数值越大越优先。
SHPR1寄存器地址:0xE000ED18-0xE000ED1B
SHPR2寄存器地址:0xE000ED1C-0xE000ED1F
SHPR3寄存器地址:0xE000ED20-0xE000ED23
上表出自《Cortex M3权威指南(中文)》第286页。
在STM32cubeMX中生成的HAL库FreeRTOS中,SHPR3寄存器的名字及地址如下:
这里需要注意PendSV的优先级和SysTick的优先级,他们两是关于系统工作和任务调度的,所以保证其他中断能随时打断任务切换等,在库中按照如下方式设置优先级为最低。
在Cortex-M3中,如果OS在某个中断活跃时,抢占了该中断,而且又发生了任务调度,并执行了任务,切换到了线程运行模式,将直接触发Fault异常。(所以把SysTick和PendSV中断优先级设置最低也是有原因的,就是为了OS调度不抢占其他中断导致Fault的问题)
虽然这样看系统工作时钟会不稳定,但是没有办法,有文章说如果外部IRQ很多,那么可以考虑提高SysTick优先级,PendSV一直设置最低,来适当解决OS调度SysTick被抢占太厉害的问题。
在FreeRTOS中,需要设置这两个优先级:
1.首先,由于高四位是用来设置优先级的,所以,要设置这两个优先级为15,就需要将15左移4位
2.由于SHPR3寄存器是4个地址,每个地址是8位的,所以按照上表,PendSV需要左移16位,SysTick需要左移24位,UL是标记该宏是无符号长整型十进制数据。
3.将PendSV和SysTick的优先级设置为15,即最低优先级。
平常见的有三种:
1.PRIMASK:1,关闭所有,只剩下NMI和硬fault两个中断,默认是0;
2.FAULTMASK:1,关闭所有,只有NMI可以相应。默认是0;
3.BASEPRI:最多有9位,当他设置为某个值的时候,优先级比这个值低的全部为屏蔽(数值比这个值大),默认是0,不关闭任何中断。
在FreeRTOS中,利用的就是BASEPRI这个寄存器。比如设置为0x50的时候,优先级在5-15内的所有都被屏蔽,0-4的正常运行。
__asm 关键字用于调用内联汇编程序,并且可在 C 或 C++ 语句合法时出现,__asm与大括号一起使用,则该关键字表示大括号之间的每一行都是一条汇编语言语句。
默认生成的HAl中,如果调用该函数就把优先级为5-15的中断全部都关闭了,可以自己更改第一张图中的u1NEWBASEPRI的值来更改屏蔽的寄存器优先级
5-15的优先级中断被FreeRTOS所屏蔽,不会直接触发中断,要经过内核的管控,可能会存在一些延时,所以一般将一些实时性高的中断放在0-4中。而在5-15的中断中,可以应用FreeRTOS的API函数,但这些必须以“FromISR”结束
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if (htim->Instance == BTIM_TIMX_INT)
{
BTIM_TIMX_INT_CLK_ENABLE(); /* 使能TIMx时钟 */
HAL_NVIC_SetPriority(BTIM_TIMX_INT_IRQn, 6, 0); /* 抢占6,子优先级0 */
HAL_NVIC_EnableIRQ(BTIM_TIMX_INT_IRQn); /* 开启ITMx中断 */
}
if(htim->Instance == BTIM_TIM7_INT)
{
BTIM_TIM7_INT_CLK_ENABLE(); /* 使能TIM7时钟 */
HAL_NVIC_SetPriority(BTIM_TIM7_INT_IRQn, 4, 0); /* 抢占4,子优先级0 */
HAL_NVIC_EnableIRQ(BTIM_TIM7_INT_IRQn); /* 开启ITM7中断 */
}
}
正点原子的程序中,设置两个定时器中断的优先级为4和6,然后再任务中进行关闭和打开
void task1( void * pvParameters )
{
uint8_t task1_num = 0;
while(1)
{
if(++task1_num == 5)
{
task1_num = 0;
printf("关中断!!\r\n");
portDISABLE_INTERRUPTS();
delay_ms(5000);
printf("开中断!!!\r\n");
portENABLE_INTERRUPTS();
}
vTaskDelay(1000);
}
}
这里使用delay_ms(5000);而不使用vTaskDelay(1000);是因为vTaskDelay()函数内部中,在其内部的临界区调用了开中断的函数,使得中断会自动打开。