一、中断
中断是 CPU 的一种常见特性,中断一般由硬件产生,当中断发生后,会中断 CPU 当前正
在执行的程序而跳转到中断对应的服务程序种去执行,ARM Cortex-M 内核的 MCU 具有一个
用于中断管理的嵌套向量中断控制器NVIC。ARM Cortex-M 的 NVIC 最大可支持 256 个中断源,其中包括 16 个系统中断和 240 个外部中断。
中断优先级配置寄存器的值与对应的优先等级成反比,即中断优先级配置寄存器的值越小,中断的优先等级越高。
STM32 的中断优先级可以分为抢占优先级和子优先级。抢占优先级:抢占优先级高的中断可以打断正在执行但抢占优先级低的中断,即抢占优先级支持中断嵌套。子优先级:抢占优先级相同时,子优先级高的中断不能打断正在执行但子优先级低的中的中断,即子优先级不支持中断嵌套。
二、临界区
宏 taskENTER_CRITICAL()
此宏用于在非中断中进入临界区,展开后是函数 vPortEnterCritical(),函数vPortEnterCritical() 的代码如下所示:
void vPortEnterCritical( void )
{
/* 关闭受 FreeRTOS 管理的中断 */
portDISABLE_INTERRUPTS();
/* 临界区支持嵌套 */
uxCriticalNesting++;
if( uxCriticalNesting == 1 )
{
/* 这个函数不能在中断中调用 */
configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
}
}
从上面的代码中可以看出,函数 vPortEnterCritical()进入临界区就是关闭中断,当然了,不受 FreeRTOS 管理的中断是不受影响的。还可以看出,FreeRTOS 的临界区是可以嵌套的,意思
就是说,在程序中可以重复地进入临界区,只要后续重复退出相同次数的临界区即可。
宏 taskEXIT_CRITICAL()
此宏用于从非中断中退出临界区,此宏展开后是vPortExitCritical(),函数vPortExitCritical()的代码如下所示:
void vPortExitCritical( void )
{
/* 必须是进入过临界区才能退出 */
configASSERT( uxCriticalNesting );
uxCriticalNesting--;
if( uxCriticalNesting == 0 )
{
/* 打开中断 */
portENABLE_INTERRUPTS();
}
}
这个函数就很好理解了,就是将用于临界区嵌套的计数器减 1,当计数器减到 0 的时候,
说明临界区已经没有嵌套了,于是调用函数 portENABLE_INTERRUPT()打开中断。在函数的一
开始还有一个断言,这个断言用于判断用于临界区嵌套的计数器在进入此函数的不为 0,这样
就保证了用户不会在还未进入临界区时,就错误地调用此函数退出临界区。
三、任务调度器
1、FreeRTOS
中提供的任务调度器是基于优先级的全抢占式调度:在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的。
2、函数 vTaskStartScheduler()用于启动任务调度器,任务调度器启动后,FreeRTOS 便会开始进行任务调度,除非调用函数 xTaskEndScheduler()停止任务调度器,否则不会再返回。也就是说调用vTaskStartScheduler函数启动任务调度器之后不会执行函数后面的语句。
3、挂起任务调度器,禁止任务的调度和切换,执行内容不允许被其他内容打断,但是中断可以正常打断执行的内容。挂起任务调度器主要是防止任务与任务之间的资源抢夺。与临界区不一样的是,挂起任务调度器,未关闭中断;任务调度器仅仅是防止了任务之间的资源争夺,中断照样可以直接响应;
vTaskSuspendAll() ;
/*代码*/
xTaskResumeAll() ;
4、 vTaskStartScheduler()主要做了六件事情
(1) 创建空闲任务,根据是否支持静态内存管理,使用静态方式或动态方式创建空闲任务。
(2) 创建定时器服务任务,创建定时器服务任务需要配置启用软件定时器,创建定时器服务
任务,同样是根据是否配置支持静态内存管理,使用静态或动态方式创建定时器服务任务。 (3)关闭中断,使用 portDISABLE_INTERRUPT()关闭中断,这种方式只关闭受 FreeRTOS 管
理的中断。关闭中断主要是为了防止 SysTick 中断在任务调度器开启之前或过程中,产生中断。
FreeRTOS 会在开始运行第一个任务时,重新打开中断。
(4)初始化一些全局变量,并将任务调度器的运行标志设置为已运行。
(5)初始化任务运行时间统计功能的时基定时器,任务运行时间统计功能需要一个硬件定时
器提供高精度的计数,这个硬件定时器就在这里进行配置,如果配置不启用任务运行时间统计
功能的,就无需进行这项硬件定时器的配置。
(6)最后就是调用函数 xPortStartScheduler()。
四、 NVIC异常类型
五、总结
进入临界区只是关闭了freeRTOS管理的中断,通过配置freertosconfig.h中的宏 configMAX_SYSCALL_INTERRUPT_PRIORITY (配置 FreeRTOS 可管理的最高优先级的中断,值为5),所以进入临界区之后中断优先级大于5的都可以打断临界区的运行。然后任务切换也可以打断临界区的运行
挂起任务调度器只是关闭了任务的调度和切换,但是此时中断都可以打断此任务的运行
优先级最小的中断>优先级最大的任务 中断是数字越小优先级越高,任务优先级是数字越大优先级越高