定时器、计数器和 PWM 到底是什么关系?从工作原理到 STM32 实战详解

前言

在学习 STM32、GD32、51 单片机或者其他 MCU 时,经常会遇到下面几个概念:

  • 定时器;
  • 计数器;
  • PWM;
  • 预分频器;
  • 自动重装载寄存器;
  • 比较寄存器;
  • 输入捕获;
  • 输出比较。

很多初学者会产生疑问:

定时器和计数器是不是两个不同的外设?
PWM 为什么要通过定时器产生?
修改 ARR 和 CCR 为什么可以改变 PWM 的频率和占空比?
定时器明明只是在计数,为什么还能测量脉宽、输出波形甚至读取编码器?

实际上,定时器、计数器和 PWM 并不是三个完全独立的功能。

它们之间的核心关系可以用一句话概括:

定时器的本质是一个按照时钟脉冲不断变化的计数器,而 PWM 是利用计数器数值与比较寄存器进行比较后产生的周期性高低电平信号。

本文将从底层原理开始,详细讲清楚三者之间的关系。


一、先理解什么是计数

计数就是对一个个脉冲进行累加或递减。

假设每来一个脉冲,计数器的值增加 1:

第1个脉冲到来:CNT = 1
第2个脉冲到来:CNT = 2
第3个脉冲到来:CNT = 3
……
第100个脉冲到来:CNT = 100

这里的 CNT 可以理解为计数器当前保存的数值。

如果脉冲来自单片机内部稳定的时钟,那么计数值就可以反映经过的时间。

如果脉冲来自单片机外部引脚,那么计数值就可以反映外部事件发生的次数。

因此,定时和计数的区别主要不在计数器本身,而在于:

用来驱动计数器的脉冲来自哪里。


二、什么是计数器

计数器英文通常称为:

Counter

计数器的作用是统计脉冲数量。

一个最简单的计数器内部可以理解为:

输入脉冲
   ↓
计数逻辑
   ↓
CNT计数寄存器

每输入一个有效脉冲,计数寄存器就增加或减少一次。

例如:

外部传感器每检测到一个产品,输出一个脉冲
                      ↓
单片机计数器累计脉冲数量
                      ↓
得到生产线产品数量

计数器常见应用包括:

  • 统计按键次数;
  • 统计传感器脉冲;
  • 统计产品数量;
  • 测量电机转速;
  • 读取旋转编码器;
  • 统计流量计脉冲;
  • 统计风扇 TACH 信号;
  • 测量外部信号频率。

例如,一个风扇每旋转一圈输出 2 个脉冲,在 1 秒内检测到 100 个脉冲,那么风扇转速为:

转速 = 100 ÷ 2 × 60
     = 3000 RPM

三、什么是定时器

定时器英文称为:

Timer

定时器的本质仍然是计数器。

区别在于,定时器通常由 MCU 内部时钟驱动。

例如,计数器每隔 1 微秒增加一次:

0 μs:CNT = 0
1 μs:CNT = 1
2 μs:CNT = 2
3 μs:CNT = 3
……
1000 μs:CNT = 1000

因为每次计数所代表的时间是确定的,所以只要知道计数次数,就能知道经过了多长时间。

因此:

时间 = 计数次数 × 每次计数所需时间

例如:

每次计数时间 = 1 μs
计数次数 = 1000

那么经过的时间就是:

1000 × 1 μs = 1 ms

这就是定时器能够“定时”的根本原因。


四、定时器和计数器的核心区别

定时器和计数器通常使用的是同一个硬件模块。

它们的主要区别是时钟来源不同。

对比项目 定时器 计数器
时钟来源 MCU 内部时钟 外部输入脉冲
统计对象 时间 外部事件数量
主要用途 延时、周期中断、PWM 脉冲计数、转速统计
本质 对内部时钟计数 对外部脉冲计数
核心寄存器 CNT CNT

可以简单记忆为:

内部时钟驱动 → 定时器
外部脉冲驱动 → 计数器

因此,很多 MCU 的用户手册会将该外设统一称为:

Timer/Counter

也就是“定时器/计数器”。


五、定时器内部有哪些核心模块

一个典型 MCU 定时器可以简化为:

             内部时钟或外部时钟
                       ↓
                 预分频器 PSC
                       ↓
                 计数时钟 CK_CNT
                       ↓
                  计数器 CNT
                       ↓
         ┌─────────────┴─────────────┐
         ↓                           ↓
自动重装载寄存器 ARR           比较寄存器 CCR
         ↓                           ↓
周期控制/溢出中断             输出比较/PWM

最重要的寄存器包括:

  • PSC:预分频寄存器;
  • CNT:计数器寄存器;
  • ARR:自动重装载寄存器;
  • CCR:捕获比较寄存器。

下面分别介绍。


六、PSC:预分频器有什么作用

单片机的系统时钟通常很高。

例如:

72 MHz
168 MHz
200 MHz

如果直接使用 72 MHz 时钟驱动计数器,那么计数器每秒需要计数 7200 万次。

每次计数时间为:

1 ÷ 72 MHz ≈ 13.89 ns

这样的计数速度太快,不方便产生毫秒级或者秒级定时。

因此需要使用预分频器对时钟进行分频。

假设定时器输入时钟为:

fTIM = 72 MHz

预分频寄存器设置为:

PSC = 71

那么实际计数频率为:

fCNT = fTIM ÷ (PSC + 1)

代入数据:

fCNT = 72 MHz ÷ 72
     = 1 MHz

此时计数器每计数一次所需时间为:

TCNT = 1 ÷ 1 MHz
     = 1 μs

需要特别注意:

预分频倍数通常是 PSC + 1,而不是 PSC。

因为当 PSC 等于 0 时,定时器仍然需要以原始时钟运行,而不是停止。


七、CNT:计数器寄存器

CNT 保存定时器当前的计数值。

在向上计数模式中:

0 → 1 → 2 → 3 → …… → ARR → 0

在向下计数模式中:

ARR → ARR-1 → ARR-2 → …… → 0 → ARR

在中心对齐模式中:

0 → 1 → 2 → …… → ARR → ARR-1 → …… → 0

软件可以读取 CNT,从而知道定时器当前已经运行到哪个位置。

例如:

uint32_t current_count;

current_count = __HAL_TIM_GET_COUNTER(&htim3);

CNT 可以用于:

  • 获取当前计数值;
  • 计算两个事件之间的时间;
  • 实现输入捕获;
  • 测量外部信号频率;
  • 统计外部脉冲;
  • 产生 PWM。

八、ARR:自动重装载寄存器

ARR 决定计数器计到多少后重新开始。

例如:

ARR = 999

在向上计数模式下,CNT 的变化为:

0 → 1 → 2 → …… → 998 → 999 → 0

一共经历:

1000次计数

也就是:

ARR + 1

如果每次计数需要 1 μs,那么一个完整周期为:

1000 × 1 μs = 1 ms

定时器频率为:

1 ÷ 1 ms = 1 kHz

因此,在普通向上计数模式下:

定时器更新频率 =
定时器输入时钟 ÷ [(PSC + 1) × (ARR + 1)]

即:

fUPDATE = fTIM / [(PSC + 1) × (ARR + 1)]

九、什么是定时器溢出

当 CNT 计数到 ARR 后重新回到 0,通常会产生一次更新事件。

例如:

CNT:998 → 999 → 0

这次从 ARR 回到 0 的过程通常称为:

  • 溢出;
  • 更新事件;
  • Update Event。

如果使能更新中断,MCU 就会进入定时器中断服务函数。

例如:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM3)
    {
        /* 每次定时器溢出执行一次 */
    }
}

因此,定时器可以周期性执行任务,例如:

  • 每 1 ms 更新系统时基;
  • 每 10 ms 扫描按键;
  • 每 100 ms 读取传感器;
  • 每 1 s 刷新设备状态;
  • 周期性喂看门狗;
  • 周期性执行控制算法。

十、什么是 PWM

PWM 的英文全称是:

Pulse Width Modulation

中文称为:

脉宽调制

PWM 是一种周期固定或可调、同时可以改变高电平持续时间的方波信号。

一个 PWM 信号通常包含两个重要参数:

  • 频率;
  • 占空比。

例如:

高电平:0.5 ms
低电平:0.5 ms
总周期:1 ms

PWM 频率为:

1 ÷ 1 ms = 1 kHz

占空比为:

0.5 ms ÷ 1 ms × 100% = 50%

PWM 波形可以表示为:

高电平  ┌─────┐       ┌─────┐
        │     │       │     │
低电平 ─┘     └───────┘     └───────
        ←──── 一个周期 ────→

十一、PWM 和定时器是什么关系

PWM 并不是一个完全独立的硬件模块。

大多数 MCU 都是利用定时器产生 PWM。

其核心过程是:

  1. 定时器 CNT 不断计数;
  2. CNT 与比较寄存器 CCR 比较;
  3. 根据比较结果控制引脚输出高电平或低电平;
  4. CNT 到达 ARR 后重新开始;
  5. 周期性重复以上过程,形成 PWM。

因此:

ARR决定PWM周期
CCR决定高电平宽度
PSC决定计数速度
CNT表示当前计数位置

这就是定时器产生 PWM 的根本原理。


十二、CCR:比较寄存器如何控制占空比

CCR 的英文含义是:

Capture/Compare Register

中文通常称为:

捕获比较寄存器

在 PWM 输出模式中,CCR 用作比较值。

假设:

ARR = 999
CCR = 500

计数器从 0 计数到 999。

在常见的 PWM 模式 1、向上计数、高电平有效配置下,可以理解为:

当 CNT < CCR 时:输出高电平
当 CNT ≥ CCR 时:输出低电平

对应过程如下:

CNT = 0~499:高电平
CNT = 500~999:低电平

高电平持续 500 个计数周期,总周期为 1000 个计数周期,因此占空比为:

500 ÷ 1000 × 100% = 50%

一般情况下,可以使用下面的近似公式计算:

占空比 = CCR ÷ (ARR + 1) × 100%

需要注意,0% 和 100% 占空比的边界行为可能受到:

  • PWM 模式;
  • 输出极性;
  • 芯片具体实现;
  • CCR 与 ARR 的关系;

等因素影响。

实际工程中需要结合芯片参考手册确认。


十三、PWM频率如何计算

在普通向上计数、边沿对齐模式下:

PWM频率 =
定时器输入时钟 ÷ [(PSC + 1) × (ARR + 1)]

即:

fPWM = fTIM / [(PSC + 1) × (ARR + 1)]

假设:

定时器输入时钟:72 MHz
PSC = 71
ARR = 999

计数频率为:

72 MHz ÷ 72 = 1 MHz

PWM 频率为:

1 MHz ÷ 1000 = 1 kHz

如果:

CCR = 250

那么占空比约为:

250 ÷ 1000 × 100% = 25%

最终得到:

频率:1 kHz
占空比:25%

十四、定时器、计数器和PWM的关系图

三者之间的关系可以概括为:

                定时器/计数器外设
                        │
                本质:CNT计数
                        │
         ┌──────────────┴──────────────┐
         │                             │
   内部时钟驱动                  外部脉冲驱动
         │                             │
       定时器                         计数器
         │                             │
  延时、周期中断                脉冲统计、转速测量
         │
   CNT与CCR进行比较
         │
        PWM

因此,可以用一句更加准确的话总结:

定时器和计数器是同一计数硬件在不同输入时钟下的两种用途,PWM 则是定时器计数和输出比较功能组合后的结果。


十五、用生活中的例子理解三者关系

可以把定时器想象成一个人在数节拍。

定时器

节拍器每隔固定时间响一次:

滴、滴、滴、滴……

人每听到一次声音,就加 1。

由于每次声音间隔固定,所以数到 100 就代表经过了固定时间。

这就是定时器。

计数器

如果不再听节拍器,而是每有一个人经过门口就加 1,那么统计的是人数。

这就是计数器。

PWM

假设每数 10 个数为一轮:

0~9

在 0~2 时打开灯,在 3~9 时关闭灯:

开、开、开、关、关、关、关、关、关、关

重复执行,就形成了 30% 占空比的 PWM。


十六、STM32定时器常见工作模式

STM32 定时器不仅可以实现普通定时和 PWM,还支持多种功能。

1. 基本定时

用于周期性产生中断。

PSC + ARR
   ↓
确定定时周期

应用:

  • 系统节拍;
  • 周期任务;
  • 传感器采集;
  • 软件定时。

2. 外部脉冲计数

通过外部引脚输入脉冲,驱动 CNT 计数。

应用:

  • 流量计;
  • 转速计;
  • 产品计数;
  • 频率统计。

3. 输出比较

当 CNT 等于 CCR 时,执行指定动作。

例如:

  • 翻转输出;
  • 拉高输出;
  • 拉低输出;
  • 产生中断。

4. PWM输出

通过 CNT 与 CCR 比较,产生周期性高低电平。

应用:

  • LED调光;
  • 风扇调速;
  • 电机控制;
  • 舵机控制;
  • 蜂鸣器;
  • DAC模拟输出。

5. 输入捕获

当外部引脚检测到边沿时,将当前 CNT 保存到 CCR。

例如:

第一次上升沿:CCR1 = 100
第二次上升沿:CCR1 = 1100

两次边沿之间相差:

1100 - 100 = 1000个计数

如果每次计数为 1 μs,那么信号周期为:

1000 μs = 1 ms

频率为:

1 kHz

输入捕获常用于:

  • 测量频率;
  • 测量周期;
  • 测量脉宽;
  • 测量 PWM 占空比;
  • 读取风扇 TACH 信号。

6. 编码器模式

定时器可以同时读取编码器的 A、B 两相信号。

根据相位关系判断:

  • 旋转方向;
  • 旋转速度;
  • 旋转位置。

应用:

  • 电机控制;
  • 旋钮;
  • 机器人关节;
  • 位置检测。

十七、STM32产生1 kHz、50%占空比PWM实例

假设条件如下:

定时器时钟:72 MHz
目标PWM频率:1 kHz
目标占空比:50%

首先将 72 MHz 分频到 1 MHz:

PSC = 72 - 1 = 71

此时:

计数频率 = 72 MHz ÷ 72
         = 1 MHz

每次计数时间为:

1 μs

要产生 1 kHz PWM,周期需要:

1 ÷ 1 kHz = 1 ms

1 ms 对应 1000 次计数,因此:

ARR = 1000 - 1 = 999

50% 占空比对应:

CCR = 500

最终参数:

PSC = 71
ARR = 999
CCR = 500

十八、STM32 HAL库配置示例

下面以 TIM3 通道 1 为例。

1. 初始化代码

#include "main.h"

TIM_HandleTypeDef htim3;

static void MX_TIM3_Init(void)
{
    TIM_ClockConfigTypeDef clock_config = {0};
    TIM_MasterConfigTypeDef master_config = {0};
    TIM_OC_InitTypeDef pwm_config = {0};

    htim3.Instance = TIM3;
    htim3.Init.Prescaler = 71;
    htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim3.Init.Period = 999;
    htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;

    if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
    {
        Error_Handler();
    }

    clock_config.ClockSource = TIM_CLOCKSOURCE_INTERNAL;

    if (HAL_TIM_ConfigClockSource(&htim3, &clock_config) != HAL_OK)
    {
        Error_Handler();
    }

    if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
    {
        Error_Handler();
    }

    master_config.MasterOutputTrigger = TIM_TRGO_RESET;
    master_config.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

    if (HAL_TIMEx_MasterConfigSynchronization(
            &htim3,
            &master_config) != HAL_OK)
    {
        Error_Handler();
    }

    pwm_config.OCMode = TIM_OCMODE_PWM1;
    pwm_config.Pulse = 500;
    pwm_config.OCPolarity = TIM_OCPOLARITY_HIGH;
    pwm_config.OCFastMode = TIM_OCFAST_DISABLE;

    if (HAL_TIM_PWM_ConfigChannel(
            &htim3,
            &pwm_config,
            TIM_CHANNEL_1) != HAL_OK)
    {
        Error_Handler();
    }

    HAL_TIM_MspPostInit(&htim3);
}

2. 启动PWM

int main(void)
{
    HAL_Init();
    SystemClock_Config();

    MX_GPIO_Init();
    MX_TIM3_Init();

    if (HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1) != HAL_OK)
    {
        Error_Handler();
    }

    while (1)
    {
    }
}

运行后,TIM3 通道 1 对应引脚会输出:

频率:1 kHz
占空比:50%

十九、运行过程中修改PWM占空比

可以直接修改 CCR。

例如设置为 25%:

__HAL_TIM_SET_COMPARE(
    &htim3,
    TIM_CHANNEL_1,
    250
);

设置为 75%:

__HAL_TIM_SET_COMPARE(
    &htim3,
    TIM_CHANNEL_1,
    750
);

可以封装成函数:

void PWM_SetDuty(
    TIM_HandleTypeDef *htim,
    uint32_t channel,
    float duty_percent)
{
    uint32_t period;
    uint32_t compare;

    if (duty_percent < 0.0f)
    {
        duty_percent = 0.0f;
    }

    if (duty_percent > 100.0f)
    {
        duty_percent = 100.0f;
    }

    period = __HAL_TIM_GET_AUTORELOAD(htim) + 1U;

    compare = (uint32_t)(
        period * duty_percent / 100.0f
    );

    __HAL_TIM_SET_COMPARE(
        htim,
        channel,
        compare
    );
}

调用:

PWM_SetDuty(
    &htim3,
    TIM_CHANNEL_1,
    30.0f
);

即可将占空比设置为约 30%。

需要注意,100% 占空比在不同 MCU、PWM 模式和输出配置下可能需要特殊处理,实际使用时应确认参考手册中的比较行为。


二十、运行过程中修改PWM频率

PWM 频率由 PSC 和 ARR 共同决定。

如果保持 PSC 不变,只修改 ARR:

void PWM_SetFrequency(
    TIM_HandleTypeDef *htim,
    uint32_t timer_count_frequency,
    uint32_t pwm_frequency)
{
    uint32_t arr;

    if ((pwm_frequency == 0U) ||
        (timer_count_frequency < pwm_frequency))
    {
        return;
    }

    arr = timer_count_frequency / pwm_frequency;

    if (arr == 0U)
    {
        return;
    }

    __HAL_TIM_SET_AUTORELOAD(
        htim,
        arr - 1U
    );

    __HAL_TIM_SET_COUNTER(
        htim,
        0U
    );
}

假设计数频率为:

1 MHz

设置 PWM 为 2 kHz:

PWM_SetFrequency(
    &htim3,
    1000000U,
    2000U
);

计算得到:

ARR = 1 000 000 ÷ 2000 - 1
    = 499

需要注意:

修改 ARR 后,如果希望占空比保持不变,也必须按照新的 ARR 重新计算 CCR。

否则原来的 CCR 不变,占空比就会发生变化。


二十一、为什么修改ARR会同时影响频率和占空比

假设原来:

ARR = 999
CCR = 500

占空比约为:

500 ÷ 1000 = 50%

现在将 ARR 改为:

ARR = 499

但 CCR 仍然是:

CCR = 500

此时 CCR 已经接近或超过计数周期范围,输出可能接近持续有效。

因此,修改频率时必须同步调整 CCR。

例如,希望 ARR 改成 499 后仍保持 50%:

CCR = (499 + 1) × 50%
    = 250

二十二、PWM模式1和PWM模式2有什么区别

在常见的向上计数模式下,可以简化理解为:

PWM模式1

CNT < CCR:有效电平
CNT ≥ CCR:无效电平

PWM模式2

CNT < CCR:无效电平
CNT ≥ CCR:有效电平

两者输出逻辑相反。

如果再结合输出极性:

高电平有效
低电平有效

最终引脚波形还会发生变化。

因此,出现下面问题时,需要检查:

  • PWM模式选错;
  • 输出极性选错;
  • CCR设置错误;
  • 引脚复用错误。

二十三、边沿对齐和中心对齐有什么区别

1. 边沿对齐

计数器只向上或只向下计数:

0 → 1 → 2 → …… → ARR → 0

PWM 的一个边沿固定在周期起点。

优点:

  • 配置简单;
  • 频率计算直观;
  • 适合普通 LED、蜂鸣器和风扇控制。

2. 中心对齐

计数器先向上,再向下:

0 → 1 → 2 → …… → ARR
ARR-1 → ARR-2 → …… → 0

PWM 脉冲相对于周期中心对称。

优点:

  • 谐波特性更好;
  • 适合电机控制;
  • 适合逆变器;
  • 减小部分电磁干扰。

中心对齐模式的周期计算与边沿对齐不同,不能直接套用普通向上计数公式,必须结合具体 MCU 的计数规则计算。


二十四、PWM有哪些实际用途

1. LED调光

通过改变 PWM 占空比,改变 LED 平均电流。

占空比越大 → LED越亮
占空比越小 → LED越暗

2. 直流电机调速

PWM 控制电机驱动器的导通时间。

占空比越大 → 平均电压越高 → 转速通常越高

需要注意,MCU 的 PWM 引脚不能直接驱动大功率电机,必须经过:

  • MOSFET;
  • 三极管;
  • 电机驱动芯片;
  • 隔离器件。

3. 风扇调速

四线风扇通常包含:

  • 电源;
  • 地;
  • TACH转速反馈;
  • PWM调速输入。

MCU 输出 PWM 控制风扇速度,同时通过计数器或输入捕获读取 TACH 信号。

这是定时器、计数器和 PWM 同时使用的典型案例:

PWM输出 → 控制风扇速度
计数器/输入捕获 → 读取风扇转速
定时器中断 → 周期计算RPM

4. 舵机控制

常见舵机使用固定周期的 PWM 信号,通过高电平脉宽控制角度。

例如:

周期约20 ms
高电平约1 ms → 某一端角度
高电平约1.5 ms → 中间位置
高电平约2 ms → 另一端角度

舵机更关心的是高电平脉宽,而不只是占空比。


5. 蜂鸣器发声

改变 PWM 频率可以改变音调:

频率越高 → 音调越高
频率越低 → 音调越低

改变占空比可以影响驱动效果和音量表现。


6. 模拟电压输出

PWM 经过低通滤波器后,可以得到近似模拟电压。

例如:

MCU PWM输出
     ↓
RC低通滤波
     ↓
近似直流电压

平均电压近似为:

VOUT ≈ VCC × 占空比

如果 MCU 电压为 3.3 V,占空比为 50%,滤波后平均电压约为:

3.3 V × 50% = 1.65 V

二十五、一个风扇控制实例中的三者关系

假设系统需要:

  • 输出 25 kHz PWM 控制风扇;
  • 读取风扇 TACH 脉冲;
  • 每 1 秒计算一次转速。

可以这样分配定时器:

定时器A:产生PWM

内部时钟
   ↓
PSC和ARR设置25 kHz周期
   ↓
CCR调节占空比
   ↓
输出PWM控制风扇

定时器B:统计TACH脉冲

风扇TACH信号
   ↓
外部计数器或输入捕获
   ↓
统计1秒内脉冲数

定时器C:产生1秒周期中断

内部时钟
   ↓
每1秒产生中断
   ↓
读取脉冲数量
   ↓
计算RPM

如果风扇每转一圈输出 2 个脉冲:

RPM = 1秒内脉冲数 × 60 ÷ 2

这就是一个完整的“定时器+计数器+PWM”工程应用。


二十六、常见问题一:PWM引脚没有波形

可以按照下面的顺序排查。

1. 定时器时钟是否使能

例如:

__HAL_RCC_TIM3_CLK_ENABLE();

2. GPIO端口时钟是否使能

例如:

__HAL_RCC_GPIOA_CLK_ENABLE();

3. GPIO是否配置为复用功能

PWM 引脚不能配置成普通输出模式,需要设置为定时器复用功能。

4. 复用编号是否正确

不同 STM32 型号中,同一个引脚可能对应不同 AF 编号。

5. PWM是否启动

仅完成初始化并不会自动产生输出,还需要:

HAL_TIM_PWM_Start(
    &htim3,
    TIM_CHANNEL_1
);

6. 通道是否匹配

检查:

  • 配置的是 CH1 还是 CH2;
  • 引脚对应哪个通道;
  • 启动函数使用哪个通道。

7. ARR和CCR是否合理

例如:

ARR = 999
CCR = 0

输出可能一直处于无效状态。


二十七、常见问题二:PWM频率计算不正确

最常见的原因是定时器实际输入时钟判断错误。

在部分 STM32 中:

  • APB 分频为 1 时,定时器时钟等于 APB 时钟;
  • APB 分频不为 1 时,定时器时钟可能等于 APB 时钟的 2 倍。

例如:

APB1时钟 = 36 MHz
APB1预分频不为1

TIM2、TIM3 等定时器的实际时钟可能是:

72 MHz

如果错误地按照 36 MHz 计算,最终 PWM 频率会相差 2 倍。

因此必须根据:

  • 时钟树;
  • RCC配置;
  • 芯片参考手册;

确认定时器真实时钟。


二十八、常见问题三:改变占空比后波形异常

可能原因包括:

  • CCR超过ARR;
  • ARR改变后没有更新CCR;
  • 预装载没有使能;
  • 更新事件未产生;
  • PWM模式错误;
  • 输出极性错误;
  • 比较值在错误通道中修改。

建议保证:

0 ≤ CCR ≤ ARR + 1

同时在修改 ARR 后重新计算 CCR。


二十九、常见问题四:低频PWM不稳定

低频 PWM 通常需要很大的计数周期。

如果 PSC 太小,ARR 可能超过定时器位宽。

例如 16 位定时器最大 ARR 通常为:

65535

如果计算结果大于 65535,就需要:

  • 增大 PSC;
  • 使用 32 位定时器;
  • 采用软件分频;
  • 使用重复计数器;
  • 重新选择时钟源。

三十、常见问题五:高频PWM分辨率太低

PWM 占空比分辨率主要由 ARR 决定。

例如:

ARR = 999

一个计数单位对应:

1 ÷ 1000 = 0.1%

如果:

ARR = 99

一个计数单位对应:

1 ÷ 100 = 1%

因此:

ARR越大 → 占空比分辨率越高

但是在定时器时钟固定时:

PWM频率越高 → ARR通常越小

这意味着 PWM 频率和占空比分辨率之间存在权衡。

例如:

  • 高频 PWM:开关速度快,但占空比分辨率可能降低;
  • 低频 PWM:ARR 可以更大,分辨率更高。

三十一、为什么定时器是MCU最重要的外设之一

定时器并不只是用来“延时”。

它实际上可以完成:

  • 周期中断;
  • 精确延时;
  • 外部脉冲计数;
  • PWM输出;
  • 输入捕获;
  • 输出比较;
  • 编码器读取;
  • 单脉冲输出;
  • 死区控制;
  • 互补PWM;
  • 电机换相;
  • 触发ADC;
  • 触发DMA;
  • 多定时器同步。

很多复杂功能的底层,本质上仍然是:

计数
比较
捕获
更新

理解了 CNT、PSC、ARR 和 CCR,就掌握了定时器最核心的逻辑。


三十二、面试中如何回答三者关系

如果面试官问:

定时器、计数器和 PWM 有什么关系?

可以这样回答:

定时器的本质是计数器。

当计数器由内部固定时钟驱动时,可以根据计数次数计算时间,
因此表现为定时器。

当计数器由外部脉冲驱动时,可以统计外部事件数量,
因此表现为计数器。

PWM则是在定时器计数过程中,将CNT与CCR比较,
并根据比较结果控制输出高低电平。

其中PSC控制计数速度,ARR控制PWM周期,
CCR控制有效电平持续时间,也就是占空比。

三十三、总结

定时器、计数器和 PWM 之间不是相互独立的关系。

它们的核心关系是:

计数器是基础
定时器是对内部时钟计数
外部计数是对外部脉冲计数
PWM是计数器与比较器共同产生的输出波形

四个核心寄存器的作用如下:

寄存器 作用
PSC 控制计数时钟分频
CNT 保存当前计数值
ARR 决定计数周期
CCR 决定比较位置和PWM占空比

在普通向上计数模式下:

计数频率 =
定时器时钟 ÷ (PSC + 1)
PWM频率 =
定时器时钟 ÷ [(PSC + 1) × (ARR + 1)]
PWM占空比 ≈
CCR ÷ (ARR + 1) × 100%

最后用一句话总结:

定时器通过内部时钟驱动计数器记录时间,计数器也可以统计外部脉冲,而 PWM 则通过比较 CNT 与 CCR,在 ARR 确定的周期内控制输出高低电平。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐