Skip to content

中断管理

中断是在内核进行的, 执行的时候用户的任务没有办法执行, 所以应该尽快执行

遇到非常耗时的中断应该把中断作为触发任务的手段, 将复杂的任务放在任务里面执行

任务只有在没有没有中断的时候才可以进行

IRQ (Interrupt Request) 是一种中断请求,是一种通知处理器进行处理的信号。当外部设备或者内部硬件发生某种事件时,就会向处理器发送 IRQ 信号,请求处理器暂停当前任务,进行中断处理。

ISR (Interrupt Service Routine) 是中断服务例程,也就是中断处理程序。当处理器收到 IRQ 信号后,会执行 ISR,ISR 会完成中断处理,并将处理结果返回给处理器,处理器再继续执行原来的任务。ISR 可以是预先编写的固定代码,也可以是动态生成的代码。

在很多的是偶FreeRTOS有两套操作函数, 后缀里面有FromISR的函数不会使得任务进入阻塞

image-20231122141607311

image-20231122141755955

image-20231122141812626

image-20231122141824006

函数的区别:

是否可以阻塞, 在任务里面执行的时候有一个死循环

在判断是否有在等待的任务以后会把这个任务从等待的队列里面删除, 之后判断释放出来的优先级比较高, 就在任务中会进行任务的切换, 在中断里面只是进行一次记录

在队列是满的的时候在任务里面会判断当前的等待时间是不是0, 是的话返回错误, 不是的话会把任务加入到等待的队列里面, 按照优先级进行排列, 之后切换任务

在中断里面的时候会直接返回一个错误

  • xHigherPriorityTaskWoken参数

是否有更高优先级的任务被唤醒了。如果为pdTRUE,则意味着 后面要进行任务切换。

在正常情况下使用写队列的函数的时候会有三种情况

  1. 队列满了进入阻塞
  2. 队列没有满, 但是写入以后会使得一个高优先级的任务被唤醒, 优先级比较高的任务应该先运行
  3. 队列没有满写入成功立即返回

可以看到,在任务中调用API函数可能导致任务阻塞、任务切换,这叫做"context switch",上下文切 换。这个函数可能很长时间才返回,在函数的内部实现了任务切换。

xQueueSendToBackFromISR() 函数也可能导致任务切换,但是不会在函数内部进行切换,而是返回一 个参数:表示是否需要切换,函数原型与用法如下:

c
/*
* 往队列尾部写入数据,此函数可以在中断函数中使用,不可阻塞
*/
BaseType_t xQueueSendToBackFromISR(
QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken
);
/* 用法示例 */
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xQueueSendToBackFromISR(xQueue, pvItemToQueue, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE)
{
/* 任务切换 */
}

xHigherPriorityTaskWoken会保存函数的结果是否需要进行任务切换

  • 不在函数里面直接切换的原因

为了效率, 因为如果多次调用这这类函数的话就会进行多次的判断然后切换

c
void XXX_ISR()
{
    int i;
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    for (i = 0; i < N; i++)
    {
    	xQueueSendToBackFromISR(..., &xHigherPriorityTaskWoken); /* 被多次调用 */
    }
    /* 最后再决定是否进行任务切换 */
    if (xHigherPriorityTaskWoken == pdTRUE)
    {
    	/* 任务切换 */
    }
}

image-20231122143335865

切换任务

c
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );

portYIELD_FROM_ISR( xHigherPriorityTaskWoken );

老版本前一个使用汇编实现, 后一个使用C语言实现, 新的版本里面统一使用的后面那一个

在参数里面传入在上面获得的是否需要切换的值