中断定义
什么是中断
使CPU打断正在执行的程序,转去执行紧急的事件或程序,这即是中断。
中断执行步骤可以概括为三点:
一是中断请求:外设请求中断(GPIO外部中断、定时器中断等)。
二是响应中断:CPU停止执行当前程序,转而去执行中断处理程序(ISR)。
三是退出中断:执行完毕,返回被打断的程序处,继续往下执行。
中断优先级
在处理紧急事件时,有更紧急的事件出现,该如何执行?
更紧急的事件会打断紧急事件,从而去执行更紧急的事件,更紧急的事件会抢占紧急的事件。更紧急的时间有更高的优先级,优先级高的事件会抢占优先级低的事件。
ARM Cortex-M使用了8位宽的寄存器来配置中断的优先等级,这个寄存器就是中断优先级配置寄存器。但STM32单片机只用了中断优先级配置寄存器的高4位 [7 : 4],所以STM32提供了最大16级的中断优先等级。
优先级分为抢占优先级和子优先级。
抢占优先级:抢占优先级高的中断可以打断正在执行但抢占优先级低的中断。
子优先级:当同时发生具有相同抢占优先级的两个中断时,子优先级数值小的优先执行。
优先级越小越优先
中断优先级设置
一共有 5 种分配方式,对应着中断优先级分组的 5 个组。
通过调用函数HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4)即可完成设置。
特点:
1、低于configMAX_SYSCALL_INTERRUPT_PRIORITY优先级的中断里才允许调用FreeRTOS 的API函数。
2.建议将所有优先级位指定为抢占优先级位,方便FreeRTOS管理。通过调用函数调用函数HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4)即可实现。
3.中断优先级数值越小越优先,任务优先级数值越大越优先。
中断相关寄存器
三个系统中断优先级配置寄存器分别是SHPR1、SHPR2以及SHPR3。这三个寄存器的寄存器地址分别是0xE000ED18、0xE000ED1C以及0xE000ED20。
0xE000ED18-0xE000ED1B分别是四个8位寄存器的寄存器地址,这四个寄存器组成SHPR1。同样地,SHPR2以及SHPR3都分别由四个8位的寄存器组成。
宏定义SHPR3的起始地址。
/*宏定义*/
#define portNVIC_SHPR3_REG (*((volatile uint32_t *)0xe000ed20))
宏定义配置pendsv以及systick的优先级,portNVIC_SHPR3_REG分别左移8*2=16位,8*3=24位即可。
PendSV和SysTick设置最低优先级的原因是保证系统任务切换不会阻塞系统其他中断的响应,换句话说即任务不可以打断中断,中断可以打断任务。
关中断
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
msr basepri, ulNewBASEPRI
dsb
isb
}
}
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 /* FreeRTOS可管理的最高中断优先级 */
开中断
#define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 )
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
__asm
{
msr basepri, ulBASEPRI
}
}
中断管理实验
设置任务
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区 */
/* 初始化TIM3、TIM5 */
btim_tim3_int_init(10000-1, 7200-1); //1s计时
btim_tim5_int_init(10000-1, 7200-1);
/* 创建任务1 */
xTaskCreate((TaskFunction_t )task1,
(const char* )"task1",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
vTaskDelete(StartTask_Handler); /* 删除开始任务 */
taskEXIT_CRITICAL(); /* 退出临界区 */
}
void task1(void *pvParameters)
{
uint32_t task1_num = 0;
while (1)
{
if (++task1_num == 5)
{
printf("FreeRTOS关闭中断\r\n");
portDISABLE_INTERRUPTS(); /* FreeRTOS关闭中断 */
delay_ms(5000);
printf("FreeRTOS打开中断\r\n");
portENABLE_INTERRUPTS(); /* FreeRTOS打开中断 */
}
vTaskDelay(1000);
}
}
配置定时器(在中断服务函数里设置)
/*定时器更新中断回调函数*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim == (&g_tim3_handle))
{
printf("TIM3输出\r\n");
}
else if (htim == (&g_tim5_handle)) /*htim为定时器句柄指针*/
{
printf("TIM5输出\r\n");
}
}
TIM3的优先级是4,TIM5的优先级是6,首先打印"TIM3输出"和“TIM5”五次,然后执行FreeRTOS关闭中断,中断优先级高于5的TIM3中断继续执行,中断优先级低于5的TIM5中断停止运行,5s后FreeRTOS打开中断,TIM5中断可以执行