3. PWM初始化代码实战:MX配置、生成代码、HAL_TIM_PWM_Init与HAL_TIM_PWM_ConfigChannel

好,咱们直接进入正题。上一章我们把PWM的理论基础捋了一遍,这一章就动手实操——用STM32CubeMX配置工程,生成代码,然后看HAL库到底帮我们干了什么。

我个人习惯,拿到一个新项目,第一步永远是打开CubeMX。别急着写代码,先把外设配明白。你想想看,如果时钟树都配错了,后面调PWM频率就是白费功夫。

3.1 用CubeMX配置PWM通道

我以STM32F103C8T6为例,选一个定时器通道输出PWM。假设我们要用TIM2的通道1,对应PA0引脚。

核心配置步骤:

  1. 选定时器时钟源:Internal Clock(内部时钟),别选外部触发,除非你有特殊需求。
  2. 配通道模式:Channel1选"PWM Generation CH1",就是PWM输出模式。
  3. 设预分频器与自动重装值:这两个参数决定PWM频率。
  4. 设脉冲宽度:就是占空比,单位是定时器计数周期。

频率怎么算?公式很简单:

PWM频率 = 定时器时钟 / (预分频器 + 1) / (自动重装值 + 1)

举个例子,TIM2挂载在APB1上,时钟36MHz。我想要20kHz的PWM,算一下:

36MHz / (36-1) / (50-1) ≈ 20.4kHz

预分频器设35,自动重装值设49,脉冲宽度设25,占空比就是50%。

我的小技巧:在CubeMX的"Parameter Settings"页面,你改完预分频器和重装值,下面会实时显示更新频率。我一般先设目标频率,再微调分频系数,让实际值尽量接近目标值。

3.2 生成代码与HAL库初始化流程

配置完点"Generate Code",STM32CubeMX会帮我们生成初始化代码。打开工程,你会看到main.c里多了几行关键调用。

HAL库的PWM初始化分两步走:

  • HAL_TIM_PWM_Init():底层定时器初始化,配置时基单元。
  • HAL_TIM_PWM_ConfigChannel():配置PWM通道参数,比如极性、对齐方式。

我截取一段典型的生成代码,你看看:

// 定时器句柄定义
TIM_HandleTypeDef htim2;

// 时基初始化结构体
TIM_OC_InitTypeDef sConfigOC = {0};

// HAL_TIM_PWM_Init 内部调用
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* htim)
{
    if(htim->Instance == TIM2)
    {
        __HAL_RCC_TIM2_CLK_ENABLE();   // 使能定时器时钟
        HAL_GPIO_Init(GPIOA, GPIO_PIN_0, &GPIO_InitStruct); // 配置PA0为复用推挽输出
    }
}

// 主函数中的初始化序列
HAL_TIM_PWM_Init(&htim2);  // 第一步:初始化定时器时基

sConfigOC.OCMode = TIM_OCMODE_PWM1;       // PWM模式1
sConfigOC.Pulse = 25;                      // 占空比对应值
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; // 高电平有效
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; // 快速模式关闭
HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1); // 第二步:配置通道

// 启动PWM输出
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);

注意:HAL_TIM_PWM_Init()会调用MspInit回调函数,这里面完成GPIO和时钟的底层配置。如果你手动改了MspInit里的内容,重新生成代码时会被覆盖。我建议把用户代码写在/* USER CODE BEGIN */和/* USER CODE END */之间。

3.3 两个核心函数的内部机制

咱们深入看看这两个函数到底干了什么。

函数 主要工作 关键寄存器
HAL_TIM_PWM_Init 配置时基:预分频器、自动重装值、计数模式 TIMx_PSC, TIMx_ARR, TIMx_CR1
HAL_TIM_PWM_ConfigChannel 配置通道:输出比较模式、极性、脉冲值 TIMx_CCMR1, TIMx_CCER, TIMx_CCR1

HAL_TIM_PWM_Init内部会做这几件事:

  • 调用MspInit使能时钟、配GPIO
  • 写PSC和ARR寄存器
  • 设置计数方向(向上/向下/中心对齐)
  • 配置时钟分频因子(DMA相关)

HAL_TIM_PWM_ConfigChannel内部:

  • 写CCMRx寄存器,设置OCxM位(PWM1或PWM2)
  • 写CCER寄存器,设置极性、使能输出
  • 写CCRx寄存器,设置脉冲宽度

我曾经在项目里遇到一个问题:PWM输出波形完全不对,频率和占空比都乱了。查了半天,发现是HAL_TIM_PWM_Init和HAL_TIM_PWM_ConfigChannel的调用顺序反了。HAL库要求先Init再ConfigChannel,顺序错了寄存器配置会互相覆盖。

避坑指南:如果你在运行时动态修改占空比,直接写CCR寄存器就行,不用重新调用ConfigChannel。比如:__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, newPulse); 这样效率高得多。

3.4 知识结构图:PWM初始化流程

下面这张图把整个初始化流程串起来了,从CubeMX配置到最终输出波形,每一步都清晰可见。

PWM初始化完整流程 ① CubeMX配置参数 ② 生成HAL初始化代码 ③ HAL_TIM_PWM_Init MspInit:时钟+GPIO 配置PSC/ARR/计数模式 HAL_TIM_PWM_ConfigChannel ④ HAL_TIM_PWM_Start ⑤ PA0输出PWM波形 ← 上一章 📖 返回目录 下一章 →