一、TIM的HAL库用法
这里记录学习HAL库开发TIM
1.1 定时中断基本结构
这里给出定时中断的基本结构
基本步骤如下
- 开启时钟
- 选择时基单元时钟源
- 配置时基单元
- 配置输出中断控制,允许更新中断输出到NVIC
- 配置NVIC,打开定时器中断通道
- 运行控制
- 编写中断服务函数
1.2 常用API
在stm32f4xx_hal_tim.c
文件和stm32f4xx_hal_tim.h
文件中,给出了定时器相关库函数,通过以下API使用定时器,TIM默认时钟是内部时钟,即APB总线时钟
HAL_TIM_xxx_MspInit()
配置底层- 底层初始化具体实现,包括时钟使能、中断、DMA等
HAL_TIM_xxx_Init()
配置定时器工作模式HAL_TIM_xxx_Start()
开启定时器外设
二、案例演示
2.1 基本定时器
这里以基本定时器TIM6为例,实现0.5s定时中断,中断服务函数为LED翻转并打印hello!
,具体实现如下
- 定时器配置
- 定时器底层驱动初始化
- 开启定时器
- 中断服务函数
源文件代码如下
/*
*********************************************************************************************************
* Module Description
*
* 定时器模块和PWM驱动模块
* - 实现基本定时器TIM6的定时中断,中断服务函数为LED翻转
*********************************************************************************************************
*/
#include "bsp.h"/* private variables */
TIM_HandleTypeDef TIM_TimeBaseStructure;/* private function prototypes */
void basic_tim6_Config(uint16_t _arr, uint16_t _psc);/*
**********************************************************************************
* @brief 定时器初始化
* @param None
* @return None
* @note
**********************************************************************************
*/
void bsp_InitTim()
{basic_tim6_Config(5000 - 1, 8400 - 1); // 0.5s定时器
}/*
**********************************************************************************
* @brief 将TIM6配置为定时器
* - 0.5s产生一次中断
* - 终端服务函数实现LED翻转
* @param 零个参数
* - _arr:自动重装值
* - _psc:预分频系数
* @return None
* @note
**********************************************************************************
*/
void basic_tim6_Config(uint16_t _arr, uint16_t _psc)
{TIM_TimeBaseStructure.Instance = BASIC_TIM; // 定时器基地址TIM_TimeBaseStructure.Init.Prescaler = _psc; // 预分频系数TIM_TimeBaseStructure.Init.CounterMode = TIM_COUNTERMODE_UP; // 递增计数模式TIM_TimeBaseStructure.Init.Period = _arr; // 自动重装载值if(HAL_TIM_Base_Init(&TIM_TimeBaseStructure) != HAL_OK) // 配置为简单的时基模式{Error_Handler();}HAL_TIM_Base_Start_IT(&TIM_TimeBaseStructure); // 使能定时器和定时器更新中断}/*
**********************************************************************************
* @brief 定时器底层驱动
* - 开启时钟
* - 开启中断
* @param None
* @return None
* @note
**********************************************************************************
*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{if(htim->Instance == BASIC_TIM){BASIC_TIM_CLK_ENABLE(); // 使能TIM6时钟HAL_NVIC_SetPriority(BASIC_TIM_IRQn, 1, 3); // 设置优先级HAL_NVIC_EnableIRQ(BASIC_TIM_IRQn); // 开启中断}
}/*
**********************************************************************************
* @brief 基本定时器TIM6中断服务函数
* @param None
* @return None
* @note
**********************************************************************************
*/
void TIM6_DAC_IRQHandler()
{HAL_TIM_IRQHandler(&TIM_TimeBaseStructure);
}/*
**********************************************************************************
* @brief 中断回调函数
* @param
* @return
* @note
**********************************************************************************
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == BASIC_TIM){bsp_LedToggle(1);bsp_LedToggle(2);printf("hello!");}
}
头文件如下
#ifndef __BSP_TIM_PWM_H
#define __BSP_TIM_PWM_H/* private define */
#define BASIC_TIM TIM6
#define BASIC_TIM_CLK_ENABLE() do{__TIM6_CLK_ENABLE();}while(0)
#define BASIC_TIM_IRQn TIM6_DAC_IRQn/* public statement */
void bsp_InitTim();#endif
2.2 通用定时器
输出比较模式中,可比较CNT和CCR的值,来对输出电平进行控制,可用于输出PWM波。输出PWM波主要取决于两个寄存器,ARR控制频率;CCR控制占空比。输出比较模式总共有八种
这里讲解PWM模式,由上图可知PWM1和PWM2区别是REF电平相反,其他都相同。有两种模式,边沿对齐模式和中心对齐模式可用于产生PWM波
-
边沿对齐模式
这里使用的是PWM1、向上计数模式,ARR=8
那么计数器寄存器从0计数到8共9个单位。可以看到,设置CCR=4
在 C N T ≤ C C R CNT\leq CCR CNT≤CCR时REF输出高电平,在 C N T ≥ C C R CNT\geq CCR CNT≥CCR时输出低电平,其他CCR
值以此类推。 -
中心对齐模式
这里使用的是PWM1、上/下计数模式,ARR=8
那么计数器寄存器从0计数到8后又从8计数到0共16个单位。可以看到,设置CCR=4
在 C N T ≤ C C R CNT\leq CCR CNT≤CCR时REF输出高电平,在 C N T ≥ C C R CNT\geq CCR CNT≥CCR时输出低电平,其他CCR
值以此类推。
综上可以得出PWM基本结构总结如下
相应的参数计算如下
- PWM信号的频率为 C K _ P S C ( P S C + 1 ) ⋅ ( A R R + 1 ) \frac{CK\_PSC}{(PSC+1)\cdot (ARR+1)} (PSC+1)⋅(ARR+1)CK_PSC
- PWM信号的占空比为 C C R A R R + 1 \frac{CCR}{ARR+1} ARR+1CCR
- PWM信号分辨率为 1 A R R + 1 \frac{1}{ARR+1} ARR+11
这里给出定时器TIM的回调函数,有5种,分析如下
==============================================================================##### TIM Callbacks functions #####==============================================================================[..]This section provides TIM callback functions:(+) TIM Period elapsed callback 非阻塞模式下定时器超时回调函数(+) TIM Output Compare callback 非阻塞模式下定时器输出比较回调函数(+) TIM Input capture callback (+) TIM Trigger callback(+) TIM Error callback
这里给出通用定时器PWM输出配置步骤
- 配置TIM,设置自动重装载值、预分频系数和计数模式等
- 使能TIM时钟,配置输出比较模式,开启PWM模式
- 配置TIM底层驱动,设置输出通道GPIO
具体代码实现如下
tim.c
文件
/*
*********************************************************************************************************
* Module Description
*
* 通用定时器模块
* - 使用通用定时器TIM3产生PWM波
* - 通过Channel2输出PWM
*********************************************************************************************************
*/
#include "bsp.h"/* private variables */
TIM_HandleTypeDef TIM3_HandleStructure;/* private function prototypes */
void general_tim3_Config(uint16_t _arr, uint16_t _psc);/*
**********************************************************************************
* @brief 定时器初始化
* @param None
* @return None
* @note
**********************************************************************************
*/
void bsp_InitTim()
{general_tim3_Config(500 - 1, 84 - 1);
}/*
**********************************************************************************
* @brief 将TIM3配置为定时器
* @param 两个参数
* - _arr:自动重装值
* - _psc:预分频系数
* @return None
* @note
**********************************************************************************
*/
void general_tim3_Config(uint16_t _arr, uint16_t _psc)
{TIM_OC_InitTypeDef TIM_OC_InitStructure = {0};GTIM_PWM_CLK_ENABLE(); // 使能TIM时钟TIM3_HandleStructure.Instance = GTIM_PWM; // 定时器基地址TIM3_HandleStructure.Init.Prescaler = _psc; // 预分频系数TIM3_HandleStructure.Init.Period = _arr; // 自动重装载值TIM3_HandleStructure.Init.CounterMode = TIM_COUNTERMODE_UP; // 递增计数TIM3_HandleStructure.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; // 自动重装载预装载使能if(HAL_TIM_PWM_Init(&TIM3_HandleStructure) != HAL_OK) // PWM模式初始化{Error_Handler();}TIM_OC_InitStructure.OCMode = TIM_OCMODE_PWM1; // PWM模式1TIM_OC_InitStructure.Pulse = _arr / 2; // 占空比TIM_OC_InitStructure.OCPolarity = TIM_OCPOLARITY_LOW; // 低电平有效TIM_OC_InitStructure.OCFastMode = TIM_OCFAST_DISABLE; // 失能快速输出比较HAL_TIM_PWM_ConfigChannel(&TIM3_HandleStructure, &TIM_OC_InitStructure, GTIM_PWM_CHANNEL);if(HAL_TIM_PWM_Start(&TIM3_HandleStructure, GTIM_PWM_CHANNEL) != HAL_OK) // 开启PWM模式{Error_Handler();}
}/*
**********************************************************************************
* @brief TIM3底层驱动,配置输出通道GPIO
* @param TIM_HandleTypeDef *htim
* @return None
* @note
**********************************************************************************
*/
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{if(htim->Instance == GTIM_PWM){GPIO_InitTypeDef GPIO_InitStructure;GTIM_PWM_GPIO_CLK_ENABLE(); // GPIO端口时钟使能GPIO_InitStructure.Pin = GTIM_PWM_GPIO_PIN; // PWM输出GPIO引脚配置GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; // PWM输出GPIO推挽输出GPIO_InitStructure.Alternate = GTIM_PWM_GPIO_AF; // PWM输出GPIO复用模式GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; // PWM输出GPIO端口速度HAL_GPIO_Init(GTIM_PWM_GPIO_PORT, &GPIO_InitStructure);}
}
tim.h
文件
#ifndef __BSP_GTIM_H
#define __BSP_GTIM_H/* private define */
#define GTIM_PWM_GPIO_PORT GPIOA
#define GTIM_PWM_GPIO_PIN GPIO_PIN_7
#define GTIM_PWM_GPIO_CLK_ENABLE() do{__HAL_RCC_GPIOA_CLK_ENABLE();}while(0)
#define GTIM_PWM_GPIO_AF GPIO_AF2_TIM3// TIM重映射
#define GTIM_PWM TIM3
#define GTIM_PWM_CHANNEL TIM_CHANNEL_2
#define GTIM_PWM_CCR TIM3->CCR1
#define GTIM_PWM_CLK_ENABLE() do{__TIM3_CLK_ENABLE();}while(0)
#define GTIM_PWM_IRQn TIM3_IRQn/* public statement */
void bsp_InitTim();#endif