STM32通用定时器输出比较(OC)与PWM模式详解

STM32通用定时器的输出比较(OC)和PWM模式


通用定时器OC & PWM模式详解


1️⃣什么是“输出比较”模式(OC模式)🕰️

想象你有个计时器小朋友,它不停地数数,1、2、3、4、5……
现在你告诉它:“当数到10的时候,给我敲一下桌子!”📢
计时器就会监视自己数的数,一旦到10,就立马敲桌子!这就是“输出比较”模式。

  • 计数器 CNT:一直计数的数字

  • 比较寄存器 CCR:目标数字,敲桌子的时间点

  • 只要 CNT == CCR,就触发“敲桌子”事件,比如切换IO电平,或者发中断。


    2️⃣ OC模式有什么作用?🔧

  • 你可以用它做定时开关,比如定时开灯、关灯

  • 还能产生特定波形,比如方波(高低电平交替)

  • 还能产生中断做别的事情

  • 举个例子:当CNT==CCR时,GPIO输出口电平翻转,实现方波!


    3️⃣ 什么是PWM?⚡(PWM其实就是OC的升级版)

    PWM,全称“脉宽调制”,就是让你的小计时器产生一个“高电平持续时间可调”的波形,为什么?

  • 定时器数到ARR(自动重装载寄存器)周期结束,重新开始

  • 你设置CCR=某个值,代表高电平持续时间

  • 计数器数到CCR时,从高电平切换到低电平

  • 举个简单的图:

    |<----- 高电平 Pulse -----|<----- 低电平 ------>|
    |<----------- Period ----------->|
    

    占空比 = 高电平时间 / 总周期 = CCR / ARR

    你调CCR,就调占空比,灯就能亮得忽明忽暗,这就是呼吸灯的原理!🌬️💡


    4️⃣ STM32的PWM模式有哪些?🤔

  • PWM1模式(TIM_OCMODE_PWM1)
    CNT < CCR 输出高电平,CNT >= CCR 输出低电平

  • PWM2模式(TIM_OCMODE_PWM2)
    CNT < CCR 输出低电平,CNT >= CCR 输出高电平

  • 一般用PWM1就行。


    5️⃣ 实际配置步骤🏗️

    1. 设置时钟预分频PSC

    2. 比如72MHz主频,预分频72,得到1MHz计数频率(1us计数一次)

    3. 设置计数周期ARR

    4. 比如设置999,计数从0计数到999,周期1000us=1ms → 频率1kHz

    5. 设置比较寄存器CCR

    6. 例如500,占空比50%

    7. 配置GPIO为复用推挽输出(AF_PP)

    8. 使能TIM通道对应的GPIO输出PWM波形

    9. 启动PWM输出


    6️⃣ 小结表格✨

    参数 作用 举例
    PSC 分频主时钟,调节计数频率 71预分频 → 1MHz计数频率
    ARR 自动重装载,定时器计数周期(周期) 999 → 1ms周期
    CCR 比较寄存器,决定高电平时间(占空比) 500 → 50%占空比
    OCMode 选择PWM模式:PWM1或PWM2 一般用PWM1
    GPIO配置 设为复用推挽输出,保证波形输出 PB5作为TIM3_CH2输出

    7️⃣ 代码演示,帮你连起来🚀

    STM32CubeMX + HAL库 来实现 通用定时器的PWM输出,并且需要配合 Cube自动生成代码 的讲解。👌


    用 STM32CubeMX + HAL 实现 TIM3 CH2 PWM 呼吸灯(基于Cube自动生成代码)


    1. CubeMX配置步骤(图形界面操作)

  • 打开CubeMX,选择你的芯片 STM32F103ZETx

  • Clock Configuration:确认时钟配置正常,比如 HSE 8MHz,PLL乘法后 72MHz

  • Pinout:找到 TIM3_CH2 的引脚,比如 PB5,设置为 TIM3_CH2(复用功能)

  • Peripherals > TIM3

  • Mode:PWM Generation CH2

  • Prescaler:设置为 71 (分频72,得到1MHz计数时钟)

  • Counter Period (ARR):设置为 999 (周期1ms,即1kHz PWM频率)

  • NVIC:启用定时器中断(可选,呼吸灯不一定用中断)

  • Project Manager:选择工具链 Keil MDK-ARM,生成代码


  • 2. Cube生成的代码结构解析

  • Cube会自动生成 tim.ctim.h

  • MX_TIM3_Init() 函数中完成了定时器的时钟配置、预分频、周期、PWM通道配置和GPIO初始化

  • Cube自动帮你写了 HAL_TIM_PWM_MspInit()HAL_TIM_MspPostInit(),配置GPIO口为复用推挽输出(AF_PP)

  • 调用 HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2); 启动PWM


  • 3. 用户代码部分(main.c)

    /* USER CODE BEGIN 0 */
    uint16_t pwm_val = 0;
    uint8_t dir = 1;
    /* USER CODE END 0 */
    
    int main(void)
    {
      HAL_Init();
      SystemClock_Config();
    
      MX_GPIO_Init();
      MX_TIM3_Init();
    
      HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2); // 启动PWM输出
    
      while (1)
      {
        if(dir) pwm_val++;
        else pwm_val--;
    
        if(pwm_val >= 999) dir = 0;
        if(pwm_val == 0) dir = 1;
    
        __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, pwm_val); // 动态调节占空比
    
        HAL_Delay(5); // 延时实现呼吸节奏
      }
    }
    

    4. Cube生成的 MX_TIM3_Init() 关键代码示例

    void MX_TIM3_Init(void)
    {
      TIM_OC_InitTypeDef sConfigOC = {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_PWM_Init(&htim3) != HAL_OK)
      {
        Error_Handler();
      }
      
      sConfigOC.OCMode = TIM_OCMODE_PWM1;
      sConfigOC.Pulse = 0;  // 初始占空比0%
      sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
      sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
      
      if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
      {
        Error_Handler();
      }
    
      HAL_TIM_MspPostInit(&htim3);
    }
    

    5. Cube生成的 HAL_TIM_MspPostInit() 配置GPIO

    void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
    {
      GPIO_InitTypeDef GPIO_InitStruct = {0};
      if(timHandle->Instance==TIM3)
      {
        __HAL_RCC_GPIOB_CLK_ENABLE();
        GPIO_InitStruct.Pin = GPIO_PIN_5;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;  // 复用推挽输出
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
        __HAL_AFIO_REMAP_TIM3_PARTIAL();  // TIM3部分重映射
      }
    }
    

    6. 总结

    关键点 说明
    CubeMX配置TIM3 PWM 自动生成预分频、计数周期、通道配置
    HAL_TIM_PWM_Init 初始化定时器PWM功能
    HAL_TIM_MspPostInit 配置对应GPIO口,设置复用推挽输出
    HAL_TIM_PWM_Start 启动PWM输出
    __HAL_TIM_SET_COMPARE 动态改变CCR寄存器,调节占空比(调光、呼吸灯等)

    你要注意的

  • Cube自动帮你写了GPIO初始化,千万别重复写,否则冲突

  • HAL_TIM_MspPostInit() 是自动调用的,配置GPIO很重要

  • 启动PWM后动态写CCR才能看到呼吸灯渐变效果

  • 9️⃣ 你应该知道的几个“坑”⚠️

  • GPIO必须是复用推挽输出(AF_PP),否则PWM波形不会正常输出。

  • TIM时钟必须使能,定时器才会计数。

  • HAL_TIM_MspPostInit()负责配置GPIO,是CubeMX自动生成的。

  • 如果你不启动 HAL_TIM_PWM_Start(),PWM不会工作。

  • PSC和ARR设置必须配合你的时钟频率,频率才对。

  • 输出比较是“计数器到达某值触发事件”,PWM是它的进阶应用

  • PWM占空比由CCR和ARR决定,调CCR能动态改变占空比

  • 配置定时器必须设置PSC、ARR、CCR,GPIO配置为AF_PP

  • STM32有PWM1和PWM2两种模式,常用PWM1

  • 动态调节CCR寄存器的值实现呼吸灯或调光效果


  • 作者:不如出家吧

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32通用定时器输出比较(OC)与PWM模式详解

    发表回复