队列简介:
注意,队列长度并不是每个队列的字节数,而是能存多少条数据。如我们创建一个队列,每条消息最大10字节,队列可以保存4条消息,那么队列的长度为4。
队列的功能:
1 数据拷贝
为什么一定要使用数据拷贝?
考虑以下情况,我们在任务中创建了一个局部变量,而局部变量可能随时会被删掉,此时如果使用引用传递(指针),数据可能就会出现问题;另外,如果采用指针,则在这个变量被队列引用之前,变量值是不能改变的,这就意味着此时任务将不能继续随意使用此变量;如果采用数据拷贝,则不会出现上述问题。
2 多任务访问
队列不是属于某个特别指定的任务的,任何任务都可以向队列中发送消息,或者从队列中提取消息。
3 出队阻塞
4 入队阻塞
队列操作过程
1 创建队列
2 写入队列
3 读取队列
队列结构体原型:
typedef struct QueueDefinition
UBaseType_t uxRecursiveCallCount;
List_t xTasksWaitingToSend;
List_t xTasksWaitingToReceive;
volatile UBaseType_t uxMessagesWaiting;
#if ((configSUPPORT_STATIC_ALLOCATION == 1) && (configSUPPORT_DYNAMIC_ALLOCATION == 1))
uint8_t ucStaticallyAllocated;
#if (configUSE_QUEUE_SETS == 1)
struct QueueDefinition *pxQueueSetContainer;
#if (configUSE_TRACE_FACILITY == 1)
UBaseType_t uxQueueNumber;
创建队列
1 动态创建队列
函数原型:
实现的功能,就是动态内存申请需要大小的内存 = 消息数 × 消息长度,然后将队列存储区指针偏移到缓存区。
QueueHandle_t xQueueGenericCreate(const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType)
size_t xQueueSizeInBytes;
uint8_t *pucQueueStorage;
configASSERT(uxQueueLength > (UBaseType_t)0);
if (uxItemSize == (UBaseType_t)0)
xQueueSizeInBytes = (size_t)0;
xQueueSizeInBytes = (size_t)(uxQueueLength * uxItemSize);
pxNewQueue = (Queue_t *)pvPortMalloc(sizeof(Queue_t) + xQueueSizeInBytes);
pucQueueStorage = ((uint8_t *)pxNewQueue) + sizeof(Queue_t);
#if (configSUPPORT_STATIC_ALLOCATION == 1)
pxNewQueue->ucStaticallyAllocated = pdFALSE;
prvInitialiseNewQueue(uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue);
函数说明:
静态创建队列:
入队函数:
入队函数说明:
函数参数说明:
通用发送函数简单分析:
数据拷贝过程:
中断级队列发送:
下面是正点原子串口中断中发送队列的程序:
void USART1_IRQHandler(void)
BaseType_t xHigherPriorityTaskWoken;
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
Res = USART_ReceiveData(USART1);
if ((USART_RX_STA & 0x8000) == 0)
if (USART_RX_STA & 0x4000)
USART_RX_BUF[USART_RX_STA & 0X3FFF] = Res;
if (USART_RX_STA > (USART_REC_LEN - 1))
if ((USART_RX_STA & 0x8000) && (Message_Queue != NULL))
xQueueSendFromISR(Message_Queue, USART_RX_BUF, &xHigherPriorityTaskWoken);
memset(USART_RX_BUF, 0, USART_REC_LEN);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
其实这个程序,个人觉得局限性比较大,因为它规定了数据格式。总体思路是,在串口接收中断中,将接收到的数据一个一个的保存到缓冲区里,等待串口数据接收完毕(可以通过读取寄存器标志位,串口空闲标志位来判断接收完毕)时,将缓冲区里的数据发送到队列中,然后清空缓冲区,并根据相关变量确定是否需要进行任务切换。
出队函数
删除读取队列函数参数说明:
不删除读取队列函数参数说明:
同样的,中断读取队列函数:
中断中进行任务切换的判别函数:
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
队列实验
设计2个任务,其中按键任务用于获取键值,并发送至队列;按键处理任务获取队列中的消息,并处理键值。
按键队列任务如下所示:
key_queue = xQueueCreate(1, sizeof(uint8_t));
void key_task(void *pvParameters)
if (key_scan(KEY1_GPIO_Port, KEY1_Pin) == KEY_ON)
error_value = xQueueOverwrite(key_queue, &key_value);
if (error_value == errQUEUE_FULL)
printf("按键队列已满,消息发送失败!\n");
if (key_scan(KEY2_GPIO_Port, KEY2_Pin) == KEY_ON)
error_value = xQueueSendToBack(key_queue, &key_value, 10);
if (error_value == errQUEUE_FULL)
printf("按键队列已满,消息发送失败!\n");
void print_task(void *pvParameters)
BaseType_t rx_queue_state;
rx_queue_state = xQueueReceive(key_queue, &rx_key_value, portMAX_DELAY);
if (rx_queue_state == pdTRUE)
printf("key_value = %d\n", rx_key_value);
测试结果如下所示:
中断服务函数使用队列
这个比较复杂,总体思路是,在串口接受回调函数中,将接收到的数据保存至RX_BUFFER;在空闲中断中,将RX_BUFFER的数据全部发送至队列;在队列处理任务中,将队列中的数据读到TX_BUFFER中,然后打印输出。
1 使能接收中断和空闲中断
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_IDLE);
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);
2 接收中断处理中,将接收到的字符保存至RX_BUFFER缓冲区
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))
xQueueSendToBackFromISR(usart_queue, RX_BUFFER, &pxHigherPriorityTaskWoken);
if (pxHigherPriorityTaskWoken == pdTRUE)
memset(RX_BUFFER, 0, USART_QUEUE_LEN);
temp = huart1.Instance->SR;
temp = huart1.Instance->DR;
整个串口中断服务函数如下所示:
void USART1_IRQHandler(void)
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE))
RX_BUFFER[i++] = huart1.Instance->DR;
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))
xQueueSendToBackFromISR(usart_queue, RX_BUFFER, &pxHigherPriorityTaskWoken);
if (pxHigherPriorityTaskWoken == pdTRUE)
memset(RX_BUFFER, 0, USART_QUEUE_LEN);
temp = huart1.Instance->SR;
temp = huart1.Instance->DR;
读取串口消息队列数据的任务如下所示:
void usart_task(void *pvParameters)
BaseType_t rx_queue_state;
rx_queue_state = xQueueReceive(usart_queue, TX_BUFFER, portMAX_DELAY);
if (rx_queue_state == pdTRUE)
printf("%s\n", TX_BUFFER);
memset(TX_BUFFER, 0, USART_QUEUE_LEN);
注意,这个实验要求,串口的中断优先级可以被屏蔽,即 ≥ 5(5为预设的屏蔽优先级,只有这样,才能在中断服务函数中使用fromISR函数,发送队列),如下所示:
测试结果如下所示:
完整的工程代码:
main.c
#define USART_QUEUE_DEPTH 1
#define USART_QUEUE_LEN 100
void start_task(void *pvParameters);
void key_task(void *pvParameters);
void print_task(void *pvParameters);
void usart_task(void *pvParameters);
#define START_TASK_SIZE 128
#define START_TASK_PRIO 1
TaskHandle_t start_Task_Handle;
#define KEY_TASK_SIZE 128
TaskHandle_t key_task_Handle;
#define PRINT_TASK_SIZE 128
#define PRINT_TASK_PRIO 3
TaskHandle_t print_task_Handle;
#define USART_TASK_SIZE 128
#define USART_TASK_PRIO 3
TaskHandle_t usart_task_Handle;
QueueHandle_t usart_queue;
uint8_t RX_BUFFER[USART_QUEUE_LEN];
uint8_t TX_BUFFER[USART_QUEUE_LEN];
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_IDLE);
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);
xTaskCreate((TaskFunction_t )start_task,
(uint16_t )START_TASK_SIZE,
(UBaseType_t )START_TASK_PRIO,
(TaskHandle_t * )&start_Task_Handle);
void start_task(void *pvParameters)
key_queue = xQueueCreate(1, sizeof(uint8_t));
usart_queue = xQueueCreate(USART_QUEUE_DEPTH, USART_QUEUE_LEN);
xTaskCreate((TaskFunction_t )key_task,
(uint16_t )KEY_TASK_SIZE,
(UBaseType_t )KEY_TASK_PRIO,
(TaskHandle_t * )&key_task_Handle);
xTaskCreate((TaskFunction_t )print_task,
(uint16_t )PRINT_TASK_SIZE,
(UBaseType_t)PRINT_TASK_PRIO,
(TaskHandle_t * )&print_task_Handle);
xTaskCreate((TaskFunction_t )usart_task,
(uint16_t )USART_TASK_SIZE,
(UBaseType_t )USART_TASK_PRIO,
(TaskHandle_t * )&usart_task_Handle);
vTaskDelete(start_Task_Handle);
void key_task(void *pvParameters)
if (key_scan(KEY1_GPIO_Port, KEY1_Pin) == KEY_ON)
error_value = xQueueOverwrite(key_queue, &key_value);
if (error_value == errQUEUE_FULL)
printf("按键队列已满,消息发送失败!\n");
if (key_scan(KEY2_GPIO_Port, KEY2_Pin) == KEY_ON)
error_value = xQueueSendToBack(key_queue, &key_value, 10);
if (error_value == errQUEUE_FULL)
printf("按键队列已满,消息发送失败!\n");
void print_task(void *pvParameters)
BaseType_t rx_queue_state;
rx_queue_state = xQueueReceive(key_queue, &rx_key_value, portMAX_DELAY);
if (rx_queue_state == pdTRUE)
printf("key_value = %d\n", rx_key_value);
void usart_task(void *pvParameters)
BaseType_t rx_queue_state;
rx_queue_state = xQueueReceive(usart_queue, TX_BUFFER, portMAX_DELAY);
if (rx_queue_state == pdTRUE)
printf("%s\n", TX_BUFFER);
memset(TX_BUFFER, 0, USART_QUEUE_LEN);
中断服务函数:
void USART1_IRQHandler(void)
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE))
RX_BUFFER[i++] = huart1.Instance->DR;
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))
xQueueSendToBackFromISR(usart_queue, RX_BUFFER, &pxHigherPriorityTaskWoken);
if (pxHigherPriorityTaskWoken == pdTRUE)
memset(RX_BUFFER, 0, USART_QUEUE_LEN);
temp = huart1.Instance->SR;
temp = huart1.Instance->DR;
文章评论(0条评论)
登录后参与讨论