ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32CubeMX电机控制配置详解:PWM、编码器与电流采样

STM32CubeMX电机控制配置详解:PWM、编码器与电流采样 做嵌入式这么长时间我有一个很深的感受STM32CubeMX这东西做流水灯、点个屏可能还看不出它的威力但一旦碰电机控制它就是帮你省下大把时间的关键工具。电机控制涉及的定时器、PWM通道、编码器接口、ADC触发源、刹车引脚、死区时间每一个都藏着一堆寄存器配置。如果你从零手写初始化代码光对着参考手册核对位定义就够你消耗一个下午。而CubeMX的价值就是把这些底层初始化变成可视化操作让你把精力放在真正核心的算法和控制逻辑上。这篇是STM32CubeMX电机控制系列的第二篇上一篇已经把基本工程、时钟树、调试接口这些基础搭好了。这一篇专门聚焦“电机相关配置部分”也就是从CubeMX界面进入Timers、GPIO、ADC这些外设把电机驱动所需的引脚和参数逐一配置好。无论你做的是直流有刷电机、无刷直流电机BLDC还是步进电机只要涉及PWM驱动、编码器反馈、电流采样这篇内容都可以直接照着做。实操中我用的是STM32F407系列CubeMX版本为6.x但整个配置思路在F1、F3、F4、G0、G4等系列上通用。1. 电机控制项目里CubeMX到底帮你省了什么1.1 为什么电机控制离不开CubeMX不少初学者会有个误区觉得用CubeMX生成代码“不高级”非要手写寄存器才有成就感。但在电机控制这个场景下我不建议这么做。原因很简单电机控制涉及的外设实在太多了每个外设之间还存在联动关系。一台直流电机要转起来你至少需要一路PWM控制速度一路GPIO控制方向可能还需要一个编码器接口测量转速一路ADC采集母线电流。如果电机是BLDC那么需要三相PWM每相可能还需要互补输出和死区时间。把这些外设全部初始化正确还要确保引脚不冲突、时钟源正确、中断优先级合理手写起来非常容易出错。CubeMX用图形化方式把引脚分配、外设模式、参数配置呈现在你面前引脚冲突会在界面上直接报红时钟配置错误会在时钟树页面警告这本身就避免了一大批低级错误。更重要的是CubeMX生成的初始化代码和HAL库配合得很干净。你不需要去维护几百行的寄存器初始化函数只需要在用户代码区加入自己的逻辑。这对于电机控制这种需要频繁调整参数的场景特别有用比如改个PWM频率在CubeMX里改一个值重新生成比在代码里翻定时器重装值要简单得多。1.2 电机控制配置的整体架构在动手配置之前先把电机控制项目的整体外设架构理清楚。我自己习惯把配置拆成四块驱动输出、状态反馈、电流采样、调试通道。驱动输出这块核心是定时器的PWM功能。如果是直流有刷电机一般用通用定时器的一路PWM就够了比如TIM2、TIM3、TIM4如果是BLDC或者需要H桥上下桥臂互补导通的大功率电机就要用到TIM1、TIM8这种高级定时器它们支持互补输出和死区插入。方向控制则用普通GPIO接电机驱动芯片的IN1、IN2引脚。状态反馈这块编码器信号通过定时器的编码器接口模式读取位置和速度都靠它。相比于外部中断手动计数定时器编码器模式可以自动识别方向并计数值增减CPU完全不用干预。电流采样这块ADC是标配。采样点放在采样电阻两端或者电流传感器的输出端。CubeMX里可以配置ADC的触发源由定时器事件触发这样采样的时机可以严格同步在PWM周期的特定时刻避开开关噪声。这一块做得好不好直接影响电流环的控制质量。调试通道这块串口是少不了的。配置一个UART用来输出速度、电流、位置信息也用来接收调试指令。没有它你调电机参数的时候会非常痛苦只能靠猜。如果你愿意也可以再配一个DAC输出控制量到示波器但不是必需。在CubeMX里这四块的配置分别在Timers、GPIO、Analog、Connectivity几个分类下。下面我逐个讲。2. 电机相关外设配置从PWM到编码器2.1 最关键的一步定时器与PWM配置定时器的PWM配置是电机控制的核心。你可以在CubeMX左侧的Categories列表里找到Timers然后选择你计划使用的定时器。以直流有刷电机为例我通常用TIM3的Channel 1作为PWM输出。进入TIM3的配置页后在Mode一栏里把Channel1的模式设置为PWM Generation CH1。如果需要在同一个定时器上输出多路PWM比如同时控制两台电机或者BLDC的三相就把对应的Channel都设置为PWM模式。注意同一个定时器的多个通道共享同一个计数频率和周期但占空比是独立的。这一点你必须记住不然配置第二路PWM时会发现频率改不了。在Parameter Settings里有三个参数最重要Prescaler预分频系数Counter Period也就是自动重载值ARRPulse初始占空比比较值后面我会单独讲这几个值怎么算。这里先强调一个容易忽略的点高级定时器TIM1、TIM8在“PWM Generation”之外还有一种叫“PWM Generation Complementary”的模式。如果你在控制H桥或者三相逆变桥上桥和下桥需要一对互补的PWM信号并且还要在换相时插入死区时间防止上下管直通烧毁。这种情况下你要选择互补输出模式然后在Break and Dead Time配置页里设置死区时间。注意高级定时器的互补输出需要配置刹车引脚Break Input以及自动输出使能MOE位。很多新手在CubeMX里生成了代码却发现PWM引脚没有波形多半就是这个原因。在CubeMX中你需要在GPIO设置里把Break引脚正确分配或者在初始化代码中调用HAL_TIM_MOE_PWMConfig相关的配置。实际上CubeMX生成代码后主函数里多半还要手动调用HAL_TIM_PWM_Start高级定时器可能还要确认__HAL_TIM_MOE_ENABLE已经生效。2.2 方向信号和刹车信号GPIO别乱接电机的方向控制看起来简单其实有一部分人在这里栽过跟头。你需要在CubeMX的GPIO设置里把方向引脚配置为推挽输出模式GPIO_MODE_OUTPUT_PP速度可以设成GPIO_SPEED_FREQ_HIGH。驱动芯片比如L298N、TB6612、DRV8825的IN1、IN2引脚就是通过这些GPIO来控制高低电平从而实现正转、反转和刹车。在这里我推荐一个规则把方向引脚和PWM引脚放在同一个定时器通道附近为什么要这样因为电路板布线时PWM信号和方向控制信号如果离得太远走线麻烦而且容易出现干扰。CubeMX里你可以在Pinout视图里直接用鼠标拖拽引脚把同一组信号尽量安排在同一侧。如果用的是BLDC或者大功率电机驱动必须关注Brake引脚也就是刹车输入。这个引脚通常接到驱动芯片的故障输出或者急停按钮上。它的作用是当外部信号触发时硬件立即封锁PWM输出保护电机和功率管。这个功能靠CPU中断是来不及响应的所以一定要用硬件刹车。CubeMX里在Break and Dead Time配置页把它打开并且选择合适的极性。2.3 编码器接口配置编码器反馈是闭环控制的眼睛。STM32的很多定时器内置了编码器接口模式可以同时接收A、B两路正交信号自动判断方向并计数。你不需要再用外部中断自己处理脉冲CPU占用几乎为零。在CubeMX里配置编码器首先要选对定时器。以STM32F407为例TIM2、TIM3、TIM4、TIM5都支持编码器模式。在TIM3的配置页面把Combined Channels设置为Encoder Mode然后Channel1和Channel2就自动变成了TI1FP1和TI2FP2分别对应编码器的A相和B相。Encoder Mode有三个选项TI1、TI2、TI1 and TI2。TI1模式只检测A相上升沿计数精度低一些TI1 and TI2模式会在A、B相每个边沿都计数也就是4倍频精度最高。绝大部分场合建议直接用TI1 and TI2配合合适的编码器线数可以让位置分辨率提升4倍。这里有个坑编码器模式的计数方向取决于A、B两相的相位关系如果你的电机正转时计数反而减小并不需要改硬件只需要在CubeMX里把Encoder Mode的Polarity相关的信号反相或者在代码里把方向取反。很多文档提到Encoder Mode有TI1FP1和TI2FP2的两个Polarity选项设置为Rising/Falling组合可以改变计数方向。我习惯在测试阶段先确认方向正确再决定是否在代码里取反。2.4 电流采样用的ADC配置电流采样是电机控制里最容易出问题的地方。CubeMX里配置ADC很多人一上来就选独立采集模式然后不断在代码里调用HAL_ADC_Start等转换完了再取数据。这种做法的缺点是采样时刻不确定而电机控制要求电流采样必须同步在PWM周期的特定时刻比如PWM中心点这个阶段开关噪声最小采出来的电流最干净。要实现同步采样需要在CubeMX里把ADC的触发源设置为定时器事件。以TIM1的PWM周期作为触发源为例在ADC配置页面里External Trigger Conversion Source选择Timer 1 Trigger Out event触发边沿选择上升沿。这样ADC转换就会和PWM周期严格同步。另一个容易忽略的点是采样时间。在ADC的Parameter Settings里Sample Time决定ADC采一个点需要多少个时钟周期。采样时间太短采样电容充不饱采集到的电压偏低太长则可能会采到开关噪声。对于电机电流采样我一般设置Sample Time为15个周期以上如果使用的是高内阻的分压电路可能还要更长。这部分的参数需要在调试时根据波形微调。CubeMX还支持注入通道和规则通道两种模式。规则通道适合常规周期采集注入通道可以在外部事件触发时打断当前转换。在电机控制中有时会用注入通道做三个桥臂电流的同时采样不过这一块对初学者来说有点复杂建议先把规则通道同步采样调通再说。2.5 串口调试接口配置串口调试接口没什么高深内容但非常重要。在Connectivity里选择USART模式选Asynchronous波特率一般设115200数据位8位停止位1位无校验。CubeMX会自动分配TX、RX引脚和对应的时钟。我建议把DMA接收打开。电机运行时主循环可能正在处理控制算法如果用阻塞式接收一个字节进来就得等半天很容易丢数据。DMA接收配合空闲中断可以在不占用CPU的情况下接收不定长的调试指令。CubeMX里的具体操作是在USART设置里DMA Settings添加USART_RX通道然后在代码里实现接收回调。完成串口配置后你还应该在中断优先级里把USART的优先级适当调高但不要高于控制定时器和编码器相关的中断原因后面在排查部分我会细说。3. 关键参数计算与生成代码后的补全3.1 PWM频率和占空比怎么算PWM频率直接决定了电机的噪音、驱动芯片的开关损耗和电流纹波。选择PWM频率时需要权衡几个因素频率太低电流纹波大电机运行不平稳频率太高MOS管开关损耗急剧增加驱动芯片容易发热甚至是烧毁。常用直流有刷电机的PWM频率范围在10kHz到50kHz之间实际项目中我常用16kHz或20kHz这个频段高于人耳能明显感知的范围噪音小同时开关损耗还在可控范围内。在CubeMX中PWM频率的计算公式是PWM频率 定时器时钟频率 / ((Prescaler 1) * (Counter Period 1))举个例子APB1定时器时钟是84MHz想得到20kHz的PWM那么先选Prescaler为84-1即83那么计数时钟就变成1MHz再用1MHz除以20kHz得到50所以Counter Period就是50-1即49这样自动重载周期是49每计数到49就溢出一次频率就是1MHz/5020kHz。占空比通过设置Pulse的值实现Pulse的取值范围是0到49Pulse25就是50%占空比。有一个很多新手会踩的坑在CubeMX界面上Prescaler和Counter Period的单位是实际写入寄存器的值需要自己减1不是直接把1MHz和20kHz填进去。你在数据手册或者参考代码里看到的频率计算公式也全都是基于“寄存器值1”来算的。所以你填的时候要注意比如想写84分频寄存器值要填83。3.2 死区时间计算使用高级定时器做互补PWM时必须设定死区时间。死区是什么在H桥或三相桥驱动中为了保证上下两个功率管不会同时导通需要在关断一个管子之后、打开另一个管子之前插入一段同时关断的时间。这个时间就叫做死区时间。死区时间过短可能发生上下管直通瞬间烧掉功率管死区时间过长会导致输出波形失真产生额外的谐波损耗。工程上一般根据功率管的开关参数来确定比如IGBT通常需要几微秒到几十微秒低压MOS管只需要几百纳秒到一两微秒。STM32高级定时器的死区时间由DTG寄存器的值控制CubeMX里则直接以秒为单位显示。在Break and Dead Time配置页Dead Time项可以填入纳秒或微秒值。比如要让死区时间大约为500ns假设定时器时钟是84MHz一个计数周期大约是11.9ns那么500ns除以11.9ns大约等于42你可以在CubeMX里选择对应的档位。如果你完全没概念可以先取一个保守值比如1us再通过示波器观察实际波形调整。能测出来再调比凭空估算靠谱。3.3 CubeMX里的具体操作路径这里我把前面讲的配置串成一个完整的流程方便你照着抄。启动CubeMX打开上一篇配置好的工程左侧Categories里按顺序操作。第一步Timers选择TIM1。如果做BLDC在Mode里把Channel1、Channel2、Channel3全部设置为PWM Generation Complementary CHx然后在Parameter Settings里设置Prescaler、Counter Period、Pulse。如果做直流有刷电机可以用TIM3把Channel1设置为PWM Generation CH1。第二步Timers选择TIM3如果TIM3已经用于PWM可以选TIM4把Combined Channels设置为Encoder Mode确认Channel1和Channel2自动变为编码器输入。第三步Analog选择ADC1在Configuration里把External Trigger Conversion Source选为Timer 1 Trigger Out event这是让电流采样与PWM同步。第四步System CoreGPIO把方向控制引脚设置为GPIO_MODE_OUTPUT_PP把必要的使能脚、刹车脚设置为输入或输出。第五步ConnectivityUSART选择异步模式波特率115200打开DMA接收。全部设置完成后点击GENERATE CODE生成工程。生成之前记得在Project Manager里确认Toolchain选择正确我这边用的是MDK-ARM。生成之后代码还不算完因为CubeMX只生成了外设初始化并不会自动启动PWM、编码器或ADC转换。3.4 生成代码后必改的几处生成代码之后有几处需要手工修改这是从“生成代码”到“电机能转”的关键一步。首先是PWM启动。在main.c的main函数中while循环之前加入PWM启动代码。以TIM1的三相互补输出为例HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_2); HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_3);如果是三相桥还要启动互补输出通道HAL_TIMEx_PWMN_Start(htim1, TIM_CHANNEL_1); HAL_TIMEx_PWMN_Start(htim1, TIM_CHANNEL_2); HAL_TIMEx_PWMN_Start(htim1, TIM_CHANNEL_3);如果漏了第二步你可能只看到上半桥有波形下半桥始终没有输出。这个问题排查起来很隐蔽因为不是没有配置而是没有启动。其次是占空比调节。运行时不要直接修改CCR寄存器用HAL库的宏__HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, 500);把比较值设为0到ARR之间的任意值就能实时改变占空比。注意Pulse的初始值在CubeMX里已经设定如果你在代码里不从0开始电机上电就会直接转这在某些设备上是很危险的事情。所以初始Pulse最好在CubeMX里设为0等系统确认安全后再赋值。然后是编码器计数清零。编码器初始化后计数器会从某个随机值开始所以要在主循环前清零__HAL_TIM_SET_COUNTER(htim4, 0);之后读取位置就调用__HAL_TIM_GET_COUNTER(htim4)。注意这是一个带符号的计数值方向反转时值会减小别用无符号整型去接收否则方向判断会乱套。ADC启动方面如果用同步触发方式实际上不需要在代码里不断开启转换。你可以在初始化后打开ADC然后等定时器事件来触发HAL_ADC_Start_DMA(hadc1, (uint32_t*)adc_buffer, 3);这里adc_buffer用volatile修饰DMA会把采到的数据直接写进数组。之后在while循环里通过adc_buffer获取当前采样值注意处理DMA的半满和全满中断。4. 常见问题与排查技巧实录4.1 PWM有配置但电机就是不动这是出现频率最高的一个问题。配置看起来都对代码也编译下载了但电机就是纹丝不动。我的排查顺序是固定的。先看示波器测PWM引脚的输出波形。如果根本没有波形重点检查PWM启动函数是否调用尤其是高级定时器的MOE使能。你可以检查以下几个点HAL_TIM_PWM_Start对应的通道是否正确CH1、CH2、CH3别弄混高级定时器有没有启动互补通道GPIO的复用功能是否被CubeMX正确设置为AF模式有些情况下你手动改过引脚配置导致复用功能丢失Break引脚是否处于有效电平如果刹车被激活PWM会被硬件强制关闭如果PWM波形正常但电机依然不转下一步就查方向GPIO和使能引脚。很多驱动芯片比如TB6612有一个STBY引脚这个引脚必须拉高电机驱动部分才工作。如果你只给了PWM忘了拉高STBY电机同样不转。还有一种情况是驱动芯片的供电和逻辑电压不一致。比如驱动芯片的VM接了12V但VCC却用的是3.3V有些芯片虽然逻辑输入兼容3.3V但如果你接错了引脚也会导致无法工作。这时候用万用表量一下各个电源管脚基本就能定位。4.2 编码器读数乱跳编码器计数异常先别急着怀疑代码。我最常遇到的原因其实是硬件问题比如A、B相接线松动或者和电源线绑在一起产生干扰。编码器信号是高阻输入很容易受干扰建议在上拉电阻方面做好处理。在CubeMX里编码器输入引脚可以设置内部上拉通常建议打开。接下来要确认方向。如果电机正转计数器却在减小这是正常的只要在代码里做一次符号反转或者对调A、B两相输入就行。注意对调A、B相后的计数方向会改变意味着一开始的零位和方向都会变控制逻辑也要同步调整。还有一种情况是计数值出现大范围跳变比如从100突然变成65530。这种通常是计数器因为溢出导致即计数范围超过了定时器的16位或32位计数上限。解决办法有两种一是改用32位定时器比如TIM2、TIM5二是在代码中处理溢出通过TIM的更新中断统计溢出次数合并出更宽的范围。简单应用里如果电机转动范围不大可以直接用有符号短整型接收16位计数值并且确保计数范围不要超过正负32767。4.3 电流采样噪声大电流采样噪声大是电机控制中最让人头疼的问题之一。示波器上看到的波形不是平滑的正弦或直线而是一堆毛刺。原因无外乎两个采样时刻不对或者硬件滤波不够。采样时刻的问题前面提过一定要让ADC转换由PWM事件触发。如果你还是用软件定时器触发采到的瞬间可能正好碰上MOS管开关沿噪声巨大电流环根本没法用。我把触发方式改到定时器事件之后噪声明显减少一个量级。硬件滤波方面最常见的是RC滤波。在采样电阻到MCU的ADC引脚之间加一个100欧姆电阻和1nF电容组成的低通滤波器截止频率大约1.6MHz这个配置在很多开发板参考设计里都能看到。不过要注意RC滤波会引入相位延迟如果电流环带宽要求很高RC不能加太大。这个需要在控制性能和噪声抑制之间找一个平衡点。还用一种很容易被忽略的噪声源ADC的采样时间太短。采样电容还没充满转换值就不稳定波动很大。这种情况下不需要改硬件把CubeMX里的Sample Time加长比如设成15个周期或更久数据会稳定很多。你可以自己做实验对比通常这个改动会带来肉眼可见的改善。4.4 调试时最好用的工具组合最后分享一套我调试电机控制时的固定工具组合。一个双通道示波器用来观察PWM波形和电流波形一个逻辑分析仪用来抓编码器信号和控制信号的时序再加一个串口助手配合MCU发送的调试信息用来查看内部变量。逻辑分析仪对排查PWM和编码器问题特别有效尤其是当你怀疑互补PWM和死区时间不对时。你不需要很贵的设备几十块钱的USB逻辑分析仪就能胜任配合开源软件可以看到每一路信号的跳变沿死区时间长度一目了然。串口调试信息的格式我建议用printf重定向到USART输出时带上时间戳比如printf(%d,%d,%d\r\n, timer_cnt, adc_value, speed);然后用串口助手的波形显示功能直接画出变量曲线。用这种方式可以快速判断速度环、电流环是否收敛比看一堆十六进制数组直观得多。我调试电机时很多参数都是靠这种方式边跑边改的。还有一点经验调试时最好把初始占空比设得很小确保哪怕出了问题电机也不会突然满速乱转。等确认控制方向正确、反馈接线无误后再逐步加大输出范围。这个习惯帮你避免了不少“电机突然飞转”的惊险场面也保护了驱动板和被驱动的机械结构。电机控制涉及的知识面确实很杂但把CubeMX里这一层配置搞定等于是把硬件和底层驱动之间的通道打开了。后面无论是做速度闭环还是位置闭环你面对的就只剩下控制算法本身而不用再纠缠于引脚映射和寄存器位定义。按照这篇文章的顺序把PWM、方向、编码器、电流采样、串口调试这几块配好你的电机控制项目基本就成功了一半。
返回列表