更新时间:2021-11-01 11:15:42 来源:极悦 浏览1078次
数据队列用于将数据包从 RF 内核传输到主 CPU,反之亦然。它们被实现为队列条目的链表。本文档描述了可用的队列条目类型,并提供了用于创建队列并与之交互的代码片段。
RF 内核支持 4 种不同类型的队列条目:
姓名 |
数据类型 |
描述 |
---|---|---|
单包入口 |
rfc_dataEntryGeneral_t |
每个条目一包;标头后的数据。 |
多包入口 |
rfc_dataEntryMulti_t |
同一条目中有多个数据包,数据在头部之后。 |
指针入口 |
rfc_dataEntryPointer_t |
每包一个条目;另一个内存位置的数据。 |
部分入场 |
rfc_dataEntryPartial_t |
存储未知或无限长度的数据包;标头后的数据。 |
只有部分条目可用于传输数据。所有其他类型都专门与 RX 命令一起使用。
所有队列条目都以一个公共头部分开始,包含一个指向下一个条目的指针、一个配置字段和条目的长度(以字节为单位)。
每个队列条目与 4 字节边界对齐非常重要。否则 RF 核心将无法访问队列条目。
单个数据包条目每个条目包含一个数据包,并将数据直接存储在标头后面,以便可以将整个队列分配在线性存储部分中。它们是最简单的条目类型,足以满足许多应用程序的需求。数据部分可能包含由 配置的数据包长度、 config.lenSz有效载荷和可选的附加元数据,如 RX 时间戳或 CRC。
当可以另外确定分组大小时,可以省略数据段开头的长度指示符。对于可变长度的数据包,这是必要的。
指针条目类似于单个数据包条目,但不包含标头之后的数据。相反,它持有一个指向包含数据的另一个内存位置的指针。当您想执行以下操作时,此队列类型很有用:
将数据存储与队列条目分开
在不同队列之间共享相同的缓冲区,而无需重新创建队列结构
将缓冲区移动到另一个队列(例如自定义队列实现)而不复制内容
专有 PHY 支持一种入口类型,在通过空中接收整个数据包之前可以访问数据。它可用于以下目的:
在接收整个数据包之前必须读取数据时。例如,当数据包包含与支持的数据包格式不兼容的长度字段时 。
当在数据包的开头不知道数据包的长度时。
当数据包的长度对于单个数据包条目太长或使用无限数据包长度时。
部分条目可能包含多个数据包。在这种情况下,数据部分中的每个数据包都以大小为lenSz的长度字段开始,该字段可用于计算同一条目中下一个数据包的开始。该字段nextIndex包含条目中写入的总字节数。
为了创建数据队列,需要执行以下步骤:
1.为所有队列条目分配足够的内存。
2.初始化每个队列条目头。
3.创建队列对象
该类型的对象dataQueue_t在头文件中定义<ti/devices/${DEVICE_FAMILY}/driverlib/rf_mailbox.h>;
// The queue structure
typedef struct {
uint8_t *pCurrEntry; // Points to the first entry, NULL for an empty queue
uint8_t *pLastEntry; // Pointer to the last entry, NULL for a circular queue
} dataQueue_t;
它持有一个指向第一个和最后一个条目的指针,并且可以具有固定或无限大小。在前一种情况下pLastEntry指向最后一个条目。在循环队列中,pLastEntry是一个空指针。
下面的例子解释了如何创建一个包含 4 个单包条目的循环队列,最多可以存储 32 个字节。
1.第一步,我们需要为所有队列条目分配足够的内存:
#include <ti/devices/${DEVICE_FAMILY}/driverlib/rfc_data_entry.h>
#include <ti/devices/${DEVICE_FAMILY}/driverlib/rfc_mailbox.h>
#define BUFFER_ENTRIES 4
// Must be word-aligned
#define DATA_SECTION_SIZE 32
// -1: Do not count the dummy data byte in the entry
#define ENTRY_HEADER_SIZE (sizeof(rfc_dataEntryGeneral_t) - 1)
#define BUFFER_SIZE_BYTES (BUFFER_ENTRIES * (ENTRY_HEADER_SIZE + DATA_SECTION_SIZE))
// Align the buffer to word boundaries. Example for GCC.
uint8_t buffer[BUFFER_SIZE_BYTES] __attribute__ ((aligned (4)));
所有队列条目都需要字对齐。当预期的负载大小不是 4 的倍数时,选择下一个更大的字对齐值作为 DATA_SECTION_SIZE。
2.准备条目标题并关闭环:
rfc_dataEntryGeneral_t item = (rfc_dataEntryGeneral_t*)&buffer[0];
for (uint8_t i = 0; i < BUFFER_ENTRIES; i++)
{
item->config.type = DATA_ENTRY_TYPE_GEN;
item->length = DATA_SECTION_SIZE; // Note: When creating partial items, add 4
// bytes for the additional header fields.
item->status = DATA_ENTRY_PENDING;
item->pNextEntry = ((uint8_t*)item) + ENTRY_HEADER_SIZE + DATA_SECTION_SIZE;
if (i == (elements - 1))
{
// Close the circle for the last item
item->pNextEntry = buffer;
}
item = (rfc_dataEntryGeneral_t*)item->pNextEntry;
}
3.创建一个循环队列对象:
dataQueue_t queue = {
.pCurrEntry = &buffer[0];
.pLastEntry = NULL;
};
每当 RF 内核完成一个数据包时,它就会引发中断IRQ_RX_ENTRY_DONE,该中断 映射到 RF 驱动程序中的事件RF_EventRxEntryDone。在处理中断和执行回调时,数据包数据从入口读取,status必须重新设置该字段 DATA_ENTRY_PENDING,RF 内核才能重新使用它。
将当前条目保存在变量中:
// 初始化为第一个条目
rfc_dataEntryGeneral_t * rxEntry = ( rfc_dataEntryGeneral_t * ) queue . pCurrEntry ;
从 RX 队列中的单个数据包条目中读取数据包。这可以在 RF 驱动程序回调中或在 RX 任务中完成:
// 要读取的当前条目。
rfc_dataEntryGeneral_t * rxEntry ;
// 数据包以 1 字节长度信息开始 (lenSz = 1)
uint8_t packetLength = * ( uint8_t * )( & rxEntry -> data );
// 有效载荷如下。
uint8_t * packetDataPointer = ( uint8_t * )( & rxEntry -> data + sizeof ( packetLength ));
// 正确:使用 memcpy 从缓冲区读取负载。
uint32_t myValue;
memcpy ( & myValue , packetDataPointer , sizeof ( myValue ));
// 危险:取消引用数据包有效载荷中可能未对齐的指针:
// myValue = *((uint32_t*)packetDataPointer);
设置rxEntry为下一次迭代的下一个队列项:
// 将条目标记为正在读取
(( volatile rfc_dataEntryGeneral_t * ) rxEntry ) -> status = DATA_ENTRY_PENDING ;
// 获取下一个条目
rfc_dataEntryGeneral_t * rxEntry = (( rfc_dataEntryGeneral_t * ) rxEntry -> pNextEntry );
以上就是关于“数据队列的使用方法”的介绍,大家如果想了解更多相关知识,不妨来关注一下极悦的Java极悦在线学习,里面有更丰富的知识在等着大家,里面的内容全面,通俗易懂,适合小白学习,希望对大家能够有所帮助。
0基础 0学费 15天面授
Java就业班有基础 直达就业
业余时间 高薪转行
Java在职加薪班工作1~3年,加薪神器
工作3~5年,晋升架构
提交申请后,顾问老师会电话与您沟通安排学习