| 
 
| 一、TIMER概述 定时器分类:基本定时器、通用定时器(输出比较、输入捕获)、高级定时器(PWM互补输出、死区及刹车功能);
 
 时基单元:
 
 计数器:16位计数器,从0计数累加到自动重装载计数值产生溢出,从0重新开始计数;
 
 预分频器:对时钟源进行分频,降低计数频率;
 
 自动重装载寄存器:定时器计数周期。
 
 输出比较:
 
 强制输出模式:通用定时器输出直接由软件将输出比较信号强制设置为有效电平、无效电平或输出不变;
 
 比较输出模式:根据捕获/比较寄存器和计数器比较结果输出为有效电平、无效电平或者电平翻转;
 
 PWM输出模式:根据捕获/比较寄存器和计数器比较结果输出为有效电平、无效电平。
 
 输入捕获:设置捕获寄存器,通过边沿中断判断状态值;
 
 二、HWTIMER定时回调
 1、CubeMX驱动生成
 根据使用定时器选择TIMX:
 
 
   
 2、使用HWTIMER设备驱动程序
 
 
   
 注意:支持哪些定时器需要查看drivers/include/config/tim_config.h,查找设备时根据.name匹配:
 
 
   
 确保HAL库hal_conf.h功能模块使能(CubeMX会自动解除注释):
 
 #define HAL_TIM_MODULE_ENABLED
 
 添加tim.c硬件驱动到drivers/board.c:
 
 void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
 
 {
 
 if(tim_baseHandle->Instance==TIM3)
 
 {
 
 /* USER CODE BEGIN TIM3_MspInit 0 */
 
 /* USER CODE END TIM3_MspInit 0 */
 
 /* TIM3 clock enable */
 
 __HAL_RCC_TIM3_CLK_ENABLE();
 
 /* USER CODE BEGIN TIM3_MspInit 1 */
 
 /* USER CODE END TIM3_MspInit 1 */
 
 }
 
 }
 
 修改drivers/board.h:
 
 #define BSP_USING_TIM
 
 #ifdef BSP_USING_TIM
 
 #define BSP_USING_TIM3
 
 /*#define BSP_USING_TIM15*/
 
 /*#define BSP_USING_TIM16*/
 
 /*#define BSP_USING_TIM17*/
 
 #endif
 
 3、使用示例
 #include <rtthread.h>
 #include <rtdevice.h>
 
 static rt_err_t timeoutCallBack(rt_device_t dev, rt_size_t size);
 bool timerInit(char *devName, uint32_t sec, uint32_t usec)
 {
 // 查找定时器设备
 rt_device_t devTimer = rt_device_find(devName);
 if (devTimer == RT_NULL) {
 rt_kprintf("rt_device_find[%s] failed!\n", devName);
 return false;
 }
 // 打开定时器设备
 rt_err_t ret = rt_device_open(devTimer, RT_DEVICE_OFLAG_RDWR);
 if (ret != RT_EOK) {
 rt_kprintf("rt_device_open[%s] failed!\n", devName);
 return false;
 }
 // 设置超时回调函数
 rt_device_set_rx_indicate(devTimer, timeoutCallBack);
 // 设置计数频率(默认1Mhz或支持的最小计数频率)
 rt_uint32_t freq = 10000;
 ret = rt_device_control(devTimer, HWTIMER_CTRL_FREQ_SET, &freq);
 if (ret != RT_EOK) {
 rt_kprintf("rt_device_control[%s] freq failed!\n", devName);
 return false;
 }
 // 设置模式为周期性定时器
 rt_hwtimer_mode_t mode = HWTIMER_MODE_PERIOD;
 ret = rt_device_control(devTimer, HWTIMER_CTRL_MODE_SET, &mode);
 if (ret != RT_EOK) {
 rt_kprintf("rt_device_control[%s] mode failed!\n", devName);
 return false;
 }
 // 设置定时器超时值并启动定时器
 rt_hwtimerval_t timeout_s;
 timeout_s.sec = sec;
 timeout_s.usec = usec;
 if (rt_device_write(devTimer, 0, &timeout_s, sizeof(timeout_s)) != sizeof(timeout_s)) {
 rt_kprintf("rt_device_write[%s] failed!\n", devName);
 return false;
 }
 
 rt_kprintf("timer init ok!\n");
 
 return true;
 }
 
 static rt_err_t timeoutCallBack(rt_device_t dev, rt_size_t size)
 {
 
 return RT_EOK;
 }
 
 三、HWTIMER输出比较
 1、输出比较种类
 强制输出模式:
 
 强制输出模式是指通用定时器的输出直接由软件将输出比较信号(OxRef和OCx)强制设置为有效电平、无效电平或者输出不变。无需考虑捕获/比较寄存器(CCRx)和计数器(CNT)之间的任何比较结果;
 
 比较输出模式:
 
 比较输出模式的输出结果和捕获/比较寄存器(CCRx)和计数器(CNT)之间比较结果相关,当这两个值相等的时候,输出结果为有效电平、无效电平或者电平翻转;
 
 PWM输出模式:
 
 PWM输出模式是输出比较的一个特例,输出结果和捕获/比较寄存器(CCRx)和计数器(CNT)之间的比较值影响,输出结果为有效电平、无效电平。
 
 注:有效电平和无效电平都可以是高电平也可以是低电平,但有效电平和无效电平两者一定是相反值。
 
 2、输出比较框图
 
 
   
 比较输出模式下,捕获/比较寄存器(CCRx)的值由用户自行设置,如果计数器(CNT)的值大于或等于捕获/比较寄存器(CCRx)中的值时,会产生对应的标志或者中断。
 
 3、PWM输出模式概述
 PWM是脉冲宽度调制的简称,在输出频率不变的情况下,通过调节其占空比(高电平占整个脉冲周期的比值),从而达到调节输出的目的。
 
 通用定时器PWM模式可以产生一个信号,这个信号的频率周期是由自动重装载寄存器中的值决定,信号的占空比则由捕获/比较寄存器中的值决定。
 
 PWM输出模式应用:
 
 控制灯光:七彩灯(RGB灯)、灯光亮度;
 
 控制电机:直流电机、步进电机、无刷电机;
 
 红外遥控:红外发射;
 
 电池充电:控制充电电流。
 
 4、CubeMX驱动生成
 查找手册查看GPIO支持复用定时器输出比较通道:
 
 
   
 or:
 
 
   
 注意:支持哪些定时器需要查看drivers/include/config/pwm_config.h,查找设备时根据.name匹配,如果默认没有则自定义添加:
 
 
   
 自定义添加示例:
 
 #ifdef BSP_USING_PWM14
 
 #ifndef PWM14_CONFIG
 
 #define PWM14_CONFIG                            \
 
 {                                           \
 
 .tim_handle.Instance     = TIM14,        \
 
 .name                    = "pwm14",      \
 
 .channel                 = 0             \
 
 }
 
 #endif /* PWM14_CONFIG */
 
 #endif /* BSP_USING_PWM14 */
 
 如果.channel不指定且drivers/drv_pwm.c中未预定义通道,则不起作用(注意位移不要搞错):
 
 
   
 5、使用PWM设备驱动程序
 
 
   
 确保HAL库hal_conf.h功能模块使能(CubeMX会自动解除注释):
 
 #define HAL_TIM_MODULE_ENABLED
 
 添加tim.c硬件驱动到drivers/board.c:
 
 void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
 
 {
 
 if(tim_baseHandle->Instance==TIM3)
 
 {
 
 /* USER CODE BEGIN TIM3_MspInit 0 */
 
 /* USER CODE END TIM3_MspInit 0 */
 
 /* TIM3 clock enable */
 
 __HAL_RCC_TIM3_CLK_ENABLE();
 
 /* USER CODE BEGIN TIM3_MspInit 1 */
 
 /* USER CODE END TIM3_MspInit 1 */
 
 }
 
 else if(tim_baseHandle->Instance==TIM14)
 
 {
 
 /* USER CODE BEGIN TIM14_MspInit 0 */
 
 /* USER CODE END TIM14_MspInit 0 */
 
 /* TIM14 clock enable */
 
 __HAL_RCC_TIM14_CLK_ENABLE();
 
 /* USER CODE BEGIN TIM14_MspInit 1 */
 
 /* USER CODE END TIM14_MspInit 1 */
 
 }
 
 }
 
 void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
 
 {
 
 GPIO_InitTypeDef GPIO_InitStruct = {0};
 
 if(timHandle->Instance==TIM3)
 
 {
 
 /* USER CODE BEGIN TIM3_MspPostInit 0 */
 
 /* USER CODE END TIM3_MspPostInit 0 */
 
 __HAL_RCC_GPIOB_CLK_ENABLE();
 
 /**TIM3 GPIO Configuration
 
 PB0     ------> TIM3_CH3
 
 */
 
 GPIO_InitStruct.Pin = GPIO_PIN_0;
 
 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
 
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
 
 GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
 
 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
 
 /* USER CODE BEGIN TIM3_MspPostInit 1 */
 
 /* USER CODE END TIM3_MspPostInit 1 */
 
 }
 
 ... ...
 
 }
 
 修改drivers/board.h:
 
 /*#define BSP_USING_PWM1*/
 
 /*#define BSP_USING_PWM2*/
 
 /*#define BSP_USING_PWM3*/
 
 #define BSP_USING_PWM14
 
 #define BSP_USING_PWM14_CH1
 
 6、使用示例
 #include <rtthread.h>
 #include <rtdevice.h>
 
 struct rt_device_pwm *pwmdev;
 bool pwmInit(char *devName, int channel, rt_uint32_t period, rt_uint32_t pulse)
 {
 pwmdev = (struct rt_device_pwm *)rt_device_find(devName);
 if (pwmdev == RT_NULL) {
 rt_kprintf("rt_device_find[%s] failed!\n", devName);
 return false;
 }
 rt_pwm_set(pwmdev, channel, period, pulse);
 rt_pwm_enable(pwmdev, channel);
 
 return true;
 }
 
 四、HWTIMER输入捕获
 1、输入捕获概述
 通用定时器输入捕获用于对输入信号脉冲宽度测量(测量时钟脉冲信号的周期以及时钟脉冲信号的占空比)。
 
 输入捕获框图:
 
 
   
 
   
 总结:通过检测TIMx_CHx边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)时,将当前定时器的计数值(TIMx_CNT)保存到对应通道的捕获/比较寄存器,完成一次捕获。
 
 2、CubeMX驱动生成
 定时器通道配置:
 
 
   
 中断使能:
 
 
   
 3、 使用示例
 #include <rtthread.h>
 #include <rtdevice.h>
 
 #include "tim.h"
 
 #define DBG_TAG "main"
 #define DBG_LVL DBG_LOG
 #include <rtdbg.h>
 
 // 中断服务函数
 // 默认生成到cubemx/Src/stm32f4xx_it.c,由于该文件未添加到SConscript,所以单独摘出来
 void TIM3_IRQHandler(void)
 {
 /* USER CODE BEGIN TIM3_IRQn 0 */
 
 /* USER CODE END TIM3_IRQn 0 */
 HAL_TIM_IRQHandler(&htim3);
 /* USER CODE BEGIN TIM3_IRQn 1 */
 
 /* USER CODE END TIM3_IRQn 1 */
 }
 
 // 输入捕获中断回调函数
 uint16_t last_capture = 0;
 void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
 {
 // 周期计算 - 100Hz的方波意味着每个周期的时间应该是10ms(10000微秒)
 if (htim->Instance == TIM3 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
 uint16_t capture = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
 uint16_t diff = (capture >= last_capture) ? (capture - last_capture) : ((0xFFFF - last_capture) + capture + 1);
 last_capture = capture;
 
 rt_kprintf("Capture: %d, Diff: %d\n", capture, diff);
 }
 }
 
 int main(void)
 {
 // TIM3初始化
 MX_TIM3_Init();
 // TIM3_CH1中断使能
 if (HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1) != HAL_OK) {
 rt_kprintf("HAL_TIM_IC_Start_IT error!\n");
 // 启动错误处理
 Error_Handler();
 }
 
 while (1) {
 rt_kprintf("status: %d\n", 1);
 rt_thread_mdelay(1000);
 }
 
 return RT_EOK;
 }
 
 
 
 
 ————————————————
 
 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
 
 原文链接:https://blog.csdn.net/dxf1971738522/article/details/146058720
 
 
 | 
 |