4. PWM常见故障1:无波形输出——时钟使能、GPIO复用功能、定时器未启动排查

各位做嵌入式开发的同行,PWM无波形输出这个故障,我敢说十个新手有八个都遇到过。我自己刚用STM32那会儿,也在这个坑里爬了好一阵子。今天咱们就把这个问题的三个核心排查点掰开揉碎了讲清楚。

4.1 故障现象与排查思路

你配置好了定时器,写好了PWM初始化代码,信心满满地接上示波器——结果呢?引脚上啥也没有,一条直线。这时候别慌,先理清思路。

无波形输出,说白了就三个层面的问题:

  • 时钟没给到——定时器根本没工作
  • 引脚没配置对——GPIO没切换到复用功能
  • 定时器没启动——寄存器配置了但没让定时器跑起来

我一般按这个顺序排查,效率最高。下面咱们一个一个来看。

核心排查顺序:时钟使能 → GPIO复用配置 → 定时器启动

这个顺序不是随便排的。时钟是基础,时钟没开后面全白搭;GPIO配置错了波形出不来;定时器没启动,前面配置得再好也没用。

PWM无波形输出排查流程图 PWM无波形输出 ① 检查定时器时钟是否使能 ② 检查GPIO复用功能配置 ③ 检查定时器是否已启动 输出正常波形 排查要点 按顺序排查,缺一不可

4.2 时钟使能——最容易忽略的第一步

很多人上来就配置定时器的ARR、CCR,结果忘了开时钟。你想想看,定时器本身也是外设,没时钟它怎么工作?

STM32的定时器挂在不同总线上:

定时器类型 挂载总线 时钟使能函数
TIM1、TIM8 APB2 RCC_APB2PeriphClockCmd()
TIM2~TIM7 APB1 RCC_APB1PeriphClockCmd()
TIM9~TIM14 APB2 RCC_APB2PeriphClockCmd()

我见过最典型的错误:用TIM3输出PWM,结果写的是RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM3, ENABLE)。TIM3挂在APB1上,你用APB2的使能函数,时钟根本开不了。

避坑指南:我曾经在一个项目里排查了半小时,最后发现是时钟使能写错了总线。记住:TIM1/TIM8在APB2,其他基本定时器在APB1。拿不准的时候,去参考手册的时钟树那一章看一眼。

正确的写法应该是:

// 以TIM3为例,挂在APB1上
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

// 如果是TIM1,挂在APB2上
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);

4.3 GPIO复用功能配置——波形出不去的关键

时钟开了,定时器配置了,但波形还是出不来?大概率是GPIO没配好。

STM32的GPIO引脚默认是普通IO模式。你要输出PWM,必须把引脚切换到复用功能(AF)模式。而且不同定时器的通道对应不同的引脚,这个得查数据手册。

举个例子,TIM3的通道1(CH1)输出PWM,对应的引脚是PA6:

GPIO_InitTypeDef GPIO_InitStructure;

// 使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

// 配置PA6为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// 注意:部分STM32型号还需要配置AFIO或GPIOx_AFRL/AFRH寄存器
// 例如STM32F4系列需要设置GPIO_PinAFConfig()

个人经验:我习惯在配置GPIO时,先确认三件事:①引脚号对不对 ②模式是不是复用推挽(AF_PP) ③有没有遗漏AFIO时钟使能。F1系列有些引脚需要开启AFIO时钟,这个很容易忘。

常见GPIO配置错误汇总:

  • 模式选错:用了GPIO_Mode_Out_PP(普通推挽)而不是GPIO_Mode_AF_PP(复用推挽)
  • 引脚号不对:比如TIM3_CH1是PA6,你配成了PA7
  • AFIO时钟遗漏:STM32F1系列重映射引脚时需要开启AFIO时钟
  • 复用功能未映射:F4/F7/H7系列需要调用GPIO_PinAFConfig()指定复用功能编号

4.4 定时器未启动——配置了不等于运行了

时钟开了,GPIO配好了,波形还是没有?嗯,这里要注意:你配置了定时器的ARR、PSC、CCR,但定时器还没开始计数呢。

很多人写完初始化代码就以为完事了,其实还差最后一步——让定时器跑起来。

// 使能定时器,开始计数
TIM_Cmd(TIM3, ENABLE);

// 如果需要,还可以使能主输出(高级定时器TIM1/TIM8需要)
TIM_CtrlPWMOutputs(TIM1, ENABLE);

这里有个坑:TIM1和TIM8是高级定时器,除了调用TIM_Cmd()之外,还得调用TIM_CtrlPWMOutputs()使能主输出。我刚开始用TIM1的时候,就因为这个函数没调,折腾了一下午。

定时器启动检查清单:

  1. 调用了TIM_Cmd()使能计数器
  2. 高级定时器(TIM1/TIM8)额外调用了TIM_CtrlPWMOutputs()
  3. 检查了TIM_SR寄存器中的UIF位,确认定时器确实在跑
  4. 用示波器或逻辑分析仪测量引脚,别光靠眼睛看LED亮度

4.5 完整排查流程总结

说了这么多,咱们把整个排查流程串起来。下次遇到PWM无波形输出,按这个步骤来:

步骤 检查项 常见错误 验证方法
1 定时器时钟使能 总线选错、遗漏使能 读RCC寄存器,或加断点看时钟状态
2 GPIO复用配置 模式选错、引脚不对、AFIO遗漏 检查GPIOx_CRL/CRH或MODER寄存器
3 定时器参数配置 ARR/PSC/CCR值异常 读TIMx_ARR、TIMx_PSC、TIMx_CCR寄存器
4 定时器启动 遗漏TIM_Cmd、高级定时器缺主输出使能 读TIMx_CR1的CEN位
5 硬件连接 示波器探头没接好、引脚虚焊 用万用表测引脚电平,或用逻辑分析仪抓

最后提醒一句:别一上来就怀疑硬件。我见过太多人,波形出不来第一反应是“芯片坏了”,结果查了半天发现是软件里少写了一行TIM_Cmd()。先软件后硬件,按顺序排查,90%的问题都能自己搞定。