在freertos中,队列是实现任务之间同步、互斥和通信的一种重要方法(其他的实现方法有:任务通知、事件组、信号量、互斥量)。
任何任务都可以向队列里存放任何数据,任何任务也可以从队列里读取数据,实现不同任务之间的通信。
1
队列特性
队列的数据的操作采用先进先出的方法(fifo,first in first out):写数据时放到尾部,读数据时从头部读,逻辑顺序如下图所示。
使用队列传输数据时有两种方法:
拷贝:把数据、把变量的值复制进队列里引用:把数据、把变量的地址复制进队列里freertos中的队列一般都使用拷贝的方式传输数据,局部变量的值可以发送到队列中,后续即使函数退出、局部变量被回收,也不会影响队列中的数据,发送任务、接收任务解耦时,接收任务不需要知道这数据是谁的、也不需要发送任务来释放数据。
如果数据实在太大,还是可以使用队列传输它的地址。
2
队列函数
1.创建
队列的创建有两种方法:动态分配内存、静态分配内存。
一般都用动态分配内存的方法,使用函数:xqueuecreate()
queuehandle_t xqueuecreate( ubasetype_t uxqueuelength, ubasetype_t uxitemsize );参数解释:
uxqueuelength :队列长度uxitemsize:每个数据的大小,以字节为单位返回值:非0:成功,返回句柄,以后使用句柄来操作队列;null:失败,因为内存不足2.删除
删除队列的函数为 vqueuedelete() ,只能删除使用动态方法创建的队列,它会释放内存。
void vqueuedelete( queuehandle_t xqueue );参数解释:
xqueue:队列句柄3.写队列
可以把数据写到队列头部,也可以写到尾部,这些函数有两个版本:在任务中使用、在 isr 中使用。
在任务中使用:
basetype_t xqueuesend( queuehandle_t xqueue,const void *pvitemtoqueue,ticktype_t xtickstowait );在isr中使用:
basetype_t xqueuesendtobackfromisr( queuehandle_t xqueue,const void *pvitemtoqueue,basetype_t *pxhigherprioritytaskwoken );参数解释:
xqueue :队列句柄,要写哪个队列pvitemtoqueue : 数据指针,这个数据的值会被复制进队列xtickstowait :如果队列满则无法写入新数据,可以让任务进入阻塞状态,xtickstowait表示阻塞的最大时间(tick count)。如果被设为0,无法写入数据时函数会立刻返回;如果被设为portmax_delay,则会一直阻塞直到有空间可写返回值:pdpass:数据成功写入了队列;errqueue_full:写入失败,因为队列满了。4.读队列
使用 xqueuereceive() 函数读队列,读到一个数据后,队列中该数据会被移除。这个函数有两个版 本:在任务中使用、在isr 中使用。
basetype_t xqueuereceive( queuehandle_t xqueue, void * const pvbuffer, ticktype_t xtickstowait );basetype_t xqueuereceivefromisr( queuehandle_t xqueue, void *pvbuffer, basetype_t *pxtaskwoken );参数解释:
xqueue :队列句柄,要写哪个队列pvbuffffer: bufer 指针,队列的数据会被复制到这个 bufferxtickstowait :如果队列空则无法读出数据,可以让任务进入阻塞状态,xtickstowait表示阻塞的最大时间(tick count)。如果被设为0,无法读出数据时函数会立刻返回;如果被设为portmax_delay,则会一直阻塞直到有数据可写返回值:pdpass:从队列读出数据入;errqueue_empty:读取失败,因为队列空了。5.其他
复位:队列刚被创建时,里面没有数据;使用过程中可以调用 xqueuereset() 把队列恢复为初始状态。
/* pxqueue : 复位哪个队列; * 返回值: pdpass(必定成功) */ basetype_t xqueuereset( queuehandle_t pxqueue);查询:可以查询队列中有多少个数据、有多少空余空间。
/** 返回队列中可用数据的个数 */ ubasetype_t uxqueuemessageswaiting( const queuehandle_t xqueue ); /** 返回队列中可用空间的个数 */ ubasetype_t uxqueuespacesavailable( const queuehandle_t xqueue );覆盖:当队列长度为 1 时,可以使用 xqueueoverwrite() 或 xqueueoverwritefromisr() 来覆盖数据。注意,队列长度必须为1。当队列满时,这些函数会覆盖里面的数据,这也以为着这些函数不会被阻塞。
/* 覆盖队列 * xqueue: 写哪个队列 * pvitemtoqueue: 数据地址 * 返回值: pdtrue表示成功, pdfalse表示失败 */ basetype_t xqueueoverwrite(queuehandle_t xqueue, const void * pvitemtoqueue ); basetype_t xqueueoverwritefromisr( queuehandle_t xqueue, const void * pvitemtoqueue, basetype_t *pxhigherprioritytaskwoken );偷看:如果想让队列中的数据供多方读取,也就是说读取时不要移除数据,要留给后来人。那么可以使用 窥 视 ,也就是 xqueuepeek() 或 xqueuepeekfromisr() 。这些函数会从队列中复制出数据,但是不移除数据。这也意味着,如果队列中没有数据,那么 偷看 时会导致阻塞;一旦队列中有数据,以后每次 偷看 都会成功。
/* 偷看队列 * xqueue: 偷看哪个队列 * pvitemtoqueue: 数据地址, 用来保存复制出来的数据 * xtickstowait: 没有数据的话阻塞一会 * 返回值: pdtrue表示成功, pdfalse表示失败 */ basetype_t xqueuepeek( queuehandle_t xqueue, void * const pvbuffer, ticktype_t xtickstowait );basetype_t xqueuepeekfromisr( queuehandle_t xqueue, void *pvbuffer, );3
队列实验
代码:
/* vsendertask被用来创建2个任务,用于写队列 * vreceivertask被用来创建1个任务,用于读队列 */static void vsendertask( void *pvparameters );static void vreceivertask( void *pvparameters );/*-----------------------------------------------------------*//* 队列句柄, 创建队列时会设置这个变量 */queuehandle_t xqueue;int main( void ){ prvsetuphardware(); /* 创建队列: 长度为5,数据大小为4字节(存放一个整数) */ xqueue = xqueuecreate( 5, sizeof( int32_t ) ); if( xqueue != null ) { /* 创建2个任务用于写队列, 传入的参数分别是100、200 * 任务函数会连续执行,向队列发送数值100、200 * 优先级为1 */ xtaskcreate( vsendertask, sender1, 1000, ( void * ) 100, 1, null ); xtaskcreate( vsendertask, sender2, 1000, ( void * ) 200, 1, null ); /* 创建1个任务用于读队列 * 优先级为2, 高于上面的两个任务 * 这意味着队列一有数据就会被读走 */ xtaskcreate( vreceivertask, receiver, 1000, null, 2, null ); /* 启动调度器 */ vtaskstartscheduler(); } else { /* 无法创建队列 */ } /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */ return 0;}/*-----------------------------------------------------------*//*-----------------------------------------------------------*/static void vsendertask( void *pvparameters ){ int32_t lvaluetosend; basetype_t xstatus; /* 我们会使用这个函数创建2个任务 * 这些任务的pvparameters不一样 */ lvaluetosend = ( int32_t ) pvparameters; /* 无限循环 */ for( ;; ) { /* 写队列 * xqueue: 写哪个队列 * &lvaluetosend: 写什么数据? 传入数据的地址, 会从这个地址把数据复制进队列 * 0: 不阻塞, 如果队列满的话, 写入失败, 立刻返回 */ xstatus = xqueuesendtoback( xqueue, &lvaluetosend, 0 ); if( xstatus != pdpass ) { printf( could not send to the queue.rn ); } }}/*-----------------------------------------------------------*/static void vreceivertask( void *pvparameters ){ /* 读取队列时, 用这个变量来存放数据 */ int32_t lreceivedvalue; basetype_t xstatus; const ticktype_t xtickstowait = pdms_to_ticks( 100ul ); /* 无限循环 */ for( ;; ) { /* 读队列 * xqueue: 读哪个队列 * &lreceivedvalue: 读到的数据复制到这个地址 * xtickstowait: 如果队列为空, 阻塞一会 */ xstatus = xqueuereceive( xqueue, &lreceivedvalue, xtickstowait ); if( xstatus == pdpass ) { /* 读到了数据 */ printf( received = %drn, lreceivedvalue ); } else { /* 没读到数据 */ printf( could not receive from the queue.rn ); } }}在这个程序中,有一个接收队列数据的任务,两个发送队列数据的任务,接收队列数据的任务优先级高,先执行,但是这时队列为空,触发该任务阻塞,这时低优先级的任务交替执行,向队列中发送数据,接收任务发现队列不为空后(解除触发的事件),立刻被唤醒从队列中读取数据并打印出来,实验结果和逻辑图如下:
机器人自动化工程项目方案设计包括的6个步骤解析
嵌入式Linux下的Socket CAN驱动理解
如何轻松稳定带感性开环输出阻抗的运算放大器
Linux 5.4内核正式版本有哪些新功能
虹科案例 | 温控无忧!虹科Comet创新产品助力va-Q-tec实现温度敏感产品运输过程质量控制温控无忧!
基于FreeRTOS的STM32F103系统—队列
iphone13pro尺寸参数详情,iphone13pro价格
新一代君威GS上市时间:7月21日(即本周五)将于与普通版同步上市!
电池常见测试项目有哪些?
海信双屏手机A6评测 在阅读时能够更好地保护我们的双眼
相控阵天线方向图:栅瓣和波束斜视
薄膜电容尺寸误差一般是多大
华为秋季全场景新品发布会:刘德华来了
专业以太网卡在民用网络中的应用
AIGC繁花,绽放在精耕的算力土壤之上
九联科技Cat.1模组赋能共享充电宝 助力共享经济快速发展
FLIR热成像区域数据集助力自动驾驶汽车测试
小创公司如何在工业物联网的冲击中与巨头竞争
中国移动将获5G牌照 超高速无线技术
紫外光刻机(桌面型掩膜对准)