目录
一、概述
1、数据存储
2、共享特性
3、读队列
4、写队列
二、函数介绍
1、队列创建
2、队列发送
3、队列接收
三、队列应用
四、队列发送指针应用
五、柔性数组与队列的高级应用
1、柔性数组
2、队列中的高级应用
队列可以保存有限个具有确定长度的数据单元。队列可以保存的最大单元数目被称为队列的“深度”。在队列创建时需要设定其深度和每个单元的大小。通常情况下,队列被作为 FIFO(先进先出)使用,即数据由队列尾写入,从队列首读出。
队列是具有自己独立权限的内核对象,并不属于或赋予任何任务。所有任务都可以向同一队列写入和读出。一个队列由多方写入是经常的事,但由多方读出倒是很少遇到。
当某个任务试图读一个队列时,其可以指定一个阻塞超时时间。在这段时间中,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。当其它任务或中断服务例程往其等待的队列中写入了数据,该任务将自动由阻塞态转移为就绪态。当等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转移为就绪态。由于队列可以被多个任务读取,所以对单个队列而言,也可能有多个任务处于阻塞状态以等待队列数据有效。这种情况下,一旦队列数据有效,只会有一个任务会被解除阻塞,这个任务就是所有等待任务中优先级最高的任务。而如果所有等待任务的优先级相同,那么被解除阻塞的任务将是等待最久的任务。
同读队列一样,任务也可以在写队列时指定一个阻塞超时时间。这个时间是当被写队列已满时,任务进入阻塞态以等待队列空间有效的最长时间。由于队列可以被多个任务写入,所以对单个队列而言,也可能有多个任务处于阻塞状态以等待队列空间有效。这种情况下,一旦队列空间有效,只会有一个任务会被解除阻塞,这个任务就是所有等待任务中优先级最高的任务。而如果所有等待任务的优先级相同,那么被解除阻塞的任务将是等待最久的任务。
QueueHandle_t xQueueGenericCreate(
const UBaseType_t uxQueueLength, //队列能够存储的最大单元数目,即队列深度。
const UBaseType_t uxItemSize //队列中数据单元的长度,以字节为单位。
);
//返回类型
//NULL 表示没有足够的堆空间分配给队列而导致创建失败。
//非 NULL 值表示队列创建成功。
返回类型 QueueHandle_t
返回值 NULL 创建失败
非 NULL 值表示队列创建成功。表示队列的句柄。
BaseType_t xQueueGenericSend(
QueueHandle_t xQueue, //队列句柄
const void * const pvItemToQueue, //数据地址
TickType_t xTicksToWait, //阻塞时间
);
返回类型 BaseType_t
返回值 pdPASS 数据被成功发送到队列
errQUEUE_FULL 如 果 由 于 队 列 已 满 而 无 法 将 数 据 写 入 , 则 将 返 回errQUEUE_FULL。
BaseType_t xQueueReceive(
QueueHandle_t xQueue, //队列句柄
void * const pvBuffer, //接收数据buff
TickType_t xTicksToWait ///接收阻塞时间
)
返回类型 BaseType_t
返回值 pdPASS 成功地从队列中读到数据。
errQUEUE_FULL 如果在读取时由于队列已空而没有读到任何数据,则将返回errQUEUE_FULL。
本次例程会常见两个线程,其中一个线程负责队列发送,一个线程负责队列接收。完成整体的队列收发工作。
#include "main.h"
//创建队列句柄
QueueHandle_t Queue_Data_Handle;
#define queue_data_length 10
//宏定义 send_task
BaseType_t retA;
TaskHandle_t Pt_send_Task_TaskHandle;
void queue_sned_Task(void *p);
#define Sned_Task_Name "queue_send"
#define Sned_Task_StackD 128
#define Sned_Task_Priority 1
//宏定义 rece_task
BaseType_t retB;
TaskHandle_t Pt_rece_Task_TaskHandle;
void queue_receive_Task(void *p);
#define Rece_Task_Name "queue_rece"
#define Rece_Task_StackD 128
#define Rece_Task_Priority 1
int main(void)
{
bsp_Init();
/*创建队列 注意队列长度 和 数据字节大小*/
Queue_Data_Handle = xQueueCreate( (const UBaseType_t)queue_data_length, //队列长度
(const UBaseType_t)sizeof(uint32_t)); //单个数据大小四字节
/*创建发送任务*/
retA = xTaskCreate( (TaskFunction_t) queue_send_Task,
(const char *)Sned_Task_Name,
(uint16_t)Sned_Task_StackD,
(void *)NULL,
(UBaseType_t)Sned_Task_Priority,
(TaskHandle_t *)&Pt_send_Task_TaskHandle);
/*创建接收任务*/
retA = xTaskCreate( (TaskFunction_t) queue_receive_Task,
(const char *)Rece_Task_Name,
(uint16_t)Rece_Task_StackD,
(void *)NULL,
(UBaseType_t)Rece_Task_Priority,
(TaskHandle_t *)&Pt_rece_Task_TaskHandle);
/*开始调度*/
vTaskStartScheduler();
/*不会执行到这里*/
while (1) {
;
}
}
void queue_send_Task(void *p)
{
uint32_t send_value = 0;
while(1)
{
xQueueSend(Queue_Data_Handle, &value, portMAX_DELAY);
{ //如果BUFF满,则不会执行如下程序
send_value++;
if(send_value > 100)
{
send_value = 0;
}
vTaskDelay(100);
}
}
}
void queue_receive_Task(void *p)
{
uint32_t rece_value = 0;
while(1)
{
xQueueReceive(Queue_Data_Handle, &rece_value, portMAX_DELAY);
{
//如果BUFF空,则不会执行如下程序
printf("rece_value = %d\r\n", rece_value);
vTaskDelay(100);
}
}
}
在发送任务中,动态申请内存,然后把分配的这块内存指针头通过队列发送给接收队列任务,在实际的操作中,大内存数据适合通过指针发送。减少队列内存分配。
typedef struct{
uint32_t index;
uint32_t length;
uint8_t buff[128];
}rx_buff_data_t;
//创建队列句柄
QueueHandle_t Queue_Data_Handle;
#define queue_data_length 3
//宏定义 send_task
BaseType_t retA;
TaskHandle_t Pt_send_Task_TaskHandle;
void queue_sned_Task(void *p);
#define Sned_Task_Name "queue_send"
#define Sned_Task_StackD 128
#define Sned_Task_Priority 1
//宏定义 rece_task
BaseType_t retB;
TaskHandle_t Pt_rece_Task_TaskHandle;
void queue_receive_Task(void *p);
#define Rece_Task_Name "queue_rece"
#define Rece_Task_StackD 128
#define Rece_Task_Priority 1
int main(void)
{
bsp_Init();
/*创建队列 注意队列长度 和 数据字节大小*/
Queue_Data_Handle = xQueueCreate( (const UBaseType_t)queue_data_length, //队列长度
(const UBaseType_t)sizeof(rx_buff_data_t *));
/*创建发送任务*/
retA = xTaskCreate( (TaskFunction_t) queue_send_Task,
(const char *)Sned_Task_Name,
(uint16_t)Sned_Task_StackD,
(void *)NULL,
(UBaseType_t)Sned_Task_Priority,
(TaskHandle_t *)&Pt_send_Task_TaskHandle);
/*创建接收任务*/
retA = xTaskCreate( (TaskFunction_t) queue_receive_Task,
(const char *)Rece_Task_Name,
(uint16_t)Rece_Task_StackD,
(void *)NULL,
(UBaseType_t)Rece_Task_Priority,
(TaskHandle_t *)&Pt_rece_Task_TaskHandle);
/*开始调度*/
vTaskStartScheduler();
/*不会执行到这里*/
while (1) {
;
}
}
void queue_send_data_reset(rx_buff_data_t *p_data)
{ //重置数据
p_data->length = 0;
p_data->index = 0;
memset(p_data->buff, 0, sizeof(p_data->buff)); //清空数据
}
void queue_send_Task(void *p)
{
while(1)
{ //动态申请内存
rx_buff_data_t *p_data = (rx_buff_data_t *)pvPortMalloc(sizeof(rx_buff_data_t));
if(!p_data)
{
vTaskDelay(100);
continue;
}
//初始化分配内存数据
queue_send_data_reset(p_data);
//处理处理
for(uint32_t cnt = 0; cnt < 100; cnt++)
{
p_data->length++;
p_data->index = cnt;
p_data->buff[cnt] = cnt;
}
//发送数据
if(xQueueSend(Queue_Data_Handle, &p_data, portMAX_DELAY) != pdPASS)
{ //发送失败
vPortFree(p_data);
}
vTaskDelay(100);
}
}
void queue_receive_Task(void *p)
{
rx_buff_data_t *rece_value = NULL;
while(1)
{
if(xQueueReceive(Queue_Data_Handle, &rece_value, portMAX_DELAY) == pdPASS)
{
if(!rece_value)
{
vTaskDelay(100);
continue;
}
for(uint32_t cnt = 0; cnt < rece_value->length; cnt++)
{
printf("rece_value->buff[%d] = %d \r\n", cnt, rece_value->buff[cnt]);
}
//接收成功并释放
vPortFree(rece_value);
rece_value = NULL;
}
vTaskDelay(100);
}
}
如果要接收一个大内存的数据,而且是不定长的数据包,并且要通过队列发送与接收的话,应该怎么实现呢?我们可以定义一个柔性数组结构体,确定该柔性数组的长度和数据包,分配对应的内存。指针指向首地址,并通过队列发送与接收。
柔性数组是C99标准引入的一种特殊数组类型,允许在结构体的最后一个成员位置声明一个未指定大小的数组。这种设计主要用于实现动态内存管理,尤其在需要处理可变长度数据的场景中具有显著优势。结构体中最后一个未指定大小的数组不占用任何内存,不过需要进行malloc管理。
typedef struct {
int length;
int data[]; // 柔性数组
}FlexArray_t;
分配公式:总内存 = 结构体大小 + 柔性数组元素个数 × 元素大小
// 分配一个包含10个整数的柔性数组
FlexArray_t *FlexArray = malloc(sizeof(FlexArray_t) + 10 * sizeof(int));
flex->length = 10;
假设接收到的数据包不定长度,我们创建队列的大小并不好把控,此时我们可以通过指针来传递数据包的首地址。其中创建队列只需要创建对应结构体指针的大小即可。在发送任务中,根据数据的长度来分配柔性数组的长度。ble_uuid1_write_val属于蓝牙的回调函数,属于发送方。ble_app_task属于处理蓝牙的指令,属于接收方。
typedef struct {
sizeof(uint16_t) len; // 数据长度
uint8_t data[]; // 柔性数组存储实际数据
}ble_rx_data_t;
static ssize_t ble_uuid1_write_val(struct bt_conn *conn, const struct bt_gatt_attr *attr,
const void *buf, u16_t len, u16_t offset,
u8_t flags)
{
// sizeof(ble_rx_data_t)等于sizeof(uint16_t) 这里uin8_t占用一个字节,所以直接使用len即可
ble_rx_data_t *recv_data = (ble_rx_data_t *)pvPortMalloc(sizeof(ble_rx_data_t) + len);
if(!recv_data){
printf("malloc failed\r\n");
return 0;
}
recv_data->len = len;
memcpy(recv_data->data, buf, len);
if(xQueueSend(ble_queue_handle, &recv_data, 100) != pdTRUE)
{
vPortFree(recv_data);
return 0;
}
return len;
}
void ble_app_task(void *params)
{
ble_rx_data_t *ble_px = NULL;
ble_queue_handle = xQueueCreate( 10 , sizeof(ble_rx_data_t*) );
while (true)
{
xQueueReceive(ble_queue_handle,&ble_px,portMAX_DELAY);
{
if(ble_px != NULL){
//处理蓝牙发送过来的数据
vPortFree(ble_px);
}
}
vTaskDelay(100);
}
}