【北京迅为】《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接口等
第四十一章 计数信号量实验
41.1计数信号量的基本概念
二进制信号量可以被认为是长度为 1 的队列,而计数信号量则可以被认为长度大于 1 的队列,信号量使用者依然不必关心存储在队列中的消息,只需关心队列是否有消息即可。
顾名思义,计数信号量肯定是用于计数的,在实际的使用中,我们常将计数信号量用于事件计数与资源管理。每当某个事件发生时,任务或者中断将释放一个信号量(信号量计数值加 1),当处理被事件时(一般在任务中处理),处理任务会取走该信号量(信号量计数值减 1),信号量的计数值则表示还有多少个事件没被处理。此外,系统还有很多资源,我们也可以使用计数信号量进行资源管理,信号量的计数值表示系统中可用的资源数目,任务必须先获取到信号量才能获取资源访问权,当信号量的计数值为零时表示系统没有可用的资源,但是要注意,在使用完资源的时候必须归还信号量,否则当计数值为 0的时候任务就无法访问该资源了。
41.2实验目的
1)STM32CubeIDE工具软件建立freertos工程
2)学习计数信号量的相关知识与初步使用
我们的实验任务为通过两个按键和串口,来观察计数信号量的特性。
首先对实验进行分析,总共需要有两个任务,分别为TAKE 释放任务和GIVE获取任务,并通过串口打印对应的信息。所实现的功能如下:
TAKE 任务:通过按下VOL_UP按键来实现计数信号量的释放,释放成功和失败会通过串口进行打印。
GIVE任务:通过按下VOL_DN按键来实现计数信号量的获取。获取成功和失败会通过串口进行打印。
本章节完成的实验存放位置为“iTOP-STM32MP157开发板\iTOP-STM32MP157开发板网盘资料汇总\08_freertos实验例程\05_计数信号量实验.zip”
41.3 计数信号量实验
41.3.1建立freertos_Count工程
首先我们打开STM32CubeIDE软件,进入软件界面之后,我们点击File属性,选择NEW下的STM32 Project的选项,如下图所示:
然后我们会进入下图所示界面:在Part Number选择框输入STM32MP157A,然后在右边的选择界面选择STM32MP157AAA,然后点击Next选项
在Project Name框中输入工程名字freertos_Count,然后点击Finish选项即可,如下图所示:
等待工程创建完毕,会询问我们是否要安装OpenSTLinux ,由于我们是在windows环境下,所以我们不需要安装,点击NO即可
至此我们的工程创建完毕,进入工程界面如下图所示界面:
41.3.2串口引脚的功能配置
首先我们在下面的搜索框之中输入我们要配置的引脚,我们在这里以PB2为例进行搜索,输入名称之后,对应的引脚在工程中会闪烁,如下图所示:
然后我们使用鼠标左键点击对应的引脚会弹出PB2的复用功能选择,我们在这里选择复用为UART4_RX功能,如下图所示:
用同样的方法对PG11进行搜索,然后我们使用鼠标左键点击对应的引脚会弹出PG11的复用功能选择,我们在这里选择复用为UART4_TX功能,如下图所示:
配置完成之后打开左侧菜单的 System CoreàGPIO 进入 GPIO 模式配置界面:如下图所示:
点击对应的引脚配置之后会弹出右下方的管脚配置界面,如上图所示:
在下方会列出要配置选项的具体说明和我们要进行的配置。
选项 GPIO Pull-up/Pull-down 用来设置 IO 口是上拉/下拉/没有上下拉。本实验我们设置为上拉(Pull-up)。
选项 GPIO mode用来设置 GPIO 口的模式,这里默认为alternate function,也就是复用功能。
选项 User Label 是用来设置初始化的 IO 口 Pin 值为我们自定义的宏,这里我们填写为 UART_RX。按照如上要求设置后的界面如下(由于PG11的配置相同,只是最后的Label值不同,也在下方列了出来):
、
而PG11和PB2的设置相似,只是多了出了一个选项:选项 Mzximum ouput speed 用来设置 IO 口输出速度为低速(Low)/中速(Medium)/高速 (Hign)/快速(Very High)。我们设置为高速Very High 。
41.3.3时钟的配置
我们本次实验所采用的时钟为外部时钟HSE,所以我们要在左侧属性栏中的System Core 属性下找到RCC将High Speed Clock选择为Crystal/Ceramic Resonator(晶体/陶瓷晶振)。如下图所示:
然后在Clock Configuration里我们选择 HSE,作为锁相环 PLL3P 的时钟源,在 MCU 子系统时钟里输入 209 并回车,软件会自动设置相应的倍频和分频,如下图所示:
设置完成之后,如下图所示,然后再手动配置 APB1DIV、APB2DIV 和 APB3DIV的分频值为 2。当 APB1DIV 的分频数大于 1 的时候,基本定时器的倍频器倍频值始终为 2,所以基本定时器的时钟频率为 209MHz。
41.3.4 配置 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默认创建的任务,添加任务名字为TAKE的工程,修改优先级为最低osPriorityLow如下图所示:
我们只需要修改任务名称和设置线程函数名即可,修改完成之后点击OK按钮,随后我们以同样的方式,创建任务名字为GIVE的任务,创建完成如下图所示:
随后我们来到Timers and Semaphores选项界面(即软件定时器和信号界面),在该页面中总共有三个任务栏,分别为定时器、二值信号量、计数信号量,如下图所示,然后我们点击下方的二值信号量的Add添加按钮如下图所示:
弹出队列的添加窗口之后,对默认的Semaphore Name进行修改,修改为CountingSem,然后对于计数量Count我们修改为4,然后我们点击确定按钮,如下图所示:
配置完成之后我们需要在Project Manage下的Code Generator选项下勾选 Generate peripheral initialization as a pair of ".c/.h' files per peripheral 选项,这样可以独立生成对应外设的初始化.h 和.c 文件(方便配置的查看),如下图所示:
41.4.5工程的生成与完善
在上述的步骤完成之后,按下键盘的“Ctrl+S”组合键保存保存 freertos_Count.ioc 文件,系统开始生成初始化代码,此处会弹出一个警告,提示我们 Systick 定时器已被 HAL 库占用,在 STM32MP157 Cortex-M4 内核上我们更换不了其他的定时器,选择 Yes 继续生成代码即可。
工程生成之后如下图所示:
然后我们进行工程的完善,以及添加对应的逻辑代码。
41.3.5.1 对应文件与文件夹的添加
由于我们在裸机章节已经完善了对应的LED,BEEP和KEY文件,所以我们将iTOP-STM32MP157开发板网盘资料汇总\06_Cortex-M4实验例程\03_KEY\KEY\CM4\Core\BSP文件拷贝到当前工程对应的位置,拷贝完成如下下图所示:
41.3.5.2 uart.h文件的完善
首先打开uart.h文件,文件存放位置如下图所示:
进入uart.h文件后在/* USER CODE BEGIN Private defines */和/* USER CODE END Private defines */之间添加定义
uint8_t RxBuffer;
用来设置串口发送和接收缓冲区的大小。添加完成如下图所示:
41.3.5.3 uart.c文件的完善
首先打开uart.h文件,文件存放位置如下图所示:
进入uart.c文件后在/* USER CODE BEGIN 1 */和/* USER CODE END 1 */之间添加回调函数,在没有编写回调函数的时候,串口只能完成一次发送和接收,在完成一次中断接收以后,就关闭串口中断了,该回调函数的作用是实现字符的循环发送和接收,添加内容如下:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
HAL_UART_Transmit(&huart4,&RxBuffer,1,0);
HAL_UART_Receive_IT(&huart4,&RxBuffer,1);
}
添加完成之后如下图所示:
41.3.5.4 main.c文件的完善
我们要修改的main.c文件路径如下图所示:
打开main.c文件,在 /* USER CODE BEGIN 2 */和/* USER CODE END 2 */之间添加以下内容:
HAL_UART_Receive_IT(&huart4,&RxBuffer,1);
在/* USER CODE BEGIN 4 */和/* USER CODE END 4 */之间添加以下内容:
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{while ((UART4->ISR & 0X40) == 0);UART4->TDR = (uint8_t) ch;return ch;
}
该函数的目的是实现串口输出重定向,重定向是指将fputc里面的输出指向目标设备。因printf函数调用了fputc,而fputc输出有默认指向的目标,且不同库中的fputc输出指向不同,所以需要重写fputc。
41.3.5.5 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/key.h"
添加完成如下图所示:
然后我们来到文件的底部可以看到我们创建的TAKE_Task 和GIVE_Task任务
修改TAKE_Task 任务的for循环中的内容,修改内容如下:
uint8_t key;osStatus_t xReturn = osOK ;for(;;){key = key_scan();if (key){switch (key){case VOL_UP_PRES:xReturn = osSemaphoreAcquire(CountingSemHandle,0);LED2_TOGGLE();if(osOK == xReturn){printf("Conuting_Handle Task Success!\n");}else{printf("Conuting_Handle Take Error!\n");}break;}}}osDelay(20);
修改完成如下图所示:
随后我们修改GIVE_Task 任务的内容,修改内容如下:
uint8_t key;osStatus_t xReturn = osOK ;for(;;){key = key_scan();if (key){switch (key){case VOL_DN_PRES:xReturn =osSemaphoreRelease(CountingSemHandle);LED3_TOGGLE();if(osOK == xReturn){printf("Conuting_Handle Release Success!\n");}else{printf("Conuting_Handle Release Error!\n");}break;}}}osDelay(20);
至此,我们的内容就添加完成了。之后进行编译烧写,当我们对VOL_UP和VOL_DN进行按下时,可以看到串口的打印信息如下图所示: