当前位置: 首页 > news >正文

采用STM32CubeMX和HAL库的定时器应用实例

目录

STM32的通用定时器配置流程

定时器应用的硬件设计

定时器应用的软件设计

1.    通过STM32CubeMX新建工程 通过STM32CubeMX新建工程的步骤如下:

2. 通过Keil MDK实现工程 通过Keil MDK实现工程的步骤如下:


STM32的通用定时器配置流程

通用定时器具有多种功能,但其原理大致相同,但其流程有所区别,以使用中断方式为例,主要包括三部分,即NVIC设置、TIM中断配置、定时器中断服务程序。 对每个步骤通过库函数的实现方式描述。首先要提到是,定时器相关的库函数主要中在HAL库文件stm32f1xx_hal_tim.h 和stm32f1xx_hal_tim.c文件中。 定时器配置步骤如下:

(1)TIM3时钟使能。 HAL中定时器使能是通过宏定义标识符实现对相关寄存器操作的,方法如下:

__HAL_RCC_TIM3_CLK_ENABLE(); //使能TIM3时钟

(2)初始化定时器参数,设置自动重装值,分频系数,计数方式等。 在HAL库中,定时器的初始化参数是通过定时器初始化函数HAL_TIM_Base_Init 实现的: HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim); 该函数只有一个入口参数,就是TIM_HandleTypeDef类型结构体指针。

(3)使能定时器更新中断,使能定时器。 HAL库中,使能定时器更新中断和使能定时器两个操作可以在函数HAL_TIM_Base_Start_IT()中一次完成的,该函数声明如下:

HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim); 该函数非常好理解,只有一个入口参数。调用该定时器之后,会首先调用__HAL_TIM_ENABLE_IT宏定义使能更新中断,然后调用宏定义__HAL_TIM_ENABLE 使能相应的定时器。

这里分别列出单独使能/关闭定时器中断和使能/关闭定时器方法:

__HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);//使能句柄指定的定时器更新中断 __HAL_TIM_DISABLE_IT (htim, TIM_IT_UPDATE);//关闭句柄指定的定时器更新中断 _HAL_TIM_ENABLE(htim);//使能句柄 htim 指定的定时器

__HAL_TIM_DISABLE(htim);//关闭句柄 htim 指定的定时器

(4)TIM3 中断优先级设置。 在定时器中断使能之后,因为要产生中断,必不可少的要设置 NVIC 相关寄存器,设置中断优先级。之前多次讲解到中断优先级的设置,这里就不重复讲解。 和串口等其他外设一样,HAL 库为定时器初始化定义了回调函数 HAL_TIM_Base_MspInit。 一般情况下,与MCU有关的时钟使能,以及中断优先级配置都会放在该回调函数内部。 函数声明如下: void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim); 对于回调函数,这里不做过多讲解,只需要重写这个函数即可。

(5)编写中断服务函数。 在最后,还是要编写定时器中断服务函数,通过该函数处理定时器产生的相关中断。通常情况下,在中断产生后,通过状态寄存器的值判断此次产生的中断属于什么类型。然后执行相关的操作,这里使用的是更新(溢出)中断,所以在状态寄存器SR的最低位。在处理完中断之后应该向TIM3_SR的最低位写 0,清除该中断标志。

定时器应用的硬件设计

本实例利用基本定时器TIM6/7定时1s,1s时间到LED翻转一次。基本定时器是单片机内部的资源,没有外部IO,不需要接外部电路,只需要一个LED灯即可。

定时器应用的软件设计

在HAL库函数头文件stm32f1xx_hal_tim.h中对定时器外设建立了四个初始化结构体,基本定时器只用到其中一个即TIM_TimeBaseInitTypeDef,其实现如下:

typedef struct {
uint32_t Prescaler; // 预分频器
uint32_t CounterMode; // 计数模式
uint32_t Period; // 定时器周期
uint32_t ClockDivision; // 时钟分频
uint32_t RepetitionCounter; // 重复计算器
uint32_t AutoReloadPreload; // 自动预装载
} TIM_TimeBaseInitTypeDef;

这些结构体成员说明如下,其中括号内的文字是对应参数在STM32 HAL库中定义的宏:

(1)Prescaler:定时器预分频器设置,时钟源经该预分频器才是定时器时钟,它设定TIMx_PSC寄存器的值。可设置范围为0至65535,实现1至65536分频。

(2)CounterMode:定时器计数方式,可设置为向上计数、向下计数以及中心对齐模式。基本定时器只能是向上计数,即TIMx_CNT只能从0开始递增,并且无须初始化。

(3)Period:定时器周期,实际就是设定自动重载寄存器的值,在事件生成时更新到影子寄存器。可设置范围为0至65535。

(4)ClockDivision:时钟分频,设置定时器时钟CK_INT频率与数字滤波器采样时钟频率分频比,基本定时器没有此功能,不用设置。

(5)RepetitionCounter:重复计数器,属于高级控制寄存器专用寄存器位,利用它可以非常容易控制输出PWM的个数。这里不用设置。

(6)AutoReloadPreload:计数器在计满一个周期之后会自动重新计数,也就是默认会连续运行。连续运行过程中如果修改了Period,那么根据当前状态的不同有可能发生超出预料的过程。如果使能了AutoReloadPreload,那么对Period的修改将会在完成当前计数周期后才更新。这里不用设置。

下面讲述如何通过STM32CubeMX新建工程、如何通过Keil MDK实现工程。

1.    通过STM32CubeMX新建工程 通过STM32CubeMX新建工程的步骤如下:

(1)新建文件夹   Demo目录下新建文件夹TIMER,这是保存本章新建工程的文件夹。

(2)新建STM32CubeMX工程 在STM32CubeMX开发环境中新建工程。

(3)选择MCU或开发板 Commercial Part Number和MCUs/MPUs List选择STM32F103ZET6,选择Start Project启动工程。

(4)保存STM32Cube MX工程 使用STM32CubeMX菜单File→Save Project,保存工程。

(5)生成报告 使用STM32CubeMX菜单File→Generate Report生成当前工程的报告文件。

(6)配置MCU时钟树 STM32CubeMX Pinout & Configuration子页面下,选择System Core→RCC,High Speed Clock(HSE)根据开发板实际情况,选择Crystal/Ceramic Resonator(晶体/陶瓷晶振)。 STM32CubeMX切换到Clock Configuration子页面下,根据开发板外设情况配置总线时钟。此处配置PLL Source Mux为HSE,PLLMul为9倍频72MHz,System Clock Mux为PLLCLK,APB1 Prescaler为X2,其余默认设置即可。

(7)配置MCU外设 根据LED电路,整理出MCU连接的GPIO引脚的输入/输出配置

再根据表进行GPIO引脚配置,具体步骤如下。 STM32CubeMX Pinout & Configuration子页面下选择System Core→GPIO,对使用的GPIO口进行设置。LED输出端口LED1_RED(PB5)

STM32CubeMX Pinout & Configuration子页面下选择Timers→TIM6,对TIM6进行设置。Mode选择Activated,定时器预分频系数71,时钟频率为1MHz;BASIC_TIMx_PERIOD为1000,定时器计数周期为1ms

切换到STM32CubeMX Pinout & Configuration子页面下选择System Core→NVIC,修改Priority Group为2 bits for pre-emption priority(2位抢占优先级),Enabled栏勾选TIM3 global interrupt,修改Preemption Priority(抢占优先级)为0,Sub Priority(子优先级)为3

Code Generation页面Select for init sequence ordering栏勾选TIM6 global interrupt

(8)配置工程 STM32CubeMX Project Manager子页面Project栏下Toolchain/IDE选择MDK-Arm,Min Version选择V5,可生成Keil MDK工程;选择STM32CubeIDE,可生成STM32CubeIDE工程。

(9)生成C代码工程 STM32CubeMX主页面,单击GENERATE CODE按钮生成C代码工程。

2. 通过Keil MDK实现工程 通过Keil MDK实现工程的步骤如下:

(1)打开工程   打开TIMER\MDK-Arm文件夹下的工程文件。

(2)编译STM32CubeMX自动生成的MDK工程 在MDK开发环境中通过菜单Project→Rebuild all target files或工具栏      Rebuild按钮编译工程。

(3)STM32CubeMX自动生成的MDK工程 main.c文件中函数main()依次调用了HAL_Init()函数用于复位所有外设,初始化Flash接口和Systick定时器。SystemClock_Config()函数用于配置各种时钟信号频率。MX_GPIO_Init()函数初始化GPIO引脚。 文件gpio.c包含了函数MX_GPIO_Init()的实现代码,如下。

void MX_GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOB_CLK_ENABLE();/*Configure GPIO pin Output Level */HAL_GPIO_WritePin(LED1_RED_GPIO_Port, LED1_RED_Pin, GPIO_PIN_SET);/*Configure GPIO pin : PtPin */GPIO_InitStruct.Pin = LED1_RED_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_PULLUP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(LED1_RED_GPIO_Port, &GPIO_InitStruct);
}

main()函数外设初始化函数MX_TIM6_Init(),它是TIM6的初始化函数。MX_TIM6_Init()是在文件time.c中定义的函数,它的代码里调用了函数HAL_TIM_Base_Init()实现STM32CubeMX配置的定时器设置。MX_TIM6_Init()实现的代码如下。

void MX_TIM6_Init(void)
{TIM_MasterConfigTypeDef sMasterConfig = {0};htim6.Instance = TIM6;htim6.Init.Prescaler = 71;htim6.Init.CounterMode = TIM_COUNTERMODE_UP;htim6.Init.Period = 1000;htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;if (HAL_TIM_Base_Init(&htim6) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK){Error_Handler();}
}
函数MX_NVIC_Init()实现中断的初始化,代码如下
static void MX_NVIC_Init(void)
{/* TIM6_IRQn interrupt configuration */HAL_NVIC_SetPriority(TIM6_IRQn, 0, 3);HAL_NVIC_EnableIRQ(TIM6_IRQn);
}

(4)新建用户文件   在TIMER\Core\Src下新建bsp_led.c,在TIMER\Core\Inc下新建bsp_led.h。将bsp_led.c添加到工程Application/User/Core文件夹下。

(5)编写用户代码 bsp_led.h和bsp_led.c文件实现LED操作的宏定义和LED初始化。 timer.c文件MX_TIM6_Init()函数使能TIM6和更新中断。   /* USER CODE BEGIN TIM6_Init 2 */   HAL_TIM_Base_Start_IT(&htim6);   /* USER CODE END TIM6_Init 2 */ timer.c文件添加中断回调函数HAL_TIM_PeriodElapsedCallback(),对计数器time加1。

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim==(&htim6))time++;     
}
main.c文件添加对用户自定义头文件的引用。
/* Private includes ---------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "bsp_led.h"
/* USER CODE END Includes */
main.c文件添加计数器time的定义。
/* USER CODE BEGIN PV */
volatile uint32_t time = 0; // ms 计时变量 
/* USER CODE END PV */
main.c文件添加对LED1的取反操作/* Infinite loop *//* USER CODE BEGIN WHILE */while (1){if ( time == 1000 ) /* 1000 * 1 ms = 1s 时间到 */{time = 0;/* LED1 取反 */      LED1_TOGGLE; } /* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */

(6)重新编译工程   重新编译添加代码后的工程。

(7)配置工程仿真与下载项 在MDK开发环境中通过菜单Project→Options for Target或工具栏     配置工程。

打开Debug选项卡,选择使用的仿真下载器ST-Link Debugger。Flash Download下勾选Reset and Run选项。单击确定。

(8)下载工程   连接好仿真下载器,开发板上电。 在MDK开发环境中通过菜单Flash→Download或工具栏    下载工程。 工程下载完成后,可以看到LED1以1s的频率闪烁一次


http://www.mrgr.cn/news/61579.html

相关文章:

  • day03|计算机网络重难点之HTTP中常见的状态码、什么是强缓存和协商缓存
  • modelsim命令:abort
  • mac|安装redis及RedisDesk可视化软件
  • 代码随想录训练营Day10 | 二叉树的遍历
  • 工具安装前必须知道的事【必看】
  • vue 果蔬识别系统百度AI识别vue+springboot java开发、elementui+ echarts+ vant开发
  • 【编程语言】在C++中使用map与unordered_map
  • c语言中结构体传参和实现位段
  • unseping攻防世界
  • 百度二面算法:合法的括号字符串(贪心解法)
  • 【机器学习】环境搭建及Sklearn鸢尾花数据集
  • Python | Leetcode Python题解之第519题随机翻转矩阵
  • Python中的切片是什么,它有什么用处?
  • 25_DNS:域名系统详解
  • C++ | Leetcode C++题解之第519题随机翻转矩阵
  • windows 驱动实例分析系列: NDIS 6.0的Filter 驱动改造(四)
  • Java | Leetcode Java题解之第520题检测大写字母
  • Linux(一)
  • 从0开始搭建一个生产级SpringBoot2.0.X项目(五)使用 validation 验证参数
  • C++核心编程和桌面应用开发 第十七天(set和multiset容器 pair map和multimap容器)
  • Json库和文件操作
  • Cargo 的工作机制
  • 一道巧妙的卡特兰数建模
  • 聊聊解构的那些事
  • 本篇文章来介绍下dockerfile
  • LeetCode 热题 100 回顾2