【STM32 实战】定时器深度解析:从精准延时到呼吸灯的全流程实现

一、定时器:STM32 的 “时间管家”

在嵌入式开发中,“时间控制” 是核心需求之一。比如让 LED 以精确的频率闪烁、控制电机转速渐变、测量传感器信号周期等。STM32 的 ** 定时器(Timer)** 就是解决这些问题的关键组件,它就像一个精准的 “电子钟表”,能实现计时、计数、波形生成等功能。本文将以 STM32F103C8T6 为例,带你掌握定时器的核心应用。

二、定时器分类与核心概念

1. 定时器类型(以 STM32F103 为例)

类型型号总线核心功能典型场景
高级定时器TIM1/TIM8APB2支持 PWM 互补输出、死区控制(电机驱动)、刹车输入无刷电机控制
通用定时器TIM2-TIM5APB1输入捕获(测频率)、输出比较(PWM)、编码器接口(测速)呼吸灯、电机调速
基本定时器TIM6/TIM7APB1简单定时、触发 DAC 转换周期性数据采样

2. 核心工作原理

  • 时钟源:定时器的 “心跳” 来自时钟,可选内部时钟(如 72MHz)、外部时钟(如传感器脉冲)或其他定时器输出。
  • 预分频器(PSC):对时钟分频,降低计数频率。例如 72MHz 时钟经 PSC=71 分频后,计数频率变为 1MHz(公式:f = 时钟频率 / (PSC+1))。
  • 计数器(CNT):从 0 开始向上 / 向下计数,达到自动重装值(ARR)时触发中断或事件。
  • 自动重装寄存器(ARR):决定计数周期,例如 ARR=999 时,计数到 999 后归零,周期为(999+1)/f秒。

三、实战一:基于定时器的精准延时(替代 Delay 函数)

需求分析

  • 问题HAL_Delay()是阻塞式延时,期间 CPU 无法处理其他任务。
  • 目标:用定时器中断实现非阻塞式延时,让 CPU 在延时期间执行其他操作(如按键扫描)。

硬件与工具

  • 开发板:STM32F103C8T6(TIM2 定时器对应 PA0 引脚)。
  • 软件:Keil MDK + STM32CubeMX。

实现步骤(以 TIM2 为例)

1. 在 CubeMX 中配置定时器

① 新建工程:沿用之前的 LED 工程,或新建 STM32F103C8 工程。
② 配置 TIM2

  • 路径:Timers → TIM2
  • 模式:向上计数(Up Counter)
  • 时钟源:内部时钟(Internal Clock)
  • 预分频器(PSC):71(72MHz ÷ (71+1) = 1MHz,计数周期 1μs)
  • 自动重装值(ARR):999(实现 1ms 定时,1μs × 1000 = 1ms
  • 使能中断:勾选 “NVIC Settings” 中的 “Enable”)。

③ 生成代码:确保勾选 “Generate interrupts”,点击生成工程。

2. 编写定时器中断代码

① 在 main.c 中定义全局变量

uint32_t tick = 0; // 定时器计数器,记录经过的毫秒数

② 编写中断回调函数
stm32f10x_it.c中找到TIM2_IRQHandler函数,修改为:

void TIM2_IRQHandler(void) {
  if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { // 检查更新中断
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);       // 清除中断标志
    tick++;                                         // 每1ms自增1
  }
}

③ 在 main 函数中初始化定时器并启动中断

int main(void) {
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM2_Init();           // 初始化TIM2
  HAL_TIM_Base_Start_IT(&htim2); // 启动定时器中断

  while (1) {
    if (tick >= 500) {       // 每500ms翻转LED状态
      HAL_GPIO_TogglePin(GPIOC, GPIO_Pin_13);
      tick = 0;              // 重置计数器
    }
    // 其他任务(如按键检测)
  }
}

关键优势

  • 非阻塞性:CPU 在等待延时期间可执行其他代码(如按键扫描串口通信)。
  • 高精度:定时器依赖系统时钟,误差远小于HAL_Delay()(受 CPU 负载影响小)。

四、实战二:呼吸灯效果(PWM 控制 LED 亮度)

需求分析

  • 原理:通过 PWM(脉冲宽度调制)调节 LED 的亮灭占空比,人眼感知为亮度渐变。
  • 目标:用 TIM3 定时器生成 PWM 波形,控制 LED 从暗到亮再变暗,循环往复。

硬件连接

  • LED 连接 PC13(GPIO 输出),定时器 TIM3 的 PWM 通道对应引脚需配置为复用功能(如 PA6 对应 TIM3_CH1)。

实现步骤(以 TIM3 为例)

1. 在 CubeMX 中配置 PWM 输出

① 配置 TIM3

  • 模式:向上计数,自动重装载预装载使能(ARR 预装载)。
  • 时钟源:内部时钟(72MHz)。
  • PSC:71(分频后 1MHz)。
  • ARR:999(PWM 周期 1ms,(999+1)/1MHz = 1ms)。
  • 通道 1(CH1)配置:PWM 模式 1,占空比初始值 0(CCR=0)。
  • 引脚映射:PA6→TIM3_CH1(需在 GPIO 配置中设为 “复用推挽输出”)。

② 生成代码:确保勾选 “Generate peripheral initialization”。

编写 PWM 控制代码

① 在 main.c 中定义占空比变量

uint16_t dutyCycle = 0; // 占空比(0~999)
uint8_t direction = 1; // 1=递增,0=递减

② 在 main 函数中启动 PWM 输出

int main(void) {
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM3_Init();
  HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // 启动TIM3通道1的PWM输出

  while (1) {
    if (direction) {
      dutyCycle++; // 占空比递增
      if (dutyCycle >= 999) direction = 0;
    } else {
      dutyCycle--; // 占空比递减
      if (dutyCycle == 0) direction = 1;
    }
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, dutyCycle); // 更新占空比
    HAL_Delay(5); // 调节渐变速度
  }
}

代码解析

  • PWM 占空比TIM3_CH1的输出电平由比较寄存器(CCR)值决定。
    • CCR=0时,低电平占比 0%(LED 最暗);
    • CCR=999时,低电平占比 100%(LED 最亮)。
  • 渐变逻辑:通过direction变量控制占空比增减,实现周期性亮度变化。

五、定时器高级应用:输入捕获测频率

需求分析

  • 目标:用 TIM2 的输入捕获功能测量外部信号频率(如红外传感器脉冲)。
  • 原理:通过捕获信号的上升沿或下降沿,计算脉冲周期。

实现步骤

1. CubeMX 配置(TIM2 输入捕获模式)
  • 通道:TIM2_CH1(PA0 引脚)。
  • 模式:输入捕获,上升沿触发。
  • 预分频器:0(不分频,直接计数)。
uint32_t captureValue1 = 0, captureValue2 = 0; // 两次捕获值
uint32_t frequency = 0;

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
  if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
    if (captureValue1 == 0) {
      captureValue1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 第一次捕获
    } else {
      captureValue2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 第二次捕获
      uint32_t period = captureValue2 - captureValue1;
      frequency = 72000000 / period; // 计算频率(72MHz时钟)
      captureValue1 = captureValue2 = 0; // 重置捕获值
    }
  }
}

六、常见问题与优化技巧

1. PWM 输出无波形

  • 原因
    • 引脚未配置为复用功能(需设为 “复用推挽输出”)。
    • 定时器时钟未使能(检查 RCC 配置)。
  • 解决:在 CubeMX 中重新配置引脚,并生成代码时勾选相关时钟。

2. 定时器中断不触发

  • 原因
    • 中断使能未勾选(CubeMX 的 NVIC 设置)。
    • 中断优先级设置错误(优先级分组需匹配)。
  • 解决:检查 CubeMX 中的中断配置,确保 “Enable” 勾选,并在代码中正确编写中断回调函数。

3. 优化建议

  • 使用 HAL 库回调函数:避免直接操作寄存器,用HAL_TIM_PWM_Start()等接口简化开发。
  • 时钟树优化:根据需求调整 PSC 和 ARR,平衡精度与定时器溢出周期(如高精度延时需增大 ARR)。

七、总结:定时器的无限可能

通过本文实战,你已掌握 STM32 定时器的核心应用:

  • 精准延时:替代阻塞式 Delay,释放 CPU 资源。
  • PWM 控制:实现呼吸灯、电机调速等模拟量控制。
  • 输入捕获:扩展至频率测量、编码器测速等场景。

下一篇预告:《STM32 ADC 实战:从模拟信号到数字世界的桥梁》
(关注我,解锁传感器数据采集技能~)

互动话题:你想用定时器实现什么创意项目?欢迎在评论区分享你的想法! 🚀

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值