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接口等
第四十三章 软件定时器实验
43.1 软件定时器的基本概念
定时器,是指从指定的时刻开始,经过一个指定时间,然后触发一个超时事件,用户可以自定义定时器的周期与频率。类似生活中的闹钟,我们可以设置闹钟每天什么时候响,还能设置响的次数,是响一次还是每天都响。
定时器有硬件定时器和软件定时器之分:
硬件定时器是芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。硬件定时器的精度一般很高,可以达到纳秒级别,并且是中断触发方式。
软件定时器,软件定时器是由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受硬件定时器资源限制的定时器服务,它实现的功能与硬件定时器也是类似的。使用硬件定时器时,每次在定时时间到达之后就会自动触发一个中断,用户在中断中处理信息; 而使用软件定时器时,需要我们在创建软件定时器时指定时间到达后要调用的函数。
在很多应用中,我们需要一些定时器任务,硬件定时器受硬件的限制,数量上不足以满足用户的实际需求,无法提供更多的定时器,那么可以采用软件定时器来完成,由软件定时器代替硬件定时器任务。但需要注意的是软件定时器的精度是无法和硬件定时器相比的,因为在软件定时器的定时过程中是极有可能被其他中断所打断,因为软件定时器的执行上下文环境是任务。所以,软件定时器更适用于对时间精度要求不高的任务,一些辅助型的任务。
43.2 实验目的
1)STM32CubeIDE工具软件建立freertos工程
2)学习软件定时器相关的知识与初步使用
我们的实验任务为串口打印软件定时器的信息,软件定时器来观察计数信号量的特性。
首先对实验进行分析,总共需要有一个任务,需要设置定时器的计时时间,然后在回调函数中,通过串口来进行定时时间的打印。
本章节完成的实验存放位置为“iTOP-STM32MP157开发板\iTOP-STM32MP157开发板网盘资料汇总\08_freertos实验例程\07_软件定时器实验.zip”。
43.3 软件定时器实验
43.3.1建立freertos_TIME工程
首先我们打开STM32CubeIDE软件,进入软件界面之后,我们点击File属性,选择NEW下的STM32 Project的选项,如下图所示:
然后我们会进入下图所示界面:在Part Number选择框输入STM32MP157A,然后在右边的选择界面选择STM32MP157AAA,然后点击Next选项
在Project Name框中输入工程名字freertos_TIME,然后点击Finish选项即可,如下图所示:
等待工程创建完毕,会询问我们是否要安装OpenSTLinux ,由于我们是在windows环境下,所以我们不需要安装,点击NO即可
至此我们的工程创建完毕,进入工程界面如下图所示界面:
43.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 。
43.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。
43.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默认创建的任务,添加任务名字为TIME的工程,修改优先级为最低osPriorityLow如下图所示:
创建完成如下图所示:
随后我们来到Timers and Semaphores选项界面(即软件定时器和信号界面),在该页面中总共有三个任务栏,分别为定时器、二值信号量、计数信号量,如下图所示,然后我们点击最上方软件定时器的Add添加按钮如下图所示:
弹出队列的添加窗口之后,对默认的Timer Name进行修改,修改为Timer01,然后我们点击确定按钮,如下图所示:
用同样的方法创建Timer02软件定时器,这里要注意的是,Timer02的类型为osTimeOnce,
创建完成如下图所示:
配置完成之后我们需要在Project Manage下的Code Generator选项下勾选 Generate peripheral initialization as a pair of ".c/.h' files per peripheral 选项,这样可以独立生成对应外设的初始化.h 和.c 文件(方便配置的查看),如下图所示:
43.3.5工程的生成与完善
在上述的步骤完成之后,按下键盘的“Ctrl+S”组合键保存保存 LED.ioc 文件,系统开始生成初始化代码,此处会弹出一个警告,提示我们 Systick 定时器已被 HAL 库占用,在 STM32MP157 Cortex-M4 内核上我们更换不了其他的定时器,选择 Yes 继续生成代码即可。
工程生成之后如下图所示:
然后我们进行工程的完善,以及添加对应的逻辑代码。
43.3.5.1 uart.h文件的完善
首先打开uart.h文件,文件存放位置如下图所示:
进入uart.h文件后在/* USER CODE BEGIN Private defines */和/* USER CODE END Private defines */之间添加定义
uint8_t RxBuffer;
用来设置串口发送和接收缓冲区的大小。添加完成如下图所示:
43.3.5.2 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);
}
添加完成之后如下图所示:
43.3.5.3 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。
43.3.5.4 app_freertos.c文件的完善
我们要修改的 app_freertos.c文件路径如下图所示:
打开app_freertos.c文件,然后我们来到文件的底部可以看到我们创建的TIME_Task任务
修改TIME_Task 任务的for循环上方添加以下内容:
osTimerStart (Timer01Handle, 1000);
osTimerStart (Timer02Handle, 5000);
添加完成如下图所示:
随后我们对回调函数Callback01 进行内容的添加,添加内容如下:
uint32_t tick_num1;
static uint32_t Count1;
Count1++;
tick_num1 = osKernelGetTickCount();
printf("Callback01 number is %lu \r\n", Count1);
printf("system counting =%lu \r\n", tick_num1);
添加完成如下图所示:
然后我们对回调函数Callback02 进行内容的添加,添加内容如下:
static uint32_t Count2;
uint32_t tick_num2;
Count2++;
tick_num2 = osKernelGetTickCount();
printf("Callback01 number is %lu \r\n", Count2);
printf("system counting =%lu \r\n", tick_num2);
添加完成如下图所示:
至此,我们的内容就添加完成了。之后进行编译烧写,可以看到串口打印时间的输出如下图所示: