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

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

  2. 设置计数周期ARR

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

  3. 设置比较寄存器CCR

    • 例如500,占空比50%

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

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

  5. 启动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寄存器的值实现呼吸灯或调光效果


Logo

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

更多推荐