目录
动态分配内存:xQueueCreate,队列的内存在函数内部动态分配
静态分配内存:xQueueCreateStatic,队列的内存要事先分配好
1.环形缓冲区
多种方法比较:

使用全局变量的缺点,CPU效率降低,而且变量之间传递的数据还可能是错误的
环形缓冲区在两个任务之间的数据传递是没有问题的,但是,依然会有使得CPU效率降低的缺点
环形缓冲区的定义
初始化(环形缓冲区空)
环形缓冲区空:r = w
环形缓冲区满
环形缓冲区空:下下次w的位置(next_w) = r
写操作
读操作
2.队列的本质
队列中,数据的读写本质就是环形缓冲区,在这个基础上增加了互斥措施、阻塞-唤醒机制
队列里面会有三个变量,写位置(int w)、读位置(int r)、数据个数(int num)
写队列的时候会把数据拷贝进去让数据个数累加,读队列的时候会从里面得到数据让数据个数减1
如果这个队列不传输数据,只调整"数据个数",它就是信号量(semaphore)如果信号量中,限定"数据个数"最大值为1,它就是互斥量(mutex)
队列的创建有两种方法:动态分配内存、静态分配内存
动态分配内存:xQueueCreate,队列的内存在函数内部动态分配
函数原型如下:
静态分配内存:xQueueCreateStatic,队列的内存要事先分配好
函数原型如下:
队列的复位
队列刚被创建时,里面没有数据;使用过程中可以调用 xQueueReset()把队列恢复为初始状态
函数原型为:
队列的删除
删除队列的函数为 vQueueDelete(),只能删除使用动态方法创建的队列,它会释放内存
原型如下:
写队列
可以把数据写到队列头部,也可以写到尾部,这些函数有两个版本:在任务中使用、在ISR 中使用
函数原型如下:
这些函数用到的参数是类似的,统一说明如下:
读队列
使用 xQueueReceive()函数读队列,读到一个数据后,队列中该数据会被移除。这个函数有两个版本:在任务中使用、在 ISR 中使用
函数原型如下:
参数说明如下:
队列的查询
可以查询队列中有多少个数据、有多少空余空间
函数原型如下:
队列的覆盖/偷看
当队列长度为 1 时,可以使用 xQueueOverwrite()或 xQueueOverwriteFromISR() 来覆盖数据
注意,队列长度必须为 1。当队列满时,这些函数会覆盖里面的数据,这也以为着这些函数不会被阻塞
函数原型如下:
如果想让队列中的数据供多方读取,也就是说读取时不要移除数据,要留给后来人。那么可以使用"窥视",也就是xQueuePeek()或xQueuePeekFromISR()。这些函数会从队列中复制出数据,但是不移除数据。这也意味着,如果队列中没有数据,那么"偷看"时会导致阻塞;一旦队列中有数据,以后每次"偷看"都会成功
函数原型如下:
队列的步骤
创建队列
写队列
读队列
队列的两大要素
1.环形缓冲区
2.两个链表
写链表
读链表
队列本质的理解
假设任务A是写队列的,任务B是读队列的
读队列
1.观察队列里有没有数据,无数据进入阻塞状态,从就绪链表进入阻塞链表和读链表中
2.唤醒方式
当队列里有数据时,任务A通过读链表唤醒任务B,任务B就从阻塞链表和读链表中进入就绪链表
如果队列里一直没有数据,那么Tick中断唤醒任务B,任务B就从阻塞链表和读链表中进入就绪链表
写队列
1.队列里有没有数据,写数据,当队列数据满了后,进入阻塞状态,从就绪链表进入阻塞链表和写链表中
2.唤醒方式
任务B读取了一个数据后,任务B通过写链表唤醒任务A,任务A就从阻塞链表和写链表中进入就绪链表
如果队列里的数据一直是满的,那么Tick中断唤醒任务A,任务A就从阻塞链表和写链表中进入就绪链表





























