☰
STM32CubeMX电机控制外设配置:PWM、编码器、ADC与GPIO规划
2026/10/5 6:17:39 网站建设 项目流程

做电机控制,尤其是用STM32CubeMX来做,卡住大家的地方往往不是写代码那一步,反而是配置这一步。GPIO、定时器、编码器、ADC,每个外设单独拎出来都能跑,但拼在一起凑成一套完整的电机控制链路时,它们之间是怎么配合的、参数之间怎么互相牵制,这才是真正花时间的地方。上一篇讲了工程建立和时钟树基础,这篇把电机相关的外设配置摊开讲——PWM怎么出、反馈怎么采、引脚怎么规划,配置完了生成代码之后还要补哪些手工活。

这篇适合两类人:一是刚用CubeMX做完点灯、串口,想往电机控制方向走的朋友;二是已经手动写过寄存器或标准库程序、想对比一下CubeMX方案的效率。我不会只给你截图级的点鼠标指引,更多是解释每个配置项背后的原因,因为把"为什么"想通了,换一块开发板、换一颗芯片,你照样能快速配出来。

1. 控制一颗电机,MCU端到底在忙什么

在打开CubeMX拖引脚之前,先把整个系统的信号链路走一遍。电机本身是靠功率驱动电路供电的,MCU通常不直接驱动电机,它只负责给驱动电路发"指令信号",同时把电机当前的状态读回来,形成一个闭环。

指令信号这一侧,核心是PWM。电机两端电压的平均值由PWM占空比决定,占空比越大,等效电压越高,电机转速越快。但光有PWM还不够,对无刷电机、或者用H桥控制的直流电机来说,你还要控制换相顺序,这就要规划好TIM的输出通道和GPIO的配合。

反馈信号这一侧,至少要有速度反馈和电流反馈。速度反馈一般来自编码器——增量式编码器输出A、B两路相位差90°的方波,MCU通过对A、B相计数来推算转速和方向。电流反馈来自采样电阻,经过运放放大后送进ADC,MCU根据电流值做电流环控制,或者做限流保护。温度、电压母线这些属于辅助反馈,但关键就这几路。

再把算法算进来,整个"MCU视角"的电机控制任务就是:以固定的控制周期(比如10kHz)执行一次PID运算——读编码器得速度,读ADC得电流,算占空比,更新PWM输出寄存器。这个循环要做到稳定且不抖动,底层的定时器、ADC触发方式就得设计好。CubeMX在这里的作用,就是把上面这些外设的初始化代码一次性生成出来,同时通过图形化的方式帮你检查引脚冲突。它不替代你思考,但它能把你从数百行初始化代码里解放出来。

2. PWM输出通道:为什么电机控制首选高级定时器

2.1 高级定时器和普通定时器的本质区别

CubeMX左侧的定时器列表里,TIM1、TIM8属于高级定时器,TIM2-TIM5是通用定时器,TIM6、TIM7是基本定时器。做电机PWM输出,优先选TIM1或TIM8,不是因为它们名字听起来厉害,而是它们有三个普通定时器没有的能力:

第一是互补输出。一个通道能同时输出两路反相的PWM(CH1和CH1N),并且两路之间可以插入死区时间。驱动芯片或H桥电路里,上桥和下桥的开关管不能同时导通,否则就是直通短路烧管子。反相信号加上死区,保证一个管子完全关断之后另一个才打开。

第二是刹车输入(Break Input)。高级定时器有一个专门的中断/事件输入,用来做故障保护。外部过流信号、驱动芯片的FAULT引脚一拉低,硬件立刻把所有PWM输出封锁到安全电平,不需要CPU参与。这种硬件级保护在处理过流时比软件快一个数量级,是靠定时器硬件实现的。

第三是重复计数器和更新事件的灵活控制。做电机控制时,PWM的更新事件可以作为ADC的触发源,让电流采样正好发生在PWM周期的特定时刻,这对电流环的稳定性非常重要。

通用定时器能不能做PWM?能,但只有单端输出,要产生互补信号得用软件反相,死区也得自己算延时,急停保护更得靠外部中断干,响应慢且代码复杂。所以别在通用定时器上死磕,CubeMX里直接把TIM1/TIM8让给PWM。

2.2 PWM频率怎么定,20kHz怎么来的

PWM频率的选择综合了三个因素:噪声、纹波、开关损耗。

人耳能听到的频率范围大概是20Hz到20kHz,PWM频率低于20kHz时,电机会发出尖锐的啸叫。所以绝大多数电机驱动器把PWM频率定在20kHz或更高,一是避开听觉范围,二是这个频率下电流纹波已经很小。但频率越高,开关管的开关损耗越大,驱动芯片发热越明显。20kHz是工程上最常用的折中值。

如果用的是自制H桥或老式驱动芯片,开关速度跟不上,可以适当降到10kHz左右,配合加大电枢电感滤波,也能工作。但用集成电机驱动芯片(比如常见的DRV系列、IR2104+MOS方案),20kHz完全没问题。PWM频率还需要避开机械系统的谐振点,有些电机在特定频率下会产生共振噪音,这时把PWM频率微调几百赫兹,共振就消失了。

2.3 预分频和自动重装载的计算方法

在CubeMX的TIMx配置页里,关键参数就三个:Prescaler(预分频)、Counter Period(自动重装载值)、Pulse(占空比)。它们之间的关系是:

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

以STM32F103这类常见的72MHz主频为例,如果定时器时钟就是72MHz,想得到20kHz的PWM:

  • 预分频PSC=71,得到1MHz的计数时钟
  • 自动重装载ARR=49,1MHz除以50,正好20kHz

这里有一个经常被忽略的权衡:ARR越大,PWM的占空比分辨率越高。ARR=49意味着占空比只有0到49共50级,做调速勉强够用,但精细控制就不够了。所以在实际项目里,我会反过来选:先定ARR,再反推PSC。比如想要10bit分辨率也就是0~1023级占空比,同时保持20kHz频率,就让ARR=999,然后计算PSC=72MHz/(20kHz*1000)-1=2.6,取整后实际频率会略有偏差,但影响不大。提高分辨率、保证频率,两边要平衡着看,这也是很多新手困惑"Prescaler和Period到底先填哪个"的答案——没有一个固定答案,取决于你要的分辨率和频率。

死区时间在CubeMX的"Break and dead-time setting"里配置。具体数值取决于你用的开关管和驱动芯片的上升/下降时间,一般取0.5μs到2μs之间。太短,容易直通;太长,占空比调节范围变小,波形畸变。用IR2104这种自带死区延时的芯片,MCU侧死区可以设小一点;用两颗三极管或半桥芯片直接驱动,MCU死区要设足。这里有一个测试方法:先把死区设大(比如3μs),用示波器看上下桥的导通波形,再逐渐减小,直到刚好不出现同时导通,就是临界值,再加20%~30%的余量。

2.4 PWM模式选择和占空比初始值

CubeMX中PWM模式有Mode1和Mode2的区别,核心就是有效电平的极性不同。Mode1是CNT小于CCR时输出有效电平,计数器值大于CCR后翻转。配套驱动芯片用哪种模式,取决于芯片的输入有效电平定义,很多驱动芯片的输入信号是"高电平有效",那用Mode1就对了。需要注意的是,PWM的初始占空比不要设成0%或100%的两个极端,初始时给一个安全的中间值,比如20%~50%,避免上电瞬间电机突然全速转。在代码启动PWM前,也要确保电机的使能引脚处于关闭状态,这个后面讲GPIO规划时会提到。

3. 编码器反馈:用定时器的Encoder Mode读速度

3.1 为什么不用外部中断自己数脉冲

很多新手拿到编码器第一反应是配置两个GPIO外部中断,然后在中断服务函数里计数。这个方案在极低速时能用,但到了几百上千转就不行了。编码器每转一圈可能输出几百到几千个脉冲,转速一高,中断频率爆炸,CPU被占用不说,还要处理两路信号的先后时序来判方向,代码复杂度飙升,很容易丢脉冲。

真正的做法是用定时器的Encoder Mode,也就是正交编码器接口。A、B两路信号直接接到定时器的两个输入通道上,硬件自动判断谁先谁后,从而识别正转反转,自动计数,完全不占用CPU。需要读取速度时,直接读定时器的计数器值就行了。CubeMX里把定时器的Combined Channels配置为Encoder Mode,定时器就变成编码器接口,这是硬件支持的事,不是软件模拟。

3.2 编码器模式的配置细节

在CubeMX里配置编码器模式时,有以下几个需要认真选择的项:

第一是Encoder Mode选择。有三个选项:TI1、TI2、TI1 and TI2。区别在于计数频率和方向判断的采样逻辑。TI1 and TI2模式同时利用A、B两相的上升沿和下降沿,对每线脉冲实现了4倍频,分辨率最高,是实际项目中最常用的。如果你不需要那么高的分辨率,或者编码器每圈脉冲数已经很多,用TI1模式也行,2倍频或1倍频。

第二是计数方向。CubeMX的Encoder Mode配置页有方向的极性设置,如果电机正转时计数器反而递减,不用改硬件,把这个方向位取反就能解决。这个坑在接线后第一次测试经常遇到。

第三是计数溢出范围。定时器的计数器是16位的,ARR设成多少很重要。如果设成0xFFFF,计数范围是0到65535,溢出周期长,但读取时要做无符号、有符号的转换。也可以设成编码器每圈脉冲的4倍频数,让计数器每圈归零一次,读起来更直观。我个人的习惯是设成4倍频的整圈脉冲数,这样计数器值直接就是"当前位置在一圈内的偏移",配合一个单独的圈数变量,就能组成多圈绝对值。

3.3 速度换算:从计数值到转速

编码器读到的只是脉冲个数,要变成转速,需要一步换算。假设编码器是1000线的,用4倍频模式,那么电机每转一圈,计数器变化4000。在CubeMX里我可以把定时器配置成每10ms读一次计数器值,两次读数之差除以4000,再乘以10,得到每秒转数,最后乘60就是RPM。

这里有一个工程经验:如果控制周期是固定的,相邻两次读取计数值之差直接等于当前周期的脉冲增量,这个增量除以每圈脉冲数,就是这个周期内的平均速度。低速时候,一个控制周期内可能只有几个脉冲,速度分辨率很低,这是正常的,解决方案是加长测速时间窗(滑块滑动平均)或者用高线数编码器。这些属于控制算法的范畴,但配置编码器的时候就要想好你选多少线的编码器、期望最低测到多快的速度,否则采样分辨率不够,后面PID参数怎么调都抖。

4. 电流采样:ADC配置不简单是"开个通道"

4.1 电机电流采样对ADC的要求

电流采样虽然用的也是ADC,但它和随便测个电压不太一样,难在两个地方:一是时机,二是量程。

时机问题很好理解。PWM周期内电流波形是锯齿状的,在开关管导通瞬间,电流可能是峰值;在临近关断时,电流可能是谷值。在不同时刻采样,读数差异很大。电流环要稳定,每次采样的时间点必须固定,否则反馈值自带噪声,控制效果不可能好。CubeMX里合适的方式是让PWM定时器的更新事件或某个比较事件触发ADC转换。PWM计数周期结束、也就是PWM的更新事件发生时,电流基本稳定,这时触发ADC采集,能够得到比较平均的相电流值。

量程问题在于,电流采样电阻上的电压通常只有毫伏级,直接进ADC什么都不够灵敏。一般要经过运放或专门的电流检测放大器放大后再进ADC。到这里就要考虑ADC的参考电压和放大倍数的匹配了。假设采样电阻0.01Ω,最大电流10A,那么满量程电压是0.1V,如果运放放大20倍,送到ADC的就是2V。这时ADC参考电压3.3V,还可以,但如果你用的参考电压是2.5V,就过冲了。这种量程计算要在CubeMX配置之前做好,选好运放增益,才能决定ADC的满量程。

4.2 规则组还是注入组、要不要DMA

CubeMX里的ADC配置有两个常见选择:一是用规则组加DMA,二是用注入组。

规则组配合DMA适合连续采样多路信号的场景,比如同时采电机A相电流、B相电流、母线电压。DMA自动把转换结果搬到内存数组,不需要CPU逐个读取。缺点是要等DMA传输完成再一起处理,实时性方面稍弱,但电机控制周期通常在100μs级别,DMA完全来得及。

注入组有优先级高、可被规则组打断的特点,更重要的一点是它支持硬件触发。很多Timer的TRGO事件可以直接触发注入组转换,这在时间要求严格的电机控制里非常有用。PWM更新事件一来,ADC注入组立刻开始采,转换完成置标志位,CPU在中断里读出电流做算法。这种"事件到数据"的路径最短,抖动最小。

我的建议是:如果只采相电流和母线电压两三路信号,直接采用注入组加定时器触发,通道不多、逻辑清晰,实时性好;如果顺带还想采温度、电位器这些不需要高速的电压信号,那规则组加DMA就更方便,低速信号用轮询或慢速DMA也够。

4.3 采样保持时间和毛刺问题

CubeMX里ADC的Sampling Time参数,经常被忽略。它决定了一次采样给电容充电的时间,设置太短,采样结果偏小且不稳定;设置太长,转换时间变长、吞吐率变低。电机控制场景推荐用中等偏长的采样时间,比如1.5μs或更高。因为电流信号叠加了PWM开关噪声,采样时间长一点,相当于做了一次硬件平均,效果比软件滤波更自然。

但采样时间长不等于能解决所有噪声问题。实测中我发现,如果采样时刻正好落在PWM开关沿附近,ADC读到的毛刺特别大。这时应该在CubeMX的触发配置里调整触发事件的位置,或者在PCB上的RC滤波也在起作用。还有一个软性缓解手段是软件滤波——连续采几次,剔除最大值和最小值再取平均,代价是增加延迟,用在保护逻辑上足够,用在电流环内部就要谨慎,延迟太大会影响带宽。

5. 控制引脚与保护逻辑:GPIO规划思路

5.1 驱动芯片的输入引脚怎么接

电机控制系统的GPIO不一定全是"通用"的——负责输出的GPIO大致分两类:一类是PWM信号脚,它们由定时器外设接管,CubeMX里引脚配置成TIM_CHx或TIM_CHxN即可;另一类是方向、使能、模式选择脚,这些是普通输出口,需要你规划。

以一个典型的半桥驱动加H桥拓扑为例,定时器的CH1、CH1N输出互补PWM接到驱动芯片的INH和INL,控制一个桥臂;另一个桥臂由CH2、CH2N控制。这样两个通道正好控制一个H桥,电机正反转通过调整占空比差值实现。而在无刷电机的场景下,三组互补输出对应三相,就是BLDC或PMSM控制的典型配置,此时需要更多的GPIO做换相逻辑辅助,比如控制预驱芯片的EN、PWM模式等。

一组常见的GPIO规划表(以直流有刷电机为例)看起来是这个样子:

功能引脚分配建议配置方式
PWM高边TIM1_CH1复用推挽输出
PWM低边TIM1_CH1N复用推挽输出
驱动使能任意GPIO推挽输出,初始为低
方向切换任意GPIO推挽输出,初始为低
过流故障输入带外部中断的GPIO上拉输入,下降沿触发

这里面"初始为低"是很多人容易忽略的细节。在CubeMX里配置GPIO时,把输出水平的初始值设置成安全电平,而不是默认的低电平或高电平——具体哪个才是安全电平,看你驱动芯片的使能极性。有些芯片是高电平使能,那上电默认低就是安全的;有些是低电平使能,那初始高才是安全。我习惯于把电机使能脚单独拉出来,初始状态为"不使能",等程序跑到初始化完成、PWM开始输出之后,再在软件里拉高使能,这样上电瞬间电机绝对不会乱转。

5.2 刹车输入和故障信号的处理

前面说过,高级定时器的刹车输入能做硬件级故障保护。CubeMX的Break设置里,把刹车信号源配置为外部输入引脚,比如PA6或PB12。驱动芯片的FAULT引脚接过来,平时是高电平,过流就拉低。CubeMX配置成"低电平触发刹车",这样硬件自动在几个时钟周期内封锁PWM,你的电流环根本来不及反应,管子已经安全了。

刹车输入的另一种做法是用软件触发:检测到ADC电流值超限后,在代码里调用刹车事件,强制关闭PWM。这作为第二层保护存在。但代码执行是有延迟的,所以硬件刹车优先级更高。硬件刹车是结构上兜底,软件保护是日常运行的规则,两者都要有,少了哪个都不对。

5.3 引脚速度、上下拉的工程经验

GPIO的Speed等级和上下拉配置,看似不起眼,但对电机控制这种大电流、强干扰场景影响很大。PWM输出脚如果GPIO速度配置太低,信号边沿变缓,驱动芯片的输入波形畸变,会增加额外的开关损耗。配置成High或Very High速度,让边沿尽量陡峭。但速度太高、线路过长又会带来振铃,需要结合PCB走线长度取舍。

驱动芯片的FAULT输入,上拉电阻要保留,否则信号悬空时容易被干扰误触发。另外建议在FAULT输入引脚到地之间加一个小电容,配合GPIO内部上拉,构成低通滤波,滤掉窄脉冲干扰。这些细节不在CubeMX图形界面里体现,但你要知道,生成的初始代码里GPIO初始化结构体就包含这些参数,在CubeMX里选好,生成代码就一步到位了。

6. 生成代码之后,还要手工补的几件事

6.1 先确认两个状态:PWM输出启动和计数器清零

CubeMX生成的main函数里,外设的初始化代码都在,但PWM并不会自动开始输出。需要调用定时器的启动函数,比如HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1),互补输出通道还需要HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1)。这是最容易漏的一步。同时,编码器定时器虽然配置好了,但它默认就在计数,只是还没清扫零点,上电后清零一次,让位置有一个绝对起点。

这里还有一个小技巧:启动PWM时,先把占空比设置为某个安全值,比如5%或10%,再调用启动函数。如果CubeMX里已经把Pulse设成安全初值,就直接启动;如果没有,启动瞬间输出可能是满占空比或零占空比,电机可能猛冲一下。所以生成代码后,第一步先去main函数里把CCR寄存器的初始值确认好,再启动PWM。

6.2 控制周期怎么搭

CubeMX生成的外设配置只是"硬件就绪",真正的电机控制算法还得有一个固定的时间基准。常见做法是用一个定时器的周期中断作为控制节拍。比如让TIM2每100μs产生一次更新中断,在中断服务函数里依次完成:读编码器计数值、读ADC采样结果、执行速度环/电流环PID计算、更新PWM比较寄存器。

在CubeMX里配置这个控制节拍定时器时,注意两点:一是中断优先级,PWM相关的ADC中断和控制节拍中断要分配合理优先级,不能让串口这种低速中断打断控制节拍;二是中断服务函数里不要做耗时操作,OLED刷新、串口打印这些通通发到主循环或低优先级任务里,否则控制周期抖动一大,高速运转的电机声音都会变得尖锐刺耳。用HAL库时,回调函数的执行时间要控制好,我实测在72MHz的F103上,一个简单的电流环+速度环控制在中断里跑,大概消耗20%~30%的CPU时间,这是可接受的。

6.3 电流采样时刻与PWM边沿错开的实测教训

这个坑我是实际踩过的。刚开始配置时,用PWM更新事件触发ADC采样,逻辑上没错,但实测电流波形上有很大的尖刺。后来用示波器对比PWM输出和ADC采样点才发现,采样时刻正好卡在了PWM关断瞬间的振荡上,采到的是噪声而不是真实的电流。

解决办法有两种:一是把ADC的触发事件从更新事件换为比较事件,通过调整比较值把采样点挪到PWM周期的中间段,避开开关沿;二是在ADC转换完成后,软件丢弃前几次采样值,取后续稳定值。前者是更彻底的方案,CubeMX里需要配置定时器的TRGO事件为OCxREF信号,这属于稍微进阶一点的用法,但对电流环质量提升非常明显。调完之后电流波形明显光滑了,速度波动也小了。这个经验在所有PWM中心对齐和边沿对齐的配置中都适用——你的采样点永远要避开PWM切换引起的震荡窗口。

6.4 调试接口留好:串口打印哪些量

最后建议在CubeMX里把串口和一个空闲定时器留好,后面调试PID用的。带PID控制的电机调试,几乎不可能一次调好。速度给定值、编码器读数、电流采样值、PID三个输出值,至少这四项要能用串口打印出来。通过串口波形工具或虚拟示波器,可以直接在电脑上观察速度曲线和电流波形,判断系统有没有振荡、饱和、积分过深等问题。

有朋友问过,串口打印会不会影响控制?只要打印放在主循环或低优先级任务里,不放在控制中断里,就没有影响,串口波特率115200足够了。CubeMX配置串口时,打开DMA发送模式,主循环里丢给DMA发送,CPU几乎零负担。

做电机控制,很多时候不是算法多复杂,而是基础配置有没有踩实。PWM频率、死区、编码器模式、采样时刻、保护引脚,这些在CubeMX里都配好了,生成的代码就是一架调整好引擎的飞机的骨架,后面往里面灌PID算法、做上位机交互,都是水到渠成的事。用CubeMX把这些底层的初始化逻辑理顺,你就能把更多精力放在真正有意思的控制逻辑上,不用再跟寄存器初始化代码较劲了。下一步如果时间允许,我计划写一篇从零把速度环和电流环调通的实操记录,把PID参数整定的细节和波形结合着讲,那部分内容很实际、很适合接着这篇往下做。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询