FreeRTOS学习笔记 ——消息队列

Posted haoaoooooo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FreeRTOS学习笔记 ——消息队列相关的知识,希望对你有一定的参考价值。

前言

学习完如何在STM32F1搭建FreeRTOS环境后,接下来学习FreeRTOS的消息队列。如果还不会搭建FreeRTOS环境的小伙伴可以先看我之前的文章->传送门.

FreeRTOS消息队列

先来了解什么是消息队列,通常情况下,队列被作为 FIFO(先进先出)缓冲区使用,即数据由队列尾写入,从队列首读出。当然,由队列首写入也是可能的。

队列有两种实现方式

  • 复制队列(Queue by copy) 表示写入队列的数据都被完整复制到队列中了
  • 引用队列(Queue by reference)表示写入队列的是要写入数据的引用并不是数据本身

在FreeRTOS中采用的是复制队列的实现方式,有如下优势:

  • 有些栈变量是在函数运行结束后会被销毁,采用引用队列的话引用会失效
  • 发送数据的函数可以重复使用变量,采用引用队列的话每发送一个数据需要一个新的变量
  • 发送队列数据和接受队列数据的函数是没有耦合的,互相不影响

具体的内容和源码分析可以看其他大佬的文章,这里主要介绍如何配置和使用。

CubeMx配置

MCU: STM32F103C8T6
CubeMX: STM32CubeMX 5.3.0

1.使能外部高速时钟源
在这里插入图片描述
2.时钟树配置(直接最大频率)
在这里插入图片描述
3.开启串口(用于调试)
在这里插入图片描述
4.开启FreeRTOS
在这里插入图片描述
然后在Tasks and Queues选项中再添加一个Tasks,默认有一个,添加之后就是有两个了。Queues就是消息队列,添加两个队列,队列大小为8到32间,十进制数。
在这里插入图片描述

5.填写项目名
在这里插入图片描述
配置项目生成单独的.c/.h文件
在这里插入图片描述
接着生成代码就ok了~ (点击后会出现警告,直接点yes即可)
在这里插入图片描述
打开工程,打开freertos.c,会发现多了以下的代码

  osMessageQDef(myQueue01, 8, uint8_t);
  myQueue01Handle = osMessageCreate(osMessageQ(myQueue01), NULL);

  /* definition and creation of myQueue02 */
  osMessageQDef(myQueue02, 32, uint32_t);
  myQueue02Handle = osMessageCreate(osMessageQ(myQueue02), NULL);

这是创建队列的代码,有兴趣可以翻源码看具体的实现。
关于队列的发送以及接收的函数如下图:
在这里插入图片描述在这里插入图片描述

BaseType_t xQueueSend( QueueHandle_t xQueue,
					   const void * pvItemToQueue,
					   TickType_t xTicksToWait);

参数:

  • xQueue: 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄。
  • pvItemToQueue:指向要发送的消息,发送时候会将这个消息拷贝到队列中。
  • xTicksToWait: 阻塞时间,此参数指示当队列满的时候任务进入阻塞态等待队列空闲的最大
    时间。如果为 0 的话当队列满的时候就立即返回;

返回值:

  • pdPASS: 向队列发送消息成功!
  • errQUEUE_FULL: 队列已经满了,消息发送失败
BaseType_t xQueueReceive(QueueHandle_t xQueue,
			             void * pvBuffer,
                         TickType_t xTicksToWait);

参数:

  • xQueue: 队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此队列的队列句柄。
  • pvBuffer: 保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中。
  • xTicksToWait: 阻塞时间,此参数指示当队列空的时候任务进入阻塞态等待队列有数据的最
    大时间。如果为 0 的话当队列空的时候就立即返回;当为 portMAX_DELAY
    的 话 就 会 一 直 等 待 , 直 到 队 列 有 数 据 , 也 就 是 死 等 , 但 是 宏
    INCLUDE_vTaskSuspend 必须为 1。

返回值:

  • pdTRUE: 从队列中读取数据成功。
  • pdFALSE: 从队列中读取数据失败。

下面进入实战,在freertos.c里,添加一个存储信息的结构体,由一个无符号8位的数作为id,char类型大小为20的数组作为信息。

typedef struct{
	uint8_t mid;
	char mDate[20];
}MSG;

MSG myDate;

接下来编写两个task里的代码。

void StartDefaultTask(void const * argument)
{ 
  /* USER CODE BEGIN StartDefaultTask */
	MSG *TXMSG;
	uint8_t i;
	TXMSG = &myDate;
  /* Infinite loop */
  for(;;)
  {
		TXMSG->mid = cnt++;
		for(i=0; i<20; i++){
			TXMSG->mDate[i] = rand()%255; 
		}
		if(xQueueSend(myQueue02Handle,&TXMSG,10) ==errQUEUE_FULL)
		{
			printf("myQueue01Handle errQUEUE_FULL\\r\\n");
		}	
		osDelay(500);
//		d_time = rand()%500;
//    osDelay(d_time);
		//xSemaphoreGive(myBinarySem01Handle);
//		printf("\\r\\nTX-Task1 Done!\\r\\n");
  }
  /* USER CODE END StartDefaultTask */
}

/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void const * argument)
{
  /* USER CODE BEGIN StartTask02 */
	MSG *RXMSG;
	uint8_t i;
  /* Infinite loop */
  for(;;)
  {
		//xSemaphoreTake(myBinarySem01Handle,osWaitForever);
		if(xQueueReceive( myQueue02Handle,&RXMSG, 10) == pdPASS)
		{
			printf("\\r\\nRXMSG->ucMessageID = %d \\r",RXMSG->mid);
			printf("RXMSG->ucData[0] = ");
			for(i=0;i<20;i++)
				printf(" %03d",RXMSG->mDate[i]);
			printf("\\r\\n");
		}
		//printf("delay time: %d RX-Task2 Done!\\r\\n",d_time);
		osDelay(500);
  }
  /* USER CODE END StartTask02 */
}

这个小demo内容大概是,通过task1将一个装有20个随机数的结构体信息推送到队列里,然后在task2中将队列里的内容出队,并打印出来。

看看效果~
在这里插入图片描述
成功利用队列来收发信息!

以上内容记录下来,希望能帮助到有需要的伙伴。
该文章如有不对地地方,欢迎指出。

以上是关于FreeRTOS学习笔记 ——消息队列的主要内容,如果未能解决你的问题,请参考以下文章

FreeRTOS学习笔记 ——消息队列

STM32CubeMX学习笔记(29)——FreeRTOS实时操作系统使用(消息队列)

FreeRTOS学习笔记7-FreeRTOS 队列相关

FreeRTOSFreeRTOS学习笔记(14)— FreeRTOS的消息队列(原生API)

FreeRTOS学习笔记 ——二值信号量

FreeRTOS学习笔记 ——二值信号量