文章目录
1、两套API函数
(1)使用场景不同
在读写队列等FreeRTOS提供API函数均有两套(如下图所示),其可用于不同的场景。
一套可以在任务和中断中使用(带有后缀FromISR的),另一套只能在任务中使用。
(2)更进一步的区别——xHigherPriorityTaskWoken 参数
关键在于 xHigherPriorityTaskWoken 参数,当然如果不想使用这个参数可知直接传NULL,
在上图中前一个API函数可能导致任务阻塞、任务切换,即上下文切换,这个函数可能很长时间才能返回,在函数内部实现了任务切换。
而后一个函数也可能导致任务切换,但是不会在函数内部进行切换。而是返回一个参数来表示是否需要切换。(明显后者的效率更高)
(3)那么使用带后缀FromISR的话,任务切换是如何进行的呢?
1)自己根据参数判断
2)调用API函数进行判断
2、中断的延时处理——即中断服务函数中记录标志位,退出后才在任务中进行处理
对于中断的处理来说,ISR要尽量的快,所以对于中断的处理来说一般是分为如下的两个部分(即中断的延时处理)
具体可以如下例子:
3、FreeRTOS中对中断的管理
(1)FreeRTOS可管理的最高中断优先级的设置
对于前面临界资源访问中提出的问题——关闭中断是否是关闭了所有中断呢?
答案是没有关闭所有中断,如下图中:
假定横线上方的中断为A类,下方的为B类,A类中断服务函数中绝对不能调用上面说的带有FromISR后缀的中断,而B类可以。
所有对于前面的读写队列等操作中关闭的中断仅仅是上面假定的B类中断 (systick也属于假定的B类中断,还有PendSV、其他硬件中断等)
前面提到自己设想的A B类中断的划分:用户可以自定义配置系统可管理的最高中断优先级的宏定义
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY , 它 是 用 于 配 置 内 核 中 的basepri 寄存器的,当 basepri 设置为某个值的时候,NVIC 不会响应比该优先级低的中断,而优先级比之更高的中断则不受影响。
(2)那么在FreeRTOS提供的API中代码端是如何判断是否可以管理的呢?
在每个带有FromISR后缀的API函数内部均调用了一个函数(见下图)来判断当前优先级是否有效(即判断当前优先级是否对于一个阈值,对于这一点的优先级数值越小优先级越高)
当判断当前优先级有效的时候才能进行带有FromISR后缀的API函数内部的后续代码(这个是FreeRTOS所做的措施)
这个API内部最终关键代码如下:
所以若在优先级高于可管理的最高优先级的中断处理函数里面调用了带有FromISR后缀的API函数,进入这个API函数后,此时因为当前优先级无效,所以这个API函数会进入死循环,即认为系统死机。
(3)FreeRTOS中经典的可管理的几类中断
主要是有systick中断、PendSV中断和一些硬件中断;其中前两个中断的优先级是最低的,其都是用于任务的切换。
具体是在开启调度器的API函数里面实现的:
/* Make PendSV and SysTick the lowest priority interrupts. */
portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;
其中里面用到的宏定义为:
1. #define __NVIC_PRIO_BITS 4 /*!< STM32 uses 4 Bits for the Priority Levels */
2. #define configPRIO_BITS __NVIC_PRIO_BITS
3. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 //中断最低优先级
4. #define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
5.#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL )
6.#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL )
(4)Pendsv中断的作用
对于systick中断服务程序的作用前面提到了,那么PendSV中断(任务调度中断)服务程序的作用是什么呢?
PendSV中断服务程序的作用:保存当前任务的现场、挑出当前优先级最高的就绪任务(在前面说到的链表中找,实际直接调用vTaskSwitchContext())并切换挑出来的任务来执行
即任务的切换是在 PendSV 中断服务函数中完成的。
那么问题又来了——为什么前面说任务的切换是在systick中断中完成的呢?
实际上是在SysTick中断中触发PendSV中断,实际的任务切换是在PendSV的中断服务函数中完成的。