【实战】STM32 FreeRTOS移植系列教程5:FreeRTOS消息队列

写在前面:

本文章为《STM32MP157开发教程之FreeRTOS操作系统篇》系列中的一篇,笔者使用的开发平台为华清远见FS-MP1A开发板(STM32MP157开发板)。stm32mp157是ARM双核,2个A7核,1个M4核,A7核上可以跑Linux操作系统,M4核上可以跑FreeRTOS、RT-Thread等实时操作系统,STM32MP157开发板所以既可以学嵌入式linux,也可以学stm32单片机。

针对FS-MP1A开发板,除了FreeRTOS操作系统篇外,还包括其他多系列教程,包括Cortex-A7开发篇、Cortex-M4开发篇、扩展板驱动移植篇、Linux应用开发篇、Linux系统移植篇、Linux驱动开发篇、硬件设计篇、人工智能机器视觉篇、Qt应用编程篇、Qt综合项目实战篇等。欢迎关注,更多stm32mp157开发教程及视频,可加技术交流Q群459754978,感谢关注。

FS-MP1A开发板详情介绍:https://item.taobao.com/item.htm?id=622457259672

2.FreeRTOS队列

2.1队列简介

队列可以用于任务与任务、任务与中断之间的通信,在任务与任务、任务与中断之间传递数据,队列中可以存储有限的、大小固定的数据项目。

一般情况下,队列采用先进先出(FIFO)的存储机制,也就是说先发送进队列的数据先被提取,当然也可以使用先进后出(LIFO)的存储缓冲,FreeRTOS中提供了两种存储缓冲机制。

队列不属于某个特定的任务,对于所有任务都可以向任务中发送数据,也可以从任务中提取数据。队列函数

2.2.1 创建队列

创建事件标志组的函数有两个,分别如下:

xQueueCreate(),此函数用于动态创建队列,所需要的内存通过动态内存管理方法分配,此函数是个宏,真正完成队列创建的函数时xQueueGenericCreate()。函数原型如下:

QueueHandle_t  xQueueCreate(UBaseType_t uxQueueLength,

UBaseType_t uxItemSize)

参数:

uxQueueLength:  要创建的队列的队列长度,这里是队列的项目数。

uxItemSize:      队列中每个项目(消息)的长度,单位为字节。

返回值:

NULL: 队列创建失败。

其他值:创建成功的队列返回的队列句柄。

xQueueCreate Static(),此函数为静态方法创建队列,所需要的内存通过用户自行分配,此函数也是个宏,最终调用的是函数xQueueGenericCreateStatic()。其函数原型如下:

QueueHandle_t xQueueCreateStatic(UBaseType_t  uxQueueLength,

                              UBaseType_t  uxItemSize,

                              uint8_t*  pucQueueStorageBuffer,

                              StaticQueue_t*  pxQueueBuffer)

参数:

uxQueueLength:     要创建的队列的队列长度,这里是队列的项目数。

uxItemSize:         队列中每个项目(消息)的长度,单位为字节

pucQueueStorage:     指向队列项目的存储区,也就是消息的存储区,这个存储区需要

用户自行分配。此参数必须指向一个 uint8_t 类型的数组。这个

存储区要大于等于(uxQueueLength * uxItemsSize)字节。

pxQueueBuffer:       此参数指向一个 StaticQueue_t 类型的变量,用来保存队列结构体。

返回值:

NULL: 队列创建失败。

其他值:创建成功的队列返回的队列句柄。

2.2.2 向队列发送消息

队列创建好以后便可以向其中发送消息,FreeRTOS提供的向队列中发送消息的API函数有8个,分别如下:

函数xQueueSend()、xQueueSendToBcck()和xQueueSendToFront()

此三个函数都用于向队列中发送消息,函数本质都是宏,其中xQueueSend()和xQueueSendToBcck()都是将消息添加到队列的后面,xQueueSendToFront()是将消息添加到队列的前面,这三个函数最终调用的同一个函数:xQueueGenericSend(),都只能用于任务函数中,其函数原型分别如下:

  BaseType_t  xQueueSend( QueueHandle_t xQueue,

const void * pvItemToQueue,

TickType_t xTicksToWait)

  BaseType_t  xQueueSendToBack(QueueHandle_t xQueue,

const void*  pvItemToQueue,

TickType_t  xTicksToWait)

  BaseType_t  xQueueSendToFront(QueueHandle_t  xQueue,

const void *  pvItemToQueue,

TickType_t xTicksToWait)

参数:

xQueue:              队列句柄,指明要向哪个队列发送数据,创建队列成功以后会

返回此队列的队列句柄。

pvItemToQueue:       指向要发送的消息,发送时候会将这个消息拷贝到队列中。

xTicksToWait:         阻塞时间,此参数指示当队列满的时候任务进入阻塞态等待队

列空闲的最大时间。

返回值:

pdPASS:              向队列发送消息成功。

ErrQUEUE_FULL:     队列已满,消息发送失败

    函数xQueueOverwrite(),此函数同样用于向队列中发送数据,但当队列满了以后会覆写掉旧的数据,不管此数据有没有被其他任务或中断取走,同样此函数也是宏,最终调用的也是函数xQueueGenericSend(),其函数原型如下:

BaseType_t  xQueueOverwrite( QueueHandle_t  xQueue,

const void *  pvItemToQueue)

参数:

xQueue:              队列句柄,指明要向哪个队列发送数据,创建队列成功以后会

返回此队列的队列句柄。

pvItemToQueue:       指向要发送的消息,发送的时候会将这个消息拷贝到队列中。

返回值:

pdPASS:   向队列发送消息成功,此函数也只会返回 pdPASS!因为此函数执行过程中

不在乎队列满不满,满了的话就覆写掉旧的数据,总之肯定能成功。

函数xQueueGenericSend(),前面所有任务级入队函数最终调用的都是此函数,其函数原型如下:

BaseType_t  xQueueGenericSend (QueueHandle_t  xQueue,

const void *   pvItemToQueue,

TickType_t  xTicksToWait,

const BaseType_t  xCopyPosition)

参数:

xQueue:              队列句柄,指明要向哪个队列发送数据,创建队列成功以后会

返回此队列的队列句柄。

pvItemToQueue:       指向要发送的消息,发送的时候会将这个消息拷贝到队列中。

xTicksToWait:         阻塞时间。

xCopyPosition:        入队方式,总共有三种

                        queueSEND_TO_BACK:  后向入队

queueSEND_TO_FRONT: 前向入队

queueOVERWRITE:      覆写入队。

返回值:

pdTRUE:              向队列发送消息成功。

errQUEUE_FULL:      队列已满,消息发送失败

函数xQueueSendFromISR()、xQueueSendToBcckFromISR ()、

xQueueSendToFrontFromISR ()

这三个函数用于中断服务函数中向队列发送消息,分别为之前三个向队列发送消息函数的中断版本,函数本质上也是宏,最终调用的是同一个函数xQueueGenericSendFromISR(),函数原型分别如下:

BaseType_t xQueueSendFromISR( QueueHandle_t  xQueue,

const void *   pvItemToQueue,

BaseType_t *  pxHigherPriorityTaskWoken)

BaseType_t xQueueSendToBackFromISR(QueueHandle_t xQueue,

const void * pvItemToQueue,

BaseType_t *  pxHigherPriorityTaskWoken)

  BaseType_t xQueueSendToFrontFromISR(QueueHandle_t  xQueue,

const void *  pvItemToQueue,

BaseType_t *  pxHigherPriorityTaskWoken)

参数:

xQueue:              队列句柄,指明要向哪个队列发送数据,创建队列成功以后会

返回此队列的队列句柄。

pvItemToQueue:       指向要发送的消息,发送的时候会将这个消息拷贝到队列中。

pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换

返回值:

pdTRUE:              向队列发送消息成功。

errQUEUE_FULL:      队列已满,消息发送失败

函数xQueueOverwriteFromISR(),此函数为xQueueOverwrite()的中断版本,同样此函数也是宏,最终调用的也是函数xQueueGenericSendFromISR(),其函数原型如下:

BaseType_t  xQueueOverwriteFromISR( QueueHandle_t  xQueue,

const void *  pvItemToQueue,

BaseType_t *  pxHigherPriorityTaskWoken)

参数:

xQueue:              队列句柄,指明要向哪个队列发送数据,创建队列成功以后会

返回此队列的队列句柄。

pvItemToQueue:       指向要发送的消息,发送的时候会将这个消息拷贝到队列中。

pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换

返回值:

pdTRUE:              向队列发送消息成功。

errQUEUE_FULL:      队列已满,消息发送失败

函数xQueueGenericSendFromISR(),前面所有中断级入队函数最终调用的都是此函数,此函数也是函数xQueueGenericSend()的中断版本,其函数原型如下:

BaseType_t  xQueueGenericSend FromISR (QueueHandle_t  xQueue,

const void *  pvItemToQueue,

BaseType_t*  pxHigherPriorityTaskWoken,

                                        BaseType_t  xCopyPosition)

参数:

xQueue:                队列句柄,指明要向哪个队列发送数据,创建队列成功以后

会返回此队列的队列句柄。

pvItemToQueue:          指向要发送的消息,发送的时候会将这个消息拷贝到队列中。

pxHigherPriorityTaskWoken:       标记退出此函数以后是否进行任务切换。

xCopyPosition:          入队方式,总共有三种

                        queueSEND_TO_BACK:  后向入队

queueSEND_TO_FRONT: 前向入队

queueOVERWRITE:      覆写入队。

返回值:

pdTRUE:              向队列发送消息成功。

errQUEUE_FULL:      队列已满,消息发送失败

2.2.3 从队列读取消息

与入队相对应,那么就会有出队,出队就是从队列中获取消息,FreeRTOS中提供的出队API函数分别如下:

xQueueReceive(),此函数用于任务中从队列读取一条消息,读取成功以后就会将这条数据删除,此函数是个宏,真正执行的函数为xQueueGenericReceive(),此函数原型如下:

BaseType_t  xQueueReceive(QueueHandle_t  xQueue,

void *  pvBuffer,

TickType_t  xTicksToWait)

参数:

xQueue:        队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此

队列的队列句柄。

pvBuffer:       保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个

缓冲区中。

  xTicksToWait:   阻塞时间,此参数指示当队列空的时候任务进入阻塞态等待队列有数

据的最大时间。

返回值:

pdTRUE:       从队列中读取消息成功。

pdFALSE:      从队列中读取消息失败。

    xQueuePeek(),此函数也用于任务中从队列读取一条消息,但是读取成功以后不会将这条数据删除,此函数也是个宏,真正执行的函数为xQueueGenericReceive(),此函数原型如下:

BaseType_t xQueuePeek( QueueHandle_t  xQueue,

void *  pvBuffer,

TickType_t  xTicksToWait)

参数:

xQueue:        队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此

队列的队列句柄。

pvBuffer:       保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个

缓冲区中。

  xTicksToWait:   阻塞时间,此参数指示当队列空的时候任务进入阻塞态等待队列有数

据的最大时间。

返回值:

pdTRUE:       从队列中读取消息成功。

pdFALSE:      从队列中读取消息失败。

xQueueReceiveFromISR(),此函数为xQueueReceive()的中断版本,用于在中断服务函数中从队列读取消息,其函数原型如下:

BaseType_t xQueueReceiveFromISR(QueueHandle_t  xQueue,

void*  pvBuffer,

BaseType_t *  pxTaskWoken)

参数:

xQueue:          队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回

此队列的队列句柄。

pvBuffer:         保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这

个缓冲区中。

pxTaskWoken:     标记退出此函数以后是否进行任务切换

返回值:

pdTRUE:  从队列中读取数据成功。

pdFALSE:  从队列中读取数据失败。

xQueuePeekFromISR (),此函数为xQueuePeek()的中断版本,读取数据成功以后不会将这条数据删除,此函数原型如下:

BaseType_t xQueuePeekFromISR( QueueHandle_t  xQueue,

void *  pvBuffer)

参数:

xQueue:        队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此

队列的队列句柄。

pvBuffer:       保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个

缓冲区中。

返回值:

pdTRUE:       从队列中读取消息成功。

pdFALSE:      从队列中读取消息失败。

    1. 操作实验

2.3.1实验设计

本次设计通过中断检测三个按键状态,读取到不同键值后发送到队列中,然后在任务中读取队列中的消息,根据不同的指令做出不同的处理。

可参考12.3.2章节进行导入已有工程,工程存放路径【华清远见-FS-MP1A开发资料\02-程序源码\ARM体系结构与接口技术\FreeRTOS\6_MP1A-FreeRTOS-Queue】

任务及其功能如下:

StartTask02():     读取队列中的数据,根据读取到的数据做不同处理。

StartDefaultTask(): 读取按键值,并发送到队列中。

2.3.2实验过程与分析

首先,根据之前几章内容配置好CubeMX,按照上一节配置“FREERTOS”,完成后生成代码。 在StartDefaultTask() 与StartTask02()中添加代码如下。

void StartDefaultTask(void *argument)

{

  /* USER CODE BEGIN 5 */

  /* Infinite loop */

uint8_t err;

  for(;;)

  {

          if((Key_Queue!=NULL)&&(key))    //消息队列Key_Queue创建成功,并且按键被按下

          {

              err=xQueueSend(Key_Queue,&key,10);

              if(err==pdPASS)    //发送按键值

              {

                  printf("\r data transmission success \n");

              }

             

          }

          vTaskDelay(10);                           //延时10ms,也就是10个时钟节拍

  }

  /* USER CODE END 5 */ 

}

void StartTask02(void *argument)

{

  /* USER CODE BEGIN StartTask02 */

  /* Infinite loop */

  for(;;)

  {

  if(Key_Queue!=NULL)

      {

      if(xQueueReceive(Key_Queue,&key,portMAX_DELAY))//请求消息Key_Queue

      {     

           switch(key)

           {

               case EVENTBIT_1: //

                    LED_1_TOG();

                    break;

               case EVENTBIT_2: //

                    LED_2_TOG();

                    break;

               case EVENTBIT_3: //

                    LED_3_TOG();

                    break;

            }

           key = 0;

       }

   }

  vTaskDelay(10);      //延时10ms,也就是10个时钟节拍

  }

  /* USER CODE END StartTask02 */

}

另外,首先需要在主函数中调用xQueueCreate ()函数创建消息队列,获得创建成功的队列句柄。

编写按键中断回调函数如下

void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)

{

switch(GPIO_Pin)

{

   case GPIO_PIN_8:

       if(HAL_GPIO_ReadPin(GPIOF,GPIO_PIN_8) == GPIO_PIN_SET) /* read KEY3 PF8 state */

        key = EVENTBIT_3;

       break;

   case GPIO_PIN_7:

   if(HAL_GPIO_ReadPin(GPIOF,GPIO_PIN_7) == GPIO_PIN_SET) /* read KEY2 PF7 state */

   key = EVENTBIT_2;

    break;

   case GPIO_PIN_9:

   if(HAL_GPIO_ReadPin(GPIOF,GPIO_PIN_9) == GPIO_PIN_SET) /* read KEY1 PF9 state */

   key = EVENTBIT_1;

    break;

}

}

 此时,当按下KEY1键时,LED1电平发生翻转;当按下KEY2键时,LED2电平发生翻转;当按下KEY3键时,LED3电平发生翻转。

硬件平台:华清远见FS-MP1A开发板(STM32MP157)

你可能感兴趣的:(stm32mp157教程,FreeRTOS开发,STM32MP157,stm32,freertos)