一、STM32中断优先级
1.1 Cortex-M有3个固定优先级(Reset、NMI、Hard Fault 优先级为负数)和256个可以编程优先级,最多128个抢占优先级。而STM32拥有16个优先级即高4bit,此为IC设计即硬件决定。
优先级分组:AIRCR寄存器配置PRIGROUP段位。
STM32支持5组优先级分组设置:
1.2 中断优先级配置,函数原型如下:SCB->AIRCR
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
/* Check the parameters */
assert_param(IS_NVIC_PRIORITY_GROUP(PriorityGroup));
/* Set the PRIGROUP[10:8] bits according to the PriorityGroup parameter value */
NVIC_SetPriorityGrouping(PriorityGroup);
}
core_cm4.h下:
#define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping
__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
uint32_t reg_value;
uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */
reg_value = SCB->AIRCR; /* read old register configuration */
reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */
reg_value = (reg_value |
((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
(PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) ); /* Insert write key and priority group */
SCB->AIRCR = reg_value;
}
二、FreeRTOS里中断优先级配置
2.1 几个相关的定义
#define configPRIO_BITS 4
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 //系统可屏蔽阈值优先级
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 //最低优先级即15
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
//内核优先级配置,0xF0
#define configMAX_SYSCALL_INTERRUPT_PRIORITY (
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
//内核可以管理中断等级
以下截图来自ALIENTEK的《STM32F103 FreeRTOS开发手册V1.1》
2.2 SysTick和PendSV中断触发会引发系统调度,固需要将配置PendSV和SysTick的中断优先级设定为最低级别。
portNVIC_SYSPRI2_REG的地址 0xe000ed20 + 2即PendSV的优先级,固左移16bit
portNVIC_SYSPRI2_REG的地址 0xe000ed20 + 3即SysTick的优先级, 固左移24bit
#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) )
#define portNVIC_PENDSV_PRI ((( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL )
#define portNVIC_SYSTICK_PRI ((( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL )
BaseType_t xPortStartScheduler( void )
{
.......
/* Make PendSV and SysTick the lowest priority interrupts. */
portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;
.......
}
三、临界区(运行过程不能被打断,如某些时序要求比较严格的通信)
#define taskENTER_CRITICAL() portENTER_CRITICAL()
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define taskEXIT_CRITICAL() portEXIT_CRITICAL()
#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
3.1 任务级临界代码段保护
执行临界区的代码一定要精简,避免进入临界区后(中断关闭后),中断优先级在系统管控范围内即低于configMAX_SYSCALL_INTERRUPT_PRIORITY(<=5)的中断得不到及时响应。优先级>5的中断不受系统管控。
/******************************进入临界区***********************************/
void vPortEnterCritical( void )
{
portDISABLE_INTERRUPTS(); //关中断
uxCriticalNesting++;
if( uxCriticalNesting == 1 )
{
configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
}
}
//关中断,给BASEPRI写configMAX_SYSCALL_INTERRUPT_PRIORITY即0x50,则优先级<=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
}
}
/***************************退出临界区****************************************/
void vPortExitCritical( void )
{
configASSERT( uxCriticalNesting );
uxCriticalNesting--;
if( uxCriticalNesting == 0 )
{
portENABLE_INTERRUPTS(); //使能中断,需要全局计数值为0
}
}
//开中断,给BASEPRI写0
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
__asm
{
/* Barrier instructions are not used as this function is only used to
lower the BASEPRI value. */
msr basepri, ulBASEPRI
}
}
3.2 中断级临界代码段保护
中断服务函数(该中断优先级必须低于configMAX_SYSCALL_INTERRUPT_PRIORITY)中使用临界区代码保护。
static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
/* Set BASEPRI to the max syscall priority to effect a critical
section. */
mrs ulReturn, basepri
msr basepri, ulNewBASEPRI
dsb
isb
}
return ulReturn;
}
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
__asm
{
/* Barrier instructions are not used as this function is only used to
lower the BASEPRI value. */
msr basepri, ulBASEPRI
}
}
使用方法:
void XXX_IRQHandler(void)
{
......
value = taskENTER_CRITICAL_FROM_ISR(); //进入临界区
.......
taskEXIT_CRITICAL_FROM_ISR( value ); //退出临界区
}
注意:在保证成对使用的情况下,通过保存和恢复寄存器 basepri 的数值可以实现中断嵌套,作用与使用全局中断嵌套计数值一致。