一.两类API对比
![](https://i-blog.csdnimg.cn/direct/69f668b41dd84e35998d78cc6433ab3c.png)
FreeRTOS有两套函数,一套是普通中,一套是在中断中使用的(在中断中使用的API在函数名的尾部都有FromISR),中断中使用的FreeRTOS的API有什么特殊点呢?1: 执行速度尽可能快,尽可能减小对操作系统实时性的影响 2:不能阻塞,因为中断是单片机硬件本身的操作,可不受FreeRTOS的调度,一旦阻塞(比如等待信号量),而中断中的内容不会被调度器调用,此时程序就卡死了。
二.以写队列为例对比两类API
![](https://i-blog.csdnimg.cn/direct/f3164d7ce47f419093bd3b328d2f8848.png)
详细内部软件流程
附件链接:
1.普通任务写队列执行过程:
(1)当队列没满的时候写入队列,如果有任务在等待接收队列数据,那就把这个任务唤醒,如果这个任务优先级比当前高,那就发起一次调度
(2)当前队列满但是写队列等待时间为0,那就直接返回错误
(3)当前队列满,且写队列等待时间不为0,那就把当前任务从readylist中移除,同时添加到delayList里去,任务阻塞
2.中断写队列执行过程
(1)函数参数里没有等待时间,即不允许写队列等待(保证不在中断中发生阻塞)
(2)当队列没满的时候写入队列,如果有任务在等待接收队列数据,那就把这个任务唤醒,如果这个任务优先级比当前高,不发起调度,但是把需要发起调度的变量设置为
pdTRUE
3.中断中写队列使用方法举例
void xxx_ISR()//某中断响应函数内部
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;//这边变量用来存放是否要发起调度
xQueueSendToBackFromISR(xQueue, pvItemToQueue, &xHigherPriorityTaskWoken);//如果有高优先级任务,需要发起调度,那xHigherPriorityTaskWoken会被设为pdTRUE
if (xHigherPriorityTaskWoken == pdTRUE)
{
/* 任务切换 */
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );//触发PendSV中断。由于PendSV中断中断优先级低,所以会在该中断函数执行后才会执行
}
}
在上述xQueueSendToBackFromISR(xQueue, pvItemToQueue, &xHigherPriorityTaskWoken)中,如果因为向队列中发送数据导致有任务处于就绪态,同时该任务的优先级更高,那么xHigherPriorityTaskWoken就会被赋值为1。这个时候就可以使用portYIELD_FROM_ISR(pdTRUE)来触发上下文的切换。那portYIELD_FROM_ISR(pdTRUE)的原理是什么呢?这个函数会设置pendsv对应的bit位置1,即触发pendSV中断,在pendsv中完后任务的上下文切换。由于pendsv优先级很低,所以pendsv会在当前中断后执行。
不仅是队列操作的工作流程是这样,信号量、事件组等也是一样。互斥量不可以在中断中使用。
4.总结
在使用非ISR的API函数时,比如写队列xQueueSendToBack(),如果此时有个高优先级的任务正在等待写队列,那么前面这个xQueueSendToBack()执行过程中就会发起调度。
但是ISR的API,比如xQueueSendToBackFromISR()在相同情况下不会发起调度(因为在中断里,优先级都比pendsv高,就算发起调度,pensv也不会执行),所以这个API只是把xHigherPriorityTaskWoken这个值设置为pdTrue,然后在函数快结束的时候执行portYIELD_FROM_ISR( xHigherPriorityTaskWoken ),触发pendsv从而触发调度。