1.消息队列的基本概念
消息队列简称队列,是一种常用于任务间通信的数据结构。
如下图:消息队列可以在任务与任务间、中断与任务间传递消息,实现任务接收来自其他任务或中断的不固定长度的消息。
相比于裸机的全局数组,使用消息队列有如下优势:
>消息队列具有超时机制,可以让 FreeRTOS 内核有效地管理任务
>使用消息队列可以防止多任务的访问冲突
>使用消息队列可以有效地解决中断服务程序与任务之间消息传递的问题,使用全局数组的话,任务得不断去监测标志位以获取数据
>消息队列具有 FIFO (先进先出)与 LIFO(后进先出) 储存机制,方便处理数据
2、消息队列的通信机制
消息队列是一种异步的通信方式。
任务能够从队列中读取消息,当队列中的消息为空时,读取消息的任务将被阻塞。用户可以指定阻塞的任务时间 xTicksToWait ,在这段时间中,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。当队列中有新消息时,被阻塞的任务会被唤醒并处理新消息;当等待的时间超过指定的阻塞时间,即使队列中没有有效数据,任务也会自动从阻塞态转为就绪态。
通过消息队列服务,任务或中断服务可以将一条或多条消息放入消息队列中。同样,一个或多个任务可以从消息队列中获得消息。当有多个消息发送到消息队列时,消息队通常是将先进入列的消息先传给任务,也就是说,任务先得到的是最先进入消息队列的消息,即先进先出原则( FIFO ), FreeRTOS 的队列也支持后进先出原则( LIFO )。
3、 FreeRTOS 中消息队列特性
- 消息支持先进先出方式排队,支持异步读写工作方式
- 读写队列均支持超时机制
- 消息支持后进先出方式排队,向队首发送消息( LIFO )
- 可以允许不同长度(不超过队列节点最大值)的任意类型消息
- 一个任务能够从任意一个消息队列接收和发送消息
- 多个任务能够从同一个消息队列接收和发送消息
- 当队列使用结束后,可以通过删除队列函数进行删除
4.消息队列运作流程图
注意 :队列是FreeRTOS主要的任务间通信方式,可以在任务与任务间、中断和任务间传送消息,发送到队列的消息是通过复制方式实现的,这意味着队列存储的数据是原始数据,而不是原始数据的地址应用,所以当消息数据长度较多时,会花费较长时间,不适用于中断(需要快进快出)。
4.使用消息队列的典型流程如下:
- 创建消息队列
- 发送消息队列
- 接收消息队列
- 删除消息队列
5.常用API函数如下图:
消息队列有关的函数详细可在FreeRTOS文件里的Queue.c文件里查找,里有详细注释
编程实例
创建3个任务:按键任务(用来想队列1或队列2发送数据)、队列1接收任务、队列2接收任务
按键任务代码:
void KEY_Task(void const * argument)
{
/* USER CODE BEGIN KEY_Task */
uint32_t ucSend_Data = 0; //向消息队列1发送的数据
const TickType_t ulSendBlockTime = pdMS_TO_TICKS(10);
/* Infinite loop */
for(;;)
{
//检测按键来源
KEY.GetKeyCode();
//KEY2按下
if(KEY.KeyCode == KEY2)
{
if(xQueueSend(xQueue1, &ucSend_Data, ulSendBlockTime) == pdTRUE)
printf("成功发送数据%u\r\n", ucSend_Data);
else
printf("超时限制!!!\r\n");
//加大发送值
ucSend_Data += 1000;
}
//KEY3´¦Àí
if(KEY.KeyCode == KEY3)
{
if(xQueueSend(xQueue2, "MCU168168168\r\n", 0) == pdTRUE)
printf("成功发送数据%s\r\n", "MCU168168168");
else
printf("超时限制!!!\r\n");
}
//KEY4´¦Àí
if(KEY.KeyCode == KEY4)
{
if(xQueueSend(xQueue2, "YJJY168168168", 0) == pdTRUE)
printf("成功发送数据%s\r\n", "YJJY168168168\r\n");
else
printf("超时限制!!!\r\n");
}
//阻塞延时20ms
osDelay(20);
}
/* USER CODE END KEY_Task */
}
队列1发送任务代码:
void Queue1_Receive_Task(void const * argument)
{
/* USER CODE BEGIN Queue1_Receive_Task */
uint32_t ucRec_Data = 0;
const TickType_t ulReceiveBlockTime = pdMS_TO_TICKS(1000);
/* Infinite loop */
for(;;)
{
if(xQueueReceive(xQueue1, &ucRec_Data, ulReceiveBlockTime) == pdTRUE)
{
printf("成功接收数据%u\r\n", ucRec_Data);
}
else
{
printf("超时限制!!!\r\n");
}
}
/* USER CODE END Queue1_Receive_Task */
}
队列2接受任务代码:
void Queue2_Receive_Task(void const * argument)
{
/* USER CODE BEGIN Queue2_Receive_Task */
uint8_t ucRec_Data[16] = {0};
/* Infinite loop */
for(;;)
{
if(xQueueReceive(xQueue2, ucRec_Data, portMAX_DELAY) == pdTRUE)
{
printf("成功接收字符串:%s\r\n", ucRec_Data);
}
}
/* USER CODE END Queue2_Receive_Task */
}