1.Cortex-M3和M4的中断介绍
Cortex-M3和M4的NVIC最多支持240个IRQ(中断请求)、一个不可屏蔽中断(NMI)、一个SysTick(滴答定时器)定时器中断和多个系统异常。
2.Cortex-M3和M4中断屏蔽寄存器介绍
这里介绍的寄存器有3个:PRIMASK、FAULTMASK、BASEPRI
(1)PRIMASK寄存器
PRIMASK寄存器可以禁止除NMI和HardFault两个中断外的所有的异常和中断。
CPSIE I; // 清除PRIMASK(使能中断)
CPSID I; // 设置PRIMASK(禁止中断)
多说一句,在uC/OS中就是通过该方式实现中断的使能和禁止。
(2)FAULTMASK寄存器
FAULTMASK寄存器会把异常的优先级提升到-1,除复位外,其他的异常和中断都能屏蔽掉。
CPSIE F; // 清除FAULTMASK
CPSID F; // 设置FAULTMASK
(3)BASEPRI寄存器
该寄存器可以屏蔽低于某一个阈值的中断。
注意:FreeRTOS的开关中断就是操作BASEPRI寄存器来实现的,它可以关闭低于某个阈值的中断,高于这个阈值的中断就不会被关闭。
3.FreeRTOS中断配置宏
(1)configPRIO_BITS
此宏用来设置MCU使用几位优先级。
(2)configLIBRARY_LOWEST_INTERRUPT_PRIORITY
此宏用来设置最低优先级。
(3)configKEYNEL_INTERRUPT_PRIORITY
此宏是configLIBRARY_LOWEST_INTERRUPT_PRIORITY左移得来的。
此宏用来配置系统调用(PendSV)和滴答定时器(Systick)的中断优先级。
(4)configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
此宏用来设置FreeRTOS系统可管理的最大优先级,也就是BASEPRI寄存器中存放的阈值。
(5)configMAX_SYSCALL_INTERRUPT_PRIORITY
此宏是configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY左移得来的。
4.FreeRTOS开关中断
FreeRTOS开关中断的函数为portENABLE_INTERRUPTS()和portDISABLE_INTERRUPTS(),它们都是宏定义。
函数的定义位于portmacro.h中:
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI() // 关中断
#define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 ) // 开中断
由以上可以看出开关中断其实是由函数vPortSetBASEPRI(0)和vPortRaiseBASEPRI()来实现的,函数的原型定义如下:
// 开中断
// 向basepri中写入0就表示开中断
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
__asm
{
msr basepri, ulBASEPRI
}
}
// 关中断
// 向basepri中写入configMAX_SYSCALL_INTERRUPT_PRIORITY,
// 表明优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断都会被屏蔽
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
msr basepri, ulNewBASEPRI
dsb
isb
}
}
5.临界段代码
临界段代码也叫临界区,是指那些必须完整运行、不能被打断的代码段。
FreeRTOS在进入临界段代码的时候需要关闭中断,处理完临界段代码以后再打开中断。
FreeRTOS与临界段代码保护有关的函数有4个:taskENTER_CRITICAL()、taskEXIT_CIRTICAL()、taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR()。这些函数其实是宏定义,在task.h中定义。前两个是任务级的临界代码段保护,后两个是中断级的临界代码段保护。
(1)任务级临界代码段保护
taskENTER_CRITICAL()是进入临界段,taskEXIT_CRITICAL()是退出临界段,定义如下:
#define taskENTER_CRITICAL() portENTER_CRITICAL()
#define taskEXIT_CRITICAL() portEXIT_CRITICAL()
portENTER_CRITICAL()和portEXIT_CRITICAL()也是宏定义,在文件portmacro.h中有定义,如下:
#define portENTER_CRITICAL() vPortEnterCritical()
#define portEXIT_CRITICAL() vPortExitCritical()
函数vPortEnterCritical()和vPortExitCritical()在文件port.c中定义,如下:
// 进入临界段代码的时候调用
void vPortEnterCritical( void )
{
portDISABLE_INTERRUPTS(); // 关闭中断
uxCriticalNesting++; // uxCriticalNesting是一个全局变量,用来记录嵌套次数的。
if( uxCriticalNesting == 1 )
{
configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
}
}
// 退出临界段代码的时候调用
void vPortExitCritical( void )
{
configASSERT( uxCriticalNesting );
uxCriticalNesting--;
if( uxCriticalNesting == 0 )
{
portENABLE_INTERRUPTS(); // 打开中断
}
}
(2)中断级临界段代码保护
函数taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR()为中断级临界段代码保护函数,用在中断服务程序中,而且这个中断优先级一定要低于configMAX_SYSCALL_INTERRUPT_PRIORITY。这两个函数在文件task.h中定义。
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
函数portSET_INTERRUPT_MASK_FROM_ISR()和port_CLEAR_INTERRUPT_MASK_FROM_ISR()在portmacro.h中定义,如下:
#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI()
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x)
函数ulPortRaiseBASEPRI()在portmacro.h中定义,如下:
static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
mrs ulReturn, basepri // 读出basepri的值,放入ulReturn
msr basepri, ulNewBASEPRI // 将configMAX_SYSCALL_INTERRUPT_PRIORITY的值写入
// basepri中
dsb
isb
}
return ulReturn; // 返回ulReturn
}