自学内容网 自学内容网

【北京迅为】《STM32MP157开发板使用手册》- 第三十九章 消息队列实验

iTOP-STM32MP157开发板采用ST推出的双核cortex-A7+单核cortex-M4异构处理器,既可用Linux、又可以用于STM32单片机开发。开发板采用核心板+底板结构,主频650M、1G内存、8G存储,核心板采用工业级板对板连接器,高可靠,牢固耐用,可满足高速信号环境下使用。共240PIN,CPU功能全部引出:底板扩展接口丰富底板板载4G接口(选配)、千兆以太网、WIFI蓝牙模块HDMI、CAN、RS485、LVDS接口、温湿度传感器(选配)光环境传感器、六轴传感器、2路USB OTG、3路串口,CAMERA接口、ADC电位器、SPDIF、SDIO接口等


第三十九章 消息队列实验

39.1 消息队列的基本概念

基于 FreeRTOS 的应用程序由一组独立的任务构成——每个任务都是具有独立权限的程序。这些独立的任务之间的通讯与同步一般都是基于操作系统提供的IPC通讯机制,而FreeRTOS 中所有的通信与同步机制都是基于队列实现的。

消息队列是一种常用于任务间通信的数据结构,队列可以在任务与任务间、中断和任务间传送信息,实现了任务接收来自其他任务或中断的不固定长度的消息。任务能够从队列里面读取消息,当队列中的消息是空时,挂起读取任务,用户还可以指定挂起的任务时间;当队列中有新消息时,挂起的读取任务被唤醒并处理新消息,消息队列是一种异步的通信方式

具有如下特性:

• 消息支持先进先出方式排队,支持异步读写工作方式。

• 读写队列均支持超时机制。

• 消息支持后进先出方式排队,往队首发送消息(LIFO)。

• 可以允许不同长度(不超过队列节点最大值)的任意类型消息。

• 一个任务能够从任意一个消息队列接收和发送消息。

• 多个任务能够从同一个消息队列接收和发送消息。

• 当队列使用结束后,可以通过删除队列函数进行删除。

39.2 消息队列的运作机制

创建消息队列时 FreeRTOS 会先给消息队列分配一块内存空间,这块内存的大小等于消息队列控制块大小加上(单个消息空间大小与消息队列长度的乘积),接着再初始化消息队列,此时消息队列为空。FreeRTOS 的消息队列控制块由多个元素组成,当消息队列被创建时,系统会为控制块分配对应的内存空间,用于保存消息队列的一些信息如消息的存储位置,头指针pcHead、尾指针 pcTail、消息大小 uxItemSize 以及队列长度 uxLength 等。同时每个消息队列都与消息空间在同一段连续的内存空间中,在创建成功的时候,这些内存就被占用了,只有删除了消息队列的时候,这段内存才会被释放掉,创建成功的时候就已经分配好每个消息空间与消息队列的容量,无法更改,每个消息空间可以存放不大于消息大小 uxItemSize 的任意类型的数据,所有消息队列中的消息空间总数即是消息队列的长度,这个长度可在消息队列创建时指定。 任务或者中断服务程序都可以给消息队列发送消息,当发送消息时,如果队列未满或者允许覆盖入队,FreeRTOS 会将消息拷贝到消息队列队尾,否则,会根据用户指定的阻塞超时时间进行阻塞,在这段时间中,如果队列一直不允许入队,该任务将保持阻塞状态以等待队列允许入队。当其他任务从其等待的队列中读取入了数据(队列未满),该任务将自动由阻塞态转移为就绪态。当等待的时间超过了指定的阻塞时间,即使队列中还不允许入队,任务也会自动从阻塞态转移为就绪态,此时发送消息的任务或者中断程序会收到一个错误码。

当其他任务或中断服务程序往其等待的队列中写入了数据,该任务将自动由阻塞态转移为就绪态。当等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转移为就绪态。 当消息队列不再被使用时,应该删除它以释放系统资源,一旦操作完成,消息队列将被永久性的删除。

消息队列的运作过程具体见下图:

39.3 实验目的

1)STM32CubeIDE工具软件建立freertos工程

2)学习消息队列相关的知识与初步使用

我们的实验任务为通过三个按键使用队列的方式分别来控制LED2、LED3和蜂鸣器状态的反转。

首先对实验进行分析,总共需要有两个任务,分别为SEND和RECEIVE两个任务。所实现的功能如下:

SEND任务:通过三个按键,分别为 VOL-UP、VOL-DN、BACK,三个按键可以分别用来通过队列,发送不同的值。

RECEIVE任务:通过接收队列中的信息,根据对应的信息实现不同的功能。

本章节完成的实验存放位置为“iTOP-STM32MP157开发板\iTOP-STM32MP157开发板网盘资料汇总\08_freertos实验例程\03_队列实验.zip

39.4 实验步骤

39.4.1建立freertos_Queue工程

首先我们打开STM32CubeIDE软件,进入软件界面之后,我们点击File属性,选择NEW下的STM32 Project的选项,如下图所示:

然后我们会进入下图所示界面:在Part Number选择框输入STM32MP157A,然后在右边的选择界面选择STM32MP157AAA,然后点击Next选项 

在Project Name框中输入工程名字freertos_Queue,然后点击Finish选项即可,如下图所示: 

等待工程创建完毕,会询问我们是否要安装OpenSTLinux ,由于我们是在windows环境下,所以我们不需要安装,点击NO即可 

至此我们的工程创建完毕,进入工程界面如下图所示界面: 

39.4.2输出引脚的配置(LED和蜂鸣器)

首先我们在下面的搜索框之中输入我们要配置的引脚,我们在这里以PE1为例进行搜索,输入名称之后,对应的引脚在工程中会闪烁,如下图所示:

然后我们使用鼠标左键点击对应的引脚会弹出PE1的复用功能选择,我们在这里选择复用为GPIO_Output功能,如下图所示: 

配置完复用功能之后,我们还要配置 Pin Reserved 选项如果不配置此项,在生成工程代码的时候将不会看到有关这个 Pin 的初始化代码。继续选中 PE1,右键弹出设置项我们选择Pin ReservedàCortex-M4。如下图所示: 

第二个LED的控制管脚PE14按同样的方法进行配置。

配置完成之后打开左侧菜单的 System CoreàGPIO 进入 GPIO 模式配置界面:如下图所示:

 

点击对应的引脚配置之后会弹出右下方的管脚配置界面,如上图所示:

在下方会列出要配置选项的具体说明和我们要进行的配置。

1)选项 GPIO output level 用来设置IO口的输出电平的高低,这这里我们选择LOW

2)选项 GPIO mode 用来设置 IO 口输出模式为 Output Push Pull(推挽)还是 Output Open Drain(开漏)。本实验我们设置为推挽输出 Output Push Pull。

3)选项 GPIO Pull-up/Pull-down 用来设置 IO 口是上拉/下拉/没有上下拉。本实验我们设置为上拉(Pull-up)。

4)选项 Mzximum ouput speed 用来设置 IO 口输出速度为低速(Low)/中速(Medium)/高速 (Hign)/快速(Very High)。我们设置为高速 High 。

5)选项 User Label 是用来设置初始化的 IO 口 Pin 值为我们自定义的宏,这里我们填写为 LED3。按照如上要求设置后的界面如下(由于PE14的配置相同,只是最后的Label值不同,也在下方列了出来):

 

39.4.3输入引脚的配置(按键)

首先我们在下面的搜索框之中输入我们要配置的引脚,我们在这里以PI2为例进行搜索(由于三个按键的配置相同,在这里我们只是列出了BACK按键的配置步骤),输入名称之后,对应的引脚在工程中会闪烁,如下图所示:

然后我们使用鼠标左键点击对应的引脚会弹出PI2的复用功能选择,我们在这里选择复用为GPIO_Input功能,如下图所示: 

配置完复用功能之后,我们还要配置 Pin Reserved 选项如果不配置此项,在生成工程代码的时候将不会看到有关这个 Pin 的初始化代码。继续选中 PI2,右键弹出设置项我们选择Pin ReservedàCortex-M4。如下图所示: 

VOL-UP和VOL-DN对应的PI3和PI1引脚按同样的方法进行配置。在此就不一一展示。

配置完成之后打开左侧菜单的 System CoreàGPIO 进入 GPIO 模式配置界面:如下图所示:

 

选项 GPIO Pull-up/Pull-down 用来设置 IO 口是上拉/下拉/没有上下拉。本实验我们设置为上拉(Pull-up)。

选项 User Label 是用来设置初始化的 IO 口 Pin 值为我们自定义的宏,这里我们填写为VOL-DN。按照如上要求设置后的界面如下(由于PI2和PI3的配置相同,只是最后的Label值不同,也在下方列了出来):

 

 

39.4.4时钟的配置

我们本次实验所采用的时钟为外部时钟HSE,所以我们要在左侧属性栏中的System Core 属性下找到RCC将High Speed Clock选择为Crystal/Ceramic Resonator(晶体/陶瓷晶振)。如下图所示:

然后在Clock Configuration里我们选择 HSE,作为锁相环 PLL3P 的时钟源,在 MCU 子系统时钟里输入 209 并回车,软件会自动设置相应的倍频和分频,如下图所示: 

设置完成之后,如下图所示,然后再手动配置 APB1DIV、APB2DIV 和 APB3DIV的分频值为 2。当 APB1DIV 的分频数大于 1 的时候,基本定时器的倍频器倍频值始终为 2,所以基本定时器的时钟频率为 209MHz。 

39.4.5 配置 FreeRTOS

时钟配置完成之后,我们要在左侧属性栏中的Middleware属性下找到FREERTOS将Interface函数接口选择 CMSIS_V2,选择完成如下图所示:

每个功能窗口对应的功能如下: 

窗口

对应的功能

Mutexes

互斥量

Events

事件

FreeRTOS Heap Usage

堆情况使用

User Constants

常量的定义

Tasks and Queues

任务和消息队列

Timers and Semaphores

软件定时器和信号

Config parameters

配置参数

Inclued parameters

头文件配置

Advanced settings

高级设置

然后我们进入到Tasks and Queues任务和消息队列窗口,如下图所示: 

随后我们点击default默认创建的任务,将任务名字修改为SEND,然后修改优先级为最低osPriorityLow如下图所示:

我们只需要修改任务名称和设置线程函数名即可,修改完成之后点击OK按钮,随后我们以同样的方式,创建任务名字为RECEIVE的任务,创建完成如下图所示: 

 在同一界面的下方,就是我们的队列串口,如下图所示,然后我们点击Add添加按钮,

 弹出队列的添加窗口之后,对默认的Queue Name进行修改,修改为Test_Queue,然后我们点击确定按钮,如下图所示:

弹出队列的添加窗口之后,对默认的Queue Name进行修改,修改为Test_Queue,然后我们点击确定按钮,如下图所示: 

配置完成之后我们需要在Project Manage下的Code Generator选项下勾选 Generate peripheral initialization as a pair of ".c/.h' files per peripheral 选项,这样可以独立生成对应外设的初始化.h 和.c 文件(方便配置的查看),如下图所示: 

39.4.6工程的生成与完善

在上述的步骤完成之后,按下键盘的Ctrl+S组合键保存保存 freertos_Queue.ioc 文件,系统开始生成初始化代码此处会弹出一个警告,提示我们 Systick 定时器已被 HAL 库占用,在 STM32MP157 Cortex-M4 内核上我们更换不了其他的定时器,选择 Yes 继续生成代码即可。

 

 工程生成之后如下图所示:

然后我们进行工程的完善,以及添加对应的逻辑代码。

39.4.6.1 对应文件与文件夹的添加

由于我们在裸机章节已经完善了对应的LED,BEEP和KEY文件,所以我们将iTOP-STM32MP157开发板网盘资料汇总\06_Cortex-M4实验例程\03_KEY\KEY\CM4\Core\BSP文件拷贝到当前工程对应的位置,拷贝完成如下下图所示:

39.4.6.2 app_freertos.c文件的完善

我们要修改的 app_freertos.c文件路径如下图所示:

打开app_freertos.c文件,我们首先在/* USER CODE BEGIN Includes */和/* USER CODE END Includes */中间添加以下内容,将led、beep和key的头文件进行添加。

#include "../BSP/Include/led.h"

#include "../BSP/Include/beep.h"

#include "../BSP/Include/key.h"

添加完成如下图所示:

 

然后我们来到文件的底部可以看到我们创建的RECEIVE_Task和SEND_Task任务

修改RECEIVE_Task 任务的for循环中的内容,修改内容如下:

 

uint32_t r_queue;

  for(;;)

  {

  osMessageQueueGet( Test_QueueHandle,&r_queue,0, osWaitForever);

  if(r_queue == 1)

LED2_TOGGLE();

  else if (r_queue == 2)

LED3_TOGGLE();

  else if (r_queue == 3)

BEEP_TOGGLE();

  }

修改完成如下图所示: 

这里的osMessageQueueGet (osMessageQueueId_t mq_id, void *msg_ptr, uint8_t *msg_prio, uint32_t timeout)API函数的四个参数分别的意义如下:

• mq_id :消息队列 ID(消息队列句柄)

• msg_ptr :接收消息的地址

• msg_prio :发送优先级,在源码中可看到该参数被忽略并不生效。

• timeout :线程等待时间

随后我们修改SEND_Task 任务的内容,修改内容如下:

uint8_t key;
uint32_t send_data;
  for(;;)
  {
  key = key_scan();
 if (key)
{
 switch (key)
  {
  case VOL_UP_PRES:
  send_data=1;
  osMessageQueuePut( Test_QueueHandle,&send_data,0,0 );
   break;
  case VOL_DN_PRES:
  send_data=2;
  osMessageQueuePut( Test_QueueHandle,&send_data,0,0 );
   break;
  case BACK_PRES:
  send_data=3;
  osMessageQueuePut( Test_QueueHandle,&send_data,0,0 );
  break;
   }
}
  }
    osDelay(20);

这里的osMessageQueuePut (osMessageQueueId_t mq_id, const void *msg_ptr, uint8_t msg_prio, uint32_t timeout) API函数的四个参数分别的意义如下:

• mq_id :消息队列 ID(消息队列句柄)

• msg_ptr :发送消息的地址

• msg_prio :发送优先级,在源码中可看到该参数被忽略并不生效。

• timeout :线程等待时间

至此,我们的内容就添加完成了。之后进行编译烧写,当按下VOL_UP按键时会发送内容为1的队列,当按下VOL_DN按键时会发送内容为2的队列、当按下BACK按键时会发送内容为3的队列,当接收端收到队列的内容为1时,LED2状态翻转,当接收端收到队列的内容为2时,LED3状态翻转,当接收端收到队列的内容为3时,BEEP状态翻转,至此,我们的消息队列实验就完成了。

 


原文地址:https://blog.csdn.net/BeiJingXunWei/article/details/142364736

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!