[单片机框架][bsp层][cx32l003][bsp_tim] TIM定时器配置和使用
2026/9/14 11:46:55 网站建设 项目流程

文章目录

  • 1. TIM1 简介
  • 2. TIM1 主要特性
  • 3. 硬件平台与工程结构
  • 4. 代码实现
    • 4.1 头文件 bsp_tim.h
    • 4.2 源文件 bsp_tim.c
  • 5. 代码说明
    • 5.1 初始化流程
    • 5.2 占空比动态调节
    • 5.2 反初始化流程
    • 6. 常见问题与排查
    • 6.1 无波形输出
      • 6.2 频率不对
      • 6.3 占空比不可调
    • 6.4 多通道互相干扰
  • 7. 总结
    • 参考资料
    • 附录:宏定义配置示例

1. TIM1 简介

高级控制定时器(TIM1)由一个 16 位的自动装载计数器组成,它由一个可编程的预分频器驱动。它适合多种用途,包含测量输入信号的脉冲宽度(输入捕获),或者产生输出波形(输出比较、PWM、嵌入死区时间的互补 PWM 等)。

使用定时器预分频器和 RCC 时钟控制预分频器,可以实现脉冲宽度和波形周期从几个微秒到几个毫秒的调节

2. TIM1 主要特性

TIM1 定时器的功能包括:
⚫ 16 位向上、向下、向上/向下自动装载计数器
⚫ 16 位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为 1~65535 之间的任意数值
⚫ 多达 4 个独立通道:

  • 输入捕获
  • 输出比较
  • PWM 生成(边缘或中间对齐模式)
  • 单脉冲模式输出

⚫ 死区时间可编程的互补输出
⚫ 使用外部信号控制定时器和定时器互联的同步电路
⚫ 允许在指定数目的计数器周期之后更新定时器寄存器的重复计数器
⚫ 刹车输入信号可以将定时器输出信号置于复位状态或者一个已知状态
⚫ 如下事件发生时产生中断:

  • 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
  • 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
  • 输入捕获
  • 输出比较
  • 刹车信号输入
    ⚫ 支持针对定位的增量(正交)编码器和霍尔传感器电路
    ⚫ 触发输入作为外部时钟或者按周期的电流管理

3. 硬件平台与工程结构

本文示例基于 CX32L003 系列 MCU,使用 HAL 库进行开发。工程中通过bsp_tim.cbsp_tim.h两个文件封装 TIM1 的 PWM 输出功能,业务层通过宏定义BS_TIM1_ENBS_TIM1_PRESCALERBS_TIM1_PERIOD等配置定时器参数,实现代码复用与模块化。

4. 代码实现

4.1 头文件 bsp_tim.h

头文件声明了定时器枚举类型与 PWM 初始化/反初始化接口,供业务层调用:

/******************************************************************************** * @file bsp_tim.h * @author jianqiang.xue * @Version V1.0.0 * @Date 2021-04-19 * @brief NULL ********************************************************************************/#ifndef__BSP_TIM_H#define__BSP_TIM_H#include<stdint.h>typedefenum{BSP_TIM_0=0,BSP_TIM_1}bsp_tim_t;voidbsp_tim_pwm_init(void);voidbsp_tim_pwm_deinit(void);#endif

4.2 源文件 bsp_tim.c

源文件实现了 TIM1 的 PWM 初始化与反初始化逻辑,支持 4 路独立通道输出:

/******************************************************************************** * @file bsp_tim.c * @author jianqiang.xue * @Version V1.0.0 * @Date 2021-04-19 * @brief ********************************************************************************//* Private includes ----------------------------------------------------------*/#include<stdio.h>#include<string.h>#include"cx32l003_hal.h"#include"bsp_gpio.h"#include"bsp_tim.h"#include"business_gpio.h"#include"business_function.h"/* Private define ------------------------------------------------------------*/#defineTIM1_ENBS_TIM1_EN#defineTIM1_PRESCALERBS_TIM1_PRESCALER#defineTIM1_PERIODBS_TIM1_PERIOD#defineTIM1_CH1_PINBS_TIM1_CH1_PIN#defineTIM1_CH1_GPIO_PORTBS_TIM1_CH1_GPIO_PORT#defineTIM1_CH1_GPIO_CLK_ENABLE()BS_TIM1_CH1_GPIO_CLK_ENABLE()#defineTIM1_CH1_GPIO_CLK_DISABLE()BS_TIM1_CH1_GPIO_CLK_DISABLE()#defineTIM1_CH2_PINBS_TIM1_CH2_PIN#defineTIM1_CH2_GPIO_PORTBS_TIM1_CH2_GPIO_PORT#defineTIM1_CH2_GPIO_CLK_ENABLE()BS_TIM1_CH2_GPIO_CLK_ENABLE()#defineTIM1_CH2_GPIO_CLK_DISABLE()BS_TIM1_CH2_GPIO_CLK_DISABLE()#defineTIM1_CH3_PINBS_TIM1_CH3_PIN#defineTIM1_CH3_GPIO_PORTBS_TIM1_CH3_GPIO_PORT#defineTIM1_CH3_GPIO_CLK_ENABLE()BS_TIM1_CH3_GPIO_CLK_ENABLE()#defineTIM1_CH3_GPIO_CLK_DISABLE()BS_TIM1_CH3_GPIO_CLK_DISABLE()#defineTIM1_CH4_PINBS_TIM1_CH4_PIN#defineTIM1_CH4_GPIO_PORTBS_TIM1_CH4_GPIO_PORT#defineTIM1_CH4_GPIO_CLK_ENABLE()BS_TIM1_CH4_GPIO_CLK_ENABLE()#defineTIM1_CH4_GPIO_CLK_DISABLE()BS_TIM1_CH4_GPIO_CLK_DISABLE()#definePWM0_ENBS_PWM0_EN#definePWM1_ENBS_PWM1_EN#definePWM2_ENBS_PWM2_EN#definePWM3_ENBS_PWM3_EN#ifTIM1_ENTIM_HandleTypeDeftim1_handle_t;TIM_OC_InitTypeDeftim1_oc_init_handle_t;#endif/* Private typedef -----------------------------------------------------------*/voidbsp_tim_pwm_init(void){#ifTIM1_ENmemset(&tim1_handle_t,0,sizeof(TIM_HandleTypeDef));memset(&tim1_oc_init_handle_t,0,sizeof(TIM_OC_InitTypeDef));__HAL_RCC_TIM1_CLK_ENABLE();/* Set TIMx instance */tim1_handle_t.Instance=TIM1;tim1_handle_t.Init.Period=TIM1_PERIOD;// 重装载值tim1_handle_t.Init.Prescaler=TIM1_PRESCALER;// 预分频tim1_handle_t.Init.ClockDivision=0;// CKD 时钟分频因子(Clock division)tim1_handle_t.Init.CounterMode=TIM_COUNTERMODE_UP;// 边沿对齐模式 计数器向上计数tim1_handle_t.Init.RepetitionCounter=0;// TIM1_RCR 重复计数器的值tim1_handle_t.Init.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_DISABLE;// 禁止自动重装载HAL_TIM_PWM_Init(&tim1_handle_t);#ifPWM0_ENbsp_gpio_init_tim(TIM1_CH1_GPIO_PORT,TIM1_CH1_PIN,GPIO_AF1_TIM1_CH1);tim1_oc_init_handle_t.OCMode=TIM_OCMODE_PWM1;// PWM模式1 TIM1_CCMR1tim1_oc_init_handle_t.Pulse=TIM1_PERIOD;// CCR 捕获/比较通道HAL_TIM_PWM_ConfigChannel(&tim1_handle_t,&tim1_oc_init_handle_t,TIM_CHANNEL_1);HAL_TIM_PWM_Start(&tim1_handle_t,TIM_CHANNEL_1);#endif#ifPWM1_ENbsp_gpio_init_tim(TIM1_CH2_GPIO_PORT,TIM1_CH2_PIN,GPIO_AF1_TIM1_CH2);tim1_oc_init_handle_t.OCMode=TIM_OCMODE_PWM1;// PWM模式1 TIM1_CCMR1tim1_oc_init_handle_t.Pulse=TIM1_PERIOD;// CCR 捕获/比较通道HAL_TIM_PWM_ConfigChannel(&tim1_handle_t,&tim1_oc_init_handle_t,TIM_CHANNEL_2);HAL_TIM_PWM_Start(&tim1_handle_t,TIM_CHANNEL_2);#endif#ifPWM2_ENbsp_gpio_init_tim(TIM1_CH3_GPIO_PORT,TIM1_CH3_PIN,GPIO_AF1_TIM1_CH3);tim1_oc_init_handle_t.OCMode=TIM_OCMODE_PWM1;// PWM模式1 TIM1_CCMR1tim1_oc_init_handle_t.Pulse=TIM1_PERIOD;// CCR 捕获/比较通道HAL_TIM_PWM_ConfigChannel(&tim1_handle_t,&tim1_oc_init_handle_t,TIM_CHANNEL_3);HAL_TIM_PWM_Start(&tim1_handle_t,TIM_CHANNEL_3);#endif#ifPWM3_ENbsp_gpio_init_tim(TIM1_CH4_GPIO_PORT,TIM1_CH4_PIN,GPIO_AF1_TIM1_CH4);tim1_oc_init_handle_t.OCMode=TIM_OCMODE_PWM1;// PWM模式1 TIM1_CCMR1tim1_oc_init_handle_t.Pulse=TIM1_PERIOD;// CCR 捕获/比较通道HAL_TIM_PWM_ConfigChannel(&tim1_handle_t,&tim1_oc_init_handle_t,TIM_CHANNEL_4);HAL_TIM_PWM_Start(&tim1_handle_t,TIM_CHANNEL_4);#endif#endif}voidbsp_tim_pwm_deinit(void){#ifTIM1_EN#ifPWM0_ENHAL_TIM_PWM_Stop(&tim1_handle_t,TIM_CHANNEL_1);bsp_gpio_deinit(TIM1_CH1_GPIO_PORT,TIM1_CH1_PIN);#endif#ifPWM1_ENHAL_TIM_PWM_Stop(&tim1_handle_t,TIM_CHANNEL_2);bsp_gpio_deinit(TIM1_CH2_GPIO_PORT,TIM1_CH2_PIN);#endif#ifPWM2_ENHAL_TIM_PWM_Stop(&tim1_handle_t,TIM_CHANNEL_3);bsp_gpio_deinit(TIM1_CH3_GPIO_PORT,TIM1_CH3_PIN);#endif#ifPWM3_ENHAL_TIM_PWM_Stop(&tim1_handle_t,TIM_CHANNEL_4);bsp_gpio_deinit(TIM1_CH4_GPIO_PORT,TIM1_CH4_PIN);#endifHAL_TIM_PWM_DeInit(&tim1_handle_t);__HAL_RCC_TIM1_CLK_DISABLE();#endif}

5. 代码说明

5.1 初始化流程

5.2 占空比动态调节

在实际应用中,经常需要在运行过程中实时改变 PWM 的占空比,例如调节 LED 亮度、控制电机转速等。基于 HAL 库,只需修改tim1_oc_init_handle_t.Pulse(即 CCR 值)并重新调用HAL_TIM_PWM_ConfigChannel即可生效。下面以通道 1 为例,给出一个动态调节占空比的示例:

/** * @brief 动态设置 TIM1 通道 1 的 PWM 占空比 * @param duty_cycle: 目标占空比,取值范围 0.0 ~ 1.0(例如 0.5 表示 50%) * @retval 无 */voidbsp_tim_set_duty_cycle(floatduty_cycle){uint32_tpulse_value;/* 1. 将占空比换算为 CCR 值:Pulse = ARR * duty_cycle */pulse_value=(uint32_t)((float)TIM1_PERIOD*duty_cycle);/* 2. 限制 Pulse 取值范围,避免超出 [0, TIM1_PERIOD] 导致输出异常 */if(pulse_value>TIM1_PERIOD){pulse_value=TIM1_PERIOD;// 占空比最大为 100%}/* 3. 更新输出比较句柄中的 Pulse 值 */tim1_oc_init_handle_t.Pulse=pulse_value;/* 4. 重新配置通道 1,使新的占空比立即生效 */HAL_TIM_PWM_ConfigChannel(&tim1_handle_t,&tim1_oc_init_handle_t,TIM_CHANNEL_1);}

调用示例:

bsp_tim_set_duty_cycle(0.25f);// 占空比设为 25%bsp_tim_set_duty_cycle(0.75f);// 占空比设为 75%

说明

  • 由于代码中AutoReloadPreload配置为TIM_AUTORELOAD_PRELOAD_DISABLE,修改Pulse后调用HAL_TIM_PWM_ConfigChannel会立即更新 CCR 寄存器,无需等待更新事件。
  • 若需要同时调节多个通道,可分别修改对应通道的Pulse并调用HAL_TIM_PWM_ConfigChannel,注意传入正确的TIM_CHANNEL_x参数。
  • 若占空比需要频繁变化(如软件 PWM 调光),建议将该函数放在定时器中断或主循环中周期性调用,并注意控制调用频率以避免不必要的开销。

bsp_tim_pwm_init函数的执行流程如下:

  1. 清零定时器句柄与输出比较句柄;
  2. 使能 TIM1 时钟;
  3. 配置周期、预分频、计数模式等参数并调用HAL_TIM_PWM_Init
  4. 根据宏开关PWM0_EN~PWM3_EN依次初始化对应通道的 GPIO 复用功能、配置 PWM 模式并启动输出。

5.2 反初始化流程

bsp_tim_pwm_deinit依次停止各通道 PWM 输出、释放 GPIO、反初始化定时器并关闭时钟,用于低功耗或资源释放场景。

6. 常见问题与排查

在实际使用 TIM1 输出 PWM 时,可能会遇到波形异常、频率偏差、占空比不可调或多通道互相干扰等问题。下面针对这些常见问题给出原因分析与排查步骤。

6.1 无波形输出

现象:调用bsp_tim_pwm_init后,对应引脚测量不到 PWM 波形。

可能原因与排查步骤

  1. 宏开关未使能:检查BS_TIM1_EN以及对应通道的PWM0_EN~PWM3_EN是否定义为 1。若为 0,相关代码会被预编译屏蔽,不会输出波形。

    #defineBS_TIM1_EN1#definePWM0_EN1// 使能通道 1#definePWM1_EN0#definePWM2_EN0#definePWM3_EN0
  2. GPIO 复用功能配置错误:确认引脚已通过bsp_gpio_init_tim配置为 TIM1 的复用功能(如GPIO_AF1_TIM1_CH1),且引脚号与通道对应正确。若引脚被配置为普通 GPIO 输出,则不会输出 PWM。

  3. 时钟未使能:确认__HAL_RCC_TIM1_CLK_ENABLE()已执行。若 TIM1 时钟未打开,寄存器配置无效。

  4. 通道未启动:确认已调用HAL_TIM_PWM_Start启动对应通道。只配置通道而不启动,不会有输出。

  5. 引脚被其他外设占用:检查该引脚是否同时被 UART、I2C 等其他外设复用,导致信号被抢占。

6.2 频率不对

现象:输出的 PWM 频率与预期值不符。

可能原因与排查步骤

  1. 预分频与重装载值配置错误:PWM 频率由BS_TIM1_PRESCALERBS_TIM1_PERIOD共同决定。计算公式为:
    PWM 频率 = TIM1 时钟频率 / ((BS_TIM1_PRESCALER + 1) * (BS_TIM1_PERIOD + 1))
    检查这两个宏的取值是否符合预期。例如,若 TIM1 时钟为 48 MHz,期望输出 1 kHz,则(PSC + 1) * (ARR + 1) = 48000

计算示例:假设 TIM1 时钟频率为 48 MHz,期望输出 1 kHz 的 PWM 波形,则:

PWM 频率 = 48 MHz / ((BS_TIM1_PRESCALER + 1) * (BS_TIM1_PERIOD + 1)) = 1 kHz

(BS_TIM1_PRESCALER + 1) * (BS_TIM1_PERIOD + 1) = 48000。可取BS_TIM1_PRESCALER = 47BS_TIM1_PERIOD = 999,此时(47 + 1) * (999 + 1) = 48 * 1000 = 48000,输出频率正好为 1 kHz。对应宏定义如下:

#defineBS_TIM1_PRESCALER47// 预分频值 PSC,实际分频系数为 48#defineBS_TIM1_PERIOD999// 重装载值 ARR,实际计数周期为 1000

此时若设置Pulse = 500,则占空比为500 / 1000 = 50%

  1. 时钟源选择错误:确认 TIM1 的时钟源(内部时钟或外部时钟)配置正确。若误选了外部时钟且外部信号异常,频率会偏离预期。

  2. 重复计数器影响RepetitionCounter会影响更新事件的频率,但不会改变 PWM 输出频率本身。若配置了非零值,需确认其用途是否符合设计。

  3. 修改后未重新初始化:修改BS_TIM1_PRESCALERBS_TIM1_PERIOD后,需要重新调用bsp_tim_pwm_init使配置生效。

6.3 占空比不可调

现象:PWM 波形存在,但改变Pulse(CCR)值后占空比不变。

可能原因与排查步骤

  1. Pulse 值未更新:确认修改的是tim1_oc_init_handle_t.Pulse后重新调用HAL_TIM_PWM_ConfigChannel。若只改了变量而未重新配置通道,占空比不会更新。

    tim1_oc_init_handle_t.Pulse=500;// 修改占空比HAL_TIM_PWM_ConfigChannel(&tim1_handle_t,&tim1_oc_init_handle_t,TIM_CHANNEL_1);
  2. Pulse 值超出范围Pulse的值应介于 0 与TIM1_PERIOD之间。若Pulse大于TIM1_PERIOD,输出将恒为高电平;若为 0,则恒为低电平。

  3. 自动重装载预装载未生效:代码中AutoReloadPreload配置为TIM_AUTORELOAD_PRELOAD_DISABLE,此时修改Pulse会立即生效。若改为ENABLE,则需等待更新事件后才会生效,需确认是否符合预期。

  4. 通道配置错误:确认修改的是目标通道的Pulse。例如,修改通道 1 的占空比时,应使用TIM_CHANNEL_1调用HAL_TIM_PWM_ConfigChannel

6.4 多通道互相干扰

现象:使能多个通道后,某个通道的波形异常或与其他通道相互影响。

可能原因与排查步骤

  1. GPIO 引脚冲突:确认 4 个通道使用的引脚互不冲突,且每个引脚都正确配置为对应的复用功能(GPIO_AF1_TIM1_CH1~GPIO_AF1_TIM1_CH4)。若两个通道复用了同一引脚,信号会互相干扰。

  2. 共用句柄被覆盖:代码中 4 个通道共用tim1_oc_init_handle_t。初始化通道 2 时,会覆盖通道 1 的OCModePulse配置。若通道 1 需要不同的占空比,必须在配置通道 2 之前先完成通道 1 的配置并启动,或者为每个通道单独保存配置参数。

  3. 死区时间配置冲突:TIM1 支持互补输出和死区时间。若使能了互补输出,需确认死区时间配置合理,避免互补通道间出现异常电平。

  4. 刹车信号误触发:若刹车输入信号被意外拉低,所有 PWM 输出会被强制置于复位状态。检查刹车引脚电平及HAL_TIM_PWM_Init中的刹车配置。

  5. 中断优先级冲突:若多个通道都开启了中断且优先级配置不当,可能导致中断丢失或响应异常。建议为 TIM1 中断设置合理的抢占优先级,并确保中断服务函数中及时清除标志位。

7. 总结

参考资料

  • CX32L003 系列数据手册(Datasheet):包含 TIM1 定时器寄存器描述、引脚复用功能表(GPIO_AF1_TIM1_CH1~GPIO_AF1_TIM1_CH4)以及电气特性参数。
  • CX32L003 HAL 库用户手册(HAL Driver User Manual):详细说明HAL_TIM_PWM_InitHAL_TIM_PWM_ConfigChannelHAL_TIM_PWM_Start等 API 的用法、参数含义与返回值。
  • CX32L003 参考手册(Reference Manual):介绍 TIM1 高级定时器的计数模式、预分频器、自动重装载寄存器、重复计数器、死区时间与刹车输入等内部机制。
  • CX32L003 应用笔记(Application Note):关于定时器 PWM 输出、互补输出与死区配置的典型应用示例与注意事项。

附录:宏定义配置示例

business_config.h中,TIM1 相关的宏定义可按如下方式配置。以下示例使能 TIM1 及通道 1、通道 2,输出 1 kHz、50% 占空比的 PWM 波形(假设 TIM1 时钟为 48 MHz):

/* TIM1 总开关 */#defineBS_TIM1_EN1/* TIM1 时钟分频与周期配置:48 MHz / ((47 + 1) * (999 + 1)) = 1 kHz */#defineBS_TIM1_PRESCALER47// 预分频值 PSC,实际分频系数为 48#defineBS_TIM1_PERIOD999// 重装载值 ARR,实际计数周期为 1000/* 通道使能开关:1 使能,0 关闭 */#defineBS_PWM0_EN1// 使能通道 1(CH1)#defineBS_PWM1_EN1// 使能通道 2(CH2)#defineBS_PWM2_EN0// 关闭通道 3(CH3)#defineBS_PWM3_EN0// 关闭通道 4(CH4)/* 通道 1 引脚配置:PA0 复用为 TIM1_CH1 */#defineBS_TIM1_CH1_PINGPIO_PIN_0#defineBS_TIM1_CH1_GPIO_PORTGPIOA#defineBS_TIM1_CH1_GPIO_CLK_ENABLE()__HAL_RCC_GPIOA_CLK_ENABLE()#defineBS_TIM1_CH1_GPIO_CLK_DISABLE()__HAL_RCC_GPIOA_CLK_DISABLE()/* 通道 2 引脚配置:PA1 复用为 TIM1_CH2 */#defineBS_TIM1_CH2_PINGPIO_PIN_1#defineBS_TIM1_CH2_GPIO_PORTGPIOA#defineBS_TIM1_CH2_GPIO_CLK_ENABLE()__HAL_RCC_GPIOA_CLK_ENABLE()#defineBS_TIM1_CH2_GPIO_CLK_DISABLE()__HAL_RCC_GPIOA_CLK_DISABLE()/* 通道 3 引脚配置(未使能,可留空或按需填写) */#defineBS_TIM1_CH3_PINGPIO_PIN_2#defineBS_TIM1_CH3_GPIO_PORTGPIOA#defineBS_TIM1_CH3_GPIO_CLK_ENABLE()__HAL_RCC_GPIOA_CLK_ENABLE()#defineBS_TIM1_CH3_GPIO_CLK_DISABLE()__HAL_RCC_GPIOA_CLK_DISABLE()/* 通道 4 引脚配置(未使能,可留空或按需填写) */#defineBS_TIM1_CH4_PINGPIO_PIN_3#defineBS_TIM1_CH4_GPIO_PORTGPIOA#defineBS_TIM1_CH4_GPIO_CLK_ENABLE()__HAL_RCC_GPIOA_CLK_ENABLE()#defineBS_TIM1_CH4_GPIO_CLK_DISABLE()__HAL_RCC_GPIOA_CLK_DISABLE()

配置说明

  • BS_TIM1_EN为总开关,置 1 时bsp_tim.c中的 TIM1 相关代码才会被编译。
  • BS_TIM1_PRESCALERBS_TIM1_PERIOD共同决定 PWM 频率,计算公式为PWM 频率 = TIM1 时钟 / ((PSC + 1) * (ARR + 1))
  • BS_PWM0_EN~BS_PWM3_EN分别控制 4 个通道的使能状态,置 1 时对应通道会初始化 GPIO 复用功能并启动 PWM 输出。
  • 各通道的引脚宏需根据实际硬件连接填写,并确保引脚复用功能与通道号对应(如GPIO_AF1_TIM1_CH1对应通道 1)。
  • 若某通道未使用,可将对应使能宏置 0,引脚相关宏可保留默认值或留空。

本文介绍了 CX32L003 平台上基于 HAL 库的 TIM1 高级定时器 PWM 输出实现,通过宏配置实现 4 路通道的灵活开关,代码结构清晰、易于移植。开发者可根据实际需求调整BS_TIM1_PERIODBS_TIM1_PRESCALER来改变 PWM 频率与占空比。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询