数据传输方法
环形buffer
环形buffer的本质就是一个循环队列,但是有一些不同
- 空:当头指针和尾指针相等时,表示缓冲区为空。
- 满:当尾指针的下一个位置等于头指针时,表示缓冲区已满(在环形结构中,尾指针要循环到数组的开始位置)。
队列
队列的本质
在嵌入式中队列运行就像一个流水线工作,其中有两个工人
A的作用
(1)写队列
(2)如果队列已满则可以进行对任务A处理(具体是把任务A放入SenderList链表以及阻塞Block链表)
(3)任务B如何被唤醒,要么由任务A唤醒任务B,要么结束阻塞,通过Tick中断唤醒B,把任务B放入ReadyList
B的作用
(1)查看流水线有无任务(产品),没有的话将进行阻塞(眯一会)(相当于把任务放入Receiverlist链表以及阻塞Block链表)
(2)读队列
(3)任务B读队列,唤醒任务A,通过Tick中断唤醒A,把任务A放入ReadyList
队列的阻塞访问
只要知道队列的句柄,谁都可以读、写该队列。任务、ISR 都可读、写队列。可以多个
任务读写队列。
任务读写队列时,简单地说:如果读写不成功,则阻塞;可以指定超时时间。口语化地
说,就是可以定个闹钟:如果能读写了就马上进入就绪态,否则就阻塞直到超时。
某个任务读队列时,如果队列没有数据,则该任务可以进入阻塞状态:还可以指定阻塞
的时间。如果队列有数据了,则该阻塞的任务会变为就绪态。如果一直都没有数据,则时间
到之后它也会进入就绪态。
既然读取队列的任务个数没有限制,那么当多个任务读取空队列时,这些任务都会进入
阻塞状态:有多个任务在等待同一个队列的数据。当队列中有数据时,哪个任务会进入就绪
态?
⚫ 优先级最高的任务⚫ 如果大家的优先级相同,那等待时间最久的任务会进入就绪态
跟读队列类似,一个任务要写队列时,如果队列满了,该任务也可以进入阻塞状态:还
可以指定阻塞的时间。如果队列有空间了,则该阻塞的任务会变为就绪态。如果一直都没有
空间,则时间到之后它也会进入就绪态。
既然写队列的任务个数没有限制,那么当多个任务写 " 满队列 " 时,这些任务都会进入阻
塞状态:有多个任务在等待同一个队列的空间。当队列中有空间时,哪个任务会进入就绪态?
⚫ 优先级最高的任务⚫ 如果大家的优先级相同,那等待时间最久的任务会进入就绪态
队列函数
创建
队列的创建有两种方法:动态分配内存、静态分配内存
动态分配内存
动态分配内存: xQueueCreate ,队列的内存在函数内部动态分配QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );
参数说明:
- uxQueueLength:队列中可以存放的最大项数(队列长度)。
- uxItemSize:每个队列项的大小(以字节为单位)。如果要存储的项是结构体或者其他数据类型,确保传入正确的大小。
返回值:
- 返回一个队列句柄(
QueueHandle_t
),如果创建成功,句柄有效;如果失败,返回NULL
。
静态分配内存
静态分配内存: xQueueCreateStatic ,队列的内存要事先分配好QueueHandle_t xQueueCreateStatic(UBaseType_t uxQueueLength,UBaseType_t uxItemSize,uint8_t *pucQueueStorageBuffer,StaticQueue_t *pxQueueBuffer);
参数说明:
- uxQueueLength:队列可以存放的最大项数(队列长度)。
- uxItemSize:每个队列项的大小(以字节为单位)。
- pucQueueStorageBuffer:指向用于存储队列项的缓冲区的指针。这个缓冲区的大小应该为
uxQueueLength * uxItemSize
。- pxQueueBuffer:指向一个
StaticQueue_t
结构体的指针,用于存储队列的控制结构。此结构体在 FreeRTOS 中被用于管理队列的状态。返回值:
- 返回一个队列句柄(
QueueHandle_t
),如果创建成功,句柄有效;如果失败,返回NULL
。
写队列
/* 等同于xQueueSendToBack * 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait */ BaseType_t xQueueSend(QueueHandle_t xQueue,const void *pvItemToQueue,TickType_t xTicksToWait); /* * 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait */ BaseType_t xQueueSendToBack(QueueHandle_t xQueue,const void *pvItemToQueue,TickType_t xTicksToWait); /* * 往队列尾部写入数据,此函数可以在中断函数中使用,不可阻塞 */ BaseType_t xQueueSendToBackFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken); /* * 往队列头部写入数据,如果没有空间,阻塞时间为xTicksToWait */ BaseType_t xQueueSendToFront(QueueHandle_t xQueue,const void *pvItemToQueue,TickType_t xTicksToWait); /* * 往队列头部写入数据,此函数可以在中断函数中使用,不可阻塞 */ BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);
参数说明:
- xQueue:要发送项的队列句柄。
- pvItemToQueue:指向要发送到队列的项的指针。此项的数据类型应与队列在创建时指定的数据类型匹配。
- xTicksToWait:如果队列已满,调用任务将等待的时间(以滴答数为单位)。可以使用
portMAX_DELAY
表示无限等待,或使用0
表示不等待。返回值:
- 返回值为
BaseType_t
,表示发送操作的结果。可能的返回值包括:
pdPASS
:成功将项发送到队列。errQUEUE_FULL
:队列已满,未能发送项(且没有设置等待时间或等待时间已到)。static int IRReceiver_IRQTimes_Parse(void) {uint64_t time;int i;int m, n;unsigned char datas[4];unsigned char data = 0;int bits = 0;int byte = 0;struct input_data idata;/* 1. 判断前导码 : 9ms的低脉冲, 4.5ms高脉冲 */time = g_IRReceiverIRQ_Timers[1] - g_IRReceiverIRQ_Timers[0];if (time < 8000000 || time > 10000000){return -1;}time = g_IRReceiverIRQ_Timers[2] - g_IRReceiverIRQ_Timers[1];if (time < 3500000 || time > 55000000){return -1;}/* 2. 解析数据 */for (i = 0; i < 32; i++){m = 3 + i*2;n = m+1;time = g_IRReceiverIRQ_Timers[n] - g_IRReceiverIRQ_Timers[m];data <<= 1;bits++;if (time > 1000000){/* 得到了数据1 */data |= 1;}if (bits == 8){datas[byte] = data;byte++;data = 0;bits = 0;}}/* 判断数据正误 */datas[1] = ~datas[1];datas[3] = ~datas[3];if ((datas[0] != datas[1]) || (datas[2] != datas[3])){g_IRReceiverIRQ_Cnt = 0;return -1;}//PutKeyToBuf(datas[0]);//PutKeyToBuf(datas[2]);/* 写队列 */idata.dev = datas[0];if (datas[2] == 0xe0)idata.val = UPT_MOVE_LEFT;else if (datas[2] == 0x90)idata.val = UPT_MOVE_RIGHT;elseidata.val = UPT_MOVE_NONE;g_last_val = idata.val;xQueueSendFromISR(g_xQueuePlatform, &idata, NULL);return 0; }
读队列
BaseType_t xQueueReceive( QueueHandle_t xQueue,void * const pvBuffer,TickType_t xTicksToWait ); BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue,void *pvBuffer,BaseType_t *pxTaskWoken);
1.
xQueueReceive
参数说明:
xQueue
:要从中接收数据的队列句柄。pvBuffer
:指向接收数据存储位置的指针。接收到的数据将存储在这里。xTicksToWait
:如果队列为空,任务等待的最大时间(以滴答为单位)。可以使用portMAX_DELAY
表示无限等待。返回值:
- 如果成功接收数据,返回
pdPASS
;如果超时或发生错误,返回errQUEUE_EMPTY
。2.
xQueueReceiveFromISR
参数说明:
xQueue
:要从中接收数据的队列句柄。pvBuffer
:指向接收数据存储位置的指针。pxTaskWoken
:指向BaseType_t
类型的指针,如果接收数据后需要唤醒一个任务,设置为pdTRUE
,否则设置为pdFALSE
。返回值:
- 与
xQueueReceive
相似,成功时返回pdPASS
,失败时返回errQUEUE_EMPTY
。/* 挡球板任务 */ static void platform_task(void *params) {byte platformXtmp = platformX; uint8_t dev, data, last_data;struct input_data idata;// Draw platformdraw_bitmap(platformXtmp, g_yres - 8, platform, 12, 8, NOINVERT, 0);draw_flushArea(platformXtmp, g_yres - 8, 12, 8);while (1){//if (0 == IRReceiver_Read(&dev, &data))xQueueReceive(g_xQueuePlatform, &idata, portMAX_DELAY);uptMove = idata.val;// Hide platformdraw_bitmap(platformXtmp, g_yres - 8, clearImg, 12, 8, NOINVERT, 0);draw_flushArea(platformXtmp, g_yres - 8, 12, 8);// Move platformif(uptMove == UPT_MOVE_RIGHT)platformXtmp += 3;else if(uptMove == UPT_MOVE_LEFT)platformXtmp -= 3;uptMove = UPT_MOVE_NONE;// Make sure platform stays on screenif(platformXtmp > 250)platformXtmp = 0;else if(platformXtmp > g_xres - PLATFORM_WIDTH)platformXtmp = g_xres - PLATFORM_WIDTH;// Draw platformdraw_bitmap(platformXtmp, g_yres - 8, platform, 12, 8, NOINVERT, 0);draw_flushArea(platformXtmp, g_yres - 8, 12, 8);platformX = platformXtmp; } }
注意
写队列就是在中断中写取数据,在进行复杂的操作时,连接一个中间的任务处理数据然后再提交
/* 挡球板任务 */ static void platform_task(void *params) {byte platformXtmp = platformX; uint8_t dev, data, last_data;struct input_data idata;// Draw platformdraw_bitmap(platformXtmp, g_yres - 8, platform, 12, 8, NOINVERT, 0);draw_flushArea(platformXtmp, g_yres - 8, 12, 8);while (1){//if (0 == IRReceiver_Read(&dev, &data))xQueueReceive(g_xQueuePlatform, &idata, portMAX_DELAY);uptMove = idata.val;// Hide platformdraw_bitmap(platformXtmp, g_yres - 8, clearImg, 12, 8, NOINVERT, 0);draw_flushArea(platformXtmp, g_yres - 8, 12, 8);// Move platformif(uptMove == UPT_MOVE_RIGHT)platformXtmp += 3;else if(uptMove == UPT_MOVE_LEFT)platformXtmp -= 3;uptMove = UPT_MOVE_NONE;// Make sure platform stays on screenif(platformXtmp > 250)platformXtmp = 0;else if(platformXtmp > g_xres - PLATFORM_WIDTH)platformXtmp = g_xres - PLATFORM_WIDTH;// Draw platformdraw_bitmap(platformXtmp, g_yres - 8, platform, 12, 8, NOINVERT, 0);draw_flushArea(platformXtmp, g_yres - 8, 12, 8);platformX = platformXtmp; } }static void RotaryEncoderTask(void *params) {struct rotary_data rdata;struct input_data idata;int left;int i, cnt;while (1){/* 读旋转编码器队列 */xQueueReceive(g_xQueueRotary, &rdata, portMAX_DELAY);/* 处理数据 *//* 判断速度: 负数表示向左转动, 正数表示向右转动 */if (rdata.speed < 0){left = 1;rdata.speed = 0 - rdata.speed;}else{left = 0;}//cnt = rdata.speed / 10;//if (!cnt)// cnt = 1;if (rdata.speed > 100)cnt = 4;else if (rdata.speed > 50)cnt = 2;elsecnt = 1;/* 写挡球板队列 */idata.dev = 1;idata.val = left ? UPT_MOVE_LEFT : UPT_MOVE_RIGHT;for (i = 0; i < cnt; i++){xQueueSend(g_xQueuePlatform, &idata, 0);}} }