文章标题

先分配内存空间
初始化消息队列
系统自动为控制块方培内存空间

如存储位置,头指针,为指针,消息大小,队列长度,
消息队列和消息空间在同一段内存空间中,创建水分配了消息空间呵消息队列的容量,无法更改。

发送一般消息:

若队列未满或者允许覆盖,将信息拷贝到队尾。否则,根据指定的超时时间阻塞。
若等到队列未满,或者超过了阻塞时间,则从阻塞状态转换为就绪状态,后者会收到错误码。

发送紧急消息,消息会被复制到消息队列队头。

  • 读取消息队列

    如果队列为空,任务保持阻塞状态。队列不空时,自动转变为就绪态。超过了阻塞时间自动转变为就绪态。

  • 在中断总发消息不允许带有阻塞机制

    中断必须快速响应和硬件中断
    任务调度器不会在中断中运行
    高优先级中断可能要更快地执行

如果在中断过程中更高优先级的任务被唤醒,中断返回后会返回更高优先级的任务。
消息队列创建:创建一个消息队列返回这个队列的队列句柄。
每创建一个新的队列需要对其分配RAM,存储状态和队列信息。
xQueueCreate():
使用动态内存分配,需将configSUPPORT_DYNAMIC_ALLOCATION 置1,(FreeRTOSConfig.h)
xQueueCreateStatic() :静态内存分配

prvInitialiseNewQueue()

参数:消息队列长度,单个消息大小,存储消息起始地址
消息队列类型
未分配存储消息的内存空间: 单个消息大小为0时将pchead指向队列控制块 pxNewQueue,因为消息队列用作互斥量时pchead设置为NULL。
分配了存储消息的内存空间: pcHead 指向存储消息的
起始地址 pucQueueStorage。
初始化消息队列控制块->重置消息队列

重置消息队列 xQueueGenericReset()

进入临界段是为了确保多任务并发环境中的数据一致性
需要恢复发送阻塞消息队列,便于数据发送

队列创建

队列创建时也需要进入临界区:临界区锁定的中断屏蔽
内存分配和指针初始化不能被打断,被错误的初始化或访问。导致内存访问错误或者队列崩溃。

通用消息队列发送函数 xQueueGenericSend()

队列未满-》添加信息

恢复等待消息阻塞的任务

  1. 若恢复的任务优先级比当前任务高-》切换任务
  2. 如果没有等待的任务,拷贝成功也需要任务切换

队列已满-》

不指定阻塞超时时间,退出,返回错误
初始化阻塞超时结构体变量,初始化进入

结束之后

检查是否超时
如果队列还是满的-》添加任务到等待发送列表中和

接受信息xQueueGenericReceive()

从队列中接收消息并把消息从队列中删除
接收的信息以拷贝的形式存在,必须提供一个足够大的缓存区(中断时必须使用特定的函数)

设置阻塞时间,若队列为空,任务保持阻塞状态。若等待时间超过两万指定的阻塞时间,直接从阻塞状态转化成就绪态。

挂起任务调度器和进入临界区

在将任务设置为阻塞的过程中,挂起调度器并进入临界区
挂起调度器意味着任务不能切换并且不准调用可能引起任务切换的 API 函数。 但挂起调度器并不会禁止中断,中断服务函数
仍然可以操作队列事件列表,可能会解除任务阻塞、可能会进行上下文切换,这也是不允许的。解决办法是还要给队列上锁,禁止任何中断来操作队列

假设有一个高优先级任务 A 和一个低优先级任务 B。任务 A 想入队,但队列已满,任务 A 将进入阻塞状态,并等待队列空闲。如果在任务 A 被正确地阻塞并添加到等待列表之前,低优先级任务 B 或者中断修改了队列的状态,它可能抢先完成入队操作

互斥量

递归获取信号量时可能出现死锁的情况:

  • 信号量用完:
  • 阻塞 vs 死锁:

    单次获取信号量且信号量不可用,任务会进入阻塞状态,此时系统能够恢复正常运行。然而,如果任务递归获取信号量,并且没有释放信号量,导致信号量完全用尽,那么任务自身将无法继续执行,且也无法释放之前获取的信号量,这就形成了死锁。此时,没有其他任务可以释放信号量,系统无法恢复,导致死锁。

使用临界区资源的方法:
获取并释放信号量
xSemaphoreTake()xSemaphoreGive()
挂起任务调度器 vTaskSuspendAll();xTaskResumeAll();
禁用中断
taskENTER_CRITICAL()和 taskEXIT_CRITICAL()