ARTICLE DETAIL

资讯详情

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

STM32双路直流有刷电机驱动:H桥与PWM控制实战

STM32双路直流有刷电机驱动:H桥与PWM控制实战 简介一套面向嵌入式开发者的STM32双路直流有刷电机驱动完整工程代码覆盖GPIO初始化、PWM调速、正反转切换与异常保护等关键控制逻辑适合需要快速实现运动控制或学习HAL库电机驱动方法的开发者。压缩包共236个文件以121个.h头文件和107个.c源文件为主包含HAL库驱动、应用层电机控制代码、Keil工程文件.uvprojx、编译产物.hex/.bin及调试配置.scvd/.uvoptx整体仅1.67MB便于直接下载阅读与移植。已有292人学习工程内代码注释清晰模块划分明确可直接参照配置定时器PWM输出与外部中断逻辑也可通过源码分析启动、停止、换向流程及保护机制对入门STM32电机驱动和复用控制逻辑均有实际参考价值。1. 用STM32驱动双路直流有刷电机先得想清楚这几件事STM32驱动双路直流有刷电机看起来是单项工程里的基础环节但“能转”和“转得稳”之间差着好几个调试夜。双路驱动并不等于两套单路驱动简单叠加还要处理每个电机的正反转、PWM占空比分辨率、停车时要不要主动刹车以及左右两侧负载不一致时的速度匹配。这篇文章给出从H桥电路、CubeMX定时器配置到HAL库封装的一整套做法可以直接落到小车、云台或泵类项目上。适合正在做基于STM32的毕业设计、想从点灯补到运动控制的同学也适合从标准库切换HAL库后想理清定时器通道分配的人。2. 驱动电路与引脚分配从H桥到STM32的接线逻辑要驱动双路直流有刷电机首先得在电路层面选好驱动方案。很多新手直接把电机正负极接到单片机上一上电不是MOS管烧了就是芯片复位。有刷电机启动和堵转时电流可以达到额定电流的好几倍所以需要通过H桥来转接功率路径而不是让STM32直接承受大电流。2.1 H桥为什么是直流有刷电机的标准方案H桥由四个开关组成常见的是两个半桥驱动芯片加上内部MOS管也可以自己搭。四个开关成对导通使电流正向流过电机获得正转反向流过就是反转如果同时导通上桥臂或下桥臂电机被短接表现为刹车状态全关断就是滑行。对ST直流有刷电机来说正反转、刹车、滑行这几种状态都能在H桥上实现所以它成了最常用的驱动拓扑。选择具体驱动芯片时主要看工作电压、峰值电流和逻辑电平兼容性。L293D是很多老教材里的选择但它的饱和压降大1A负载下发热严重直接跑双路小车要加散热片。TB6612FNG的压降小很多逻辑电源和电机电源分开适合给小型有刷电机用。DRV8833这类芯片则把逻辑稳压也做进去适合电池供电的小型机器人。如果负载电流超过2A就不要用这些集成芯片改用分立MOS管搭H桥或者使用自带PWM接口的大电流驱动模块。在驱动双路直流有刷电机时同一个芯片里往往集成了两个H桥通道正好对应左右两个电机。比如TB6612有AIN1/AIN2/PWMA通道A和BIN1/BIN2/PWMB通道B这样能用一颗芯片完成双路独立驱动节省PCB面积逻辑引脚也比较好规划。2.2 用STM32的哪些引脚产生双路PWM双路驱动需要两路独立PWM以及每组H桥的方向、使能信号。最直接的方案是用两个通用定时器通道输出PWM。以STM32F103C8T6为例我一般优先用TIM3的CH1和CH2对应PA6和PA7两个引脚。这两个引脚在板子上容易引出而且TIM3挂在APB1上配置时不容易和调试接口冲突。方向信号用四个普通GPIO控制电机A接PIN1/PIN2电机B接PIN3/PIN4。下面是一份可直接照用的引脚分配表信号引脚功能电机A PWMPA6TIM3_CH1电机B PWMPA7TIM3_CH2电机A方向1PB0AIN1电机A方向2PB1AIN2电机B方向1PB10BIN1电机B方向2PB11BIN2电机电源地外部GND与STM32共地如果改用自己的板子只要保证两路PWM来自同一个定时器的不同通道或者来自不同定时器的通道占空比更新逻辑都成立。不建议用EXTI外部中断去翻转GPIO来模拟PWM那样频率和响应速度都达不到稳定的双路控制。2.3 供电、共地和去耦要注意什么驱动直流有刷电机时电机电源和逻辑电源一般分开但必须共地。如果两个电源之间没有地线连接H桥芯片的低端逻辑参考点和STM32的地不是同一个电位方向控制信号就会出现随机跳变严重时会烧毁逻辑侧端口。电机端需要并联一个大容量电解电容和一个小容量陶瓷电容放在电机驱动芯片的电源脚附近。比如使用100uF电解电容并联104瓷片电容用来吸收换向时产生的尖峰电压。另一个容易忽略的问题是PWM关断瞬间、电机电感会反向续流H桥芯片内部必须集成续流二极管或MOS管体二极管。如果自己搭分立H桥一定要加正向外部续流二极管到电机电源母线否则开关管很容易击穿。当电机功率较大时我还会在STM32供电入口和电机电源入口分别加磁珠或电感隔离避免电机电流突变拉低模拟电源。这样ADC采样电流时才不会看到一堆毛刺。3. 用STM32CubeMX配置定时器输出两路PWM并生成工程确定了接线方案后下一步是配置STM32的定时器。使用HAL库的环境下CubeMX做初始化可以省掉手写寄存器的一大堆时间。关键设置都在定时器参数里调不好即使代码正确电机也不会按预期转速转动。3.1 在CubeMX里选择定时器和通道在CubeMX中新建STM32F103C8T6工程先配置时钟树让APB1定时器时钟达到72MHz。然后在左边的芯片引脚图上点击PA6和PA7选中TIM3_CH1和TIM3_CH2。接着打开“Timers”里的TIM3将通道1和通道2都设为PWM Generation CHx。参数设置要注意直接决定PWM频率和占空比分辨率。为了让频率落在可听声范围之上又不至于让MOS开关损耗过大我一般选20kHz左右。用72MHz除以目标频率再拆分成预分频系数和自动重载值。参数值说明Prescaler (PSC)71时钟分频系数Counter Period (ARR)49自动重载值决定频率Pulse0初始占空比比较值ModePWM1占空比计数模式CH PolarityHigh高电平有效这里的计算结果为 72MHz / (711) / (491) 20kHzARR为49意味着占空比调节只有0到49共50个步进。对有刷电机速度控制来说够用因为电机本身有惯性相邻步进之间的转速差很小。如果项目需要更细腻的调速可以把PSC降为35ARR设为99频率依然约20kHz但分辨率提升到100级。3.2 在HAL库中启动PWM并改变占空比CubeMX生成的代码会调用MX_TIM3_Init初始化定时器但默认不会输出波形必须在main函数里显式启动PWM通道。启动代码非常简单HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_2);这两行放在用户代码区的“USER CODE BEGIN 2”之后。第一个参数指定定时器句柄htim3第二个参数指定要启动的通道。其中底层和占空比相关的寄存器是TIM3的CCR1和CCR2。启动之后如果直接测PA6引脚看到的是持续低电平因为pulse初始为0。要修改占空比可以直接操作定时器句柄里的比较寄存器常用的是内核提供的宏__HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, 25); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_2, 25);第一个宏设置通道1的CCR1为25第二个宏设置通道2的CCR2。注意这里的25不是百分比而是ARR长度49下的一半对应实际占空比约51%。如果要设置百分比需要先做一次换算duty (percent * ARR) / 100。这里还有一点要注意PWM启动后如果用HAL_TIM_PWM_Stop停止那么输出会回到空闲电平。此时如果H桥芯片还有使能电平两个方向引脚可能都是低导致电机处于滑行而不是刹车。所以逻辑上必须把停止和电机控制状态分开。3.3 方向控制与使能引脚的驱动代码方向控制用GPIO写电平的方式完成。以TB6612为例AIN1和AIN2组合决定电机状态AIN11、AIN20时正转反之为反转两个都是0则滑行两个都是1则刹车。把这部分逻辑封装成函数后续业务代码就不必关心具体引脚。下面是一份简化但能跑的双路电机驱动模块使用HAL库的GPIO接口typedef struct { TIM_HandleTypeDef *htim; uint32_t channel; GPIO_TypeDef *ain1_port; uint16_t ain1_pin; GPIO_TypeDef *ain2_port; uint16_t ain2_pin; } Motor_t; void motor_init(Motor_t *m, TIM_HandleTypeDef *htim, uint32_t ch, GPIO_TypeDef *p1, uint16_t pin1, GPIO_TypeDef *p2, uint16_t pin2) { m-htim htim; m-channel ch; m-ain1_port p1; m-ain1_pin pin1; m-ain2_port p2; m-ain2_pin pin2; } void motor_set_duty(Motor_t *m, int16_t speed) { uint16_t compare_value; if (speed 100) speed 100; if (speed -100) speed -100; if (speed 0) { HAL_GPIO_WritePin(m-ain1_port, m-ain1_pin, GPIO_PIN_SET); HAL_GPIO_WritePin(m-ain2_port, m-ain2_pin, GPIO_PIN_RESET); } else if (speed 0) { HAL_GPIO_WritePin(m-ain1_port, m-ain1_pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(m-ain2_port, m-ain2_pin, GPIO_PIN_SET); } else { HAL_GPIO_WritePin(m-ain1_port, m-ain1_pin, GPIO_PIN_SET); HAL_GPIO_WritePin(m-ain2_port, m-ain2_pin, GPIO_PIN_SET); __HAL_TIM_SET_COMPARE(m-htim, m-channel, 0); return; } compare_value (uint16_t)((int32_t)abs(speed) * 49 / 100); __HAL_TIM_SET_COMPARE(m-htim, m-channel, compare_value); }motor_set_duty接收一个带符号的整数正数表示正转负数表示反转0表示刹车。内部首先把输入限幅在-100到100之间然后根据正负写方向引脚最后用前面的交换公式设置定时器比较值。对于刹车状态我特意把方向引脚同时置高并让PWM输出低电平保证H桥上下桥臂不会同时导通同时让电机端子短路实现堵转制动。在使用时定义两个Motor_t实例分别用TIM3通道1、通道2初始化再在业务代码里调用motor_set_duty(motor_a, 60)即可。4. 双路电机控制的换向、刹车与速度匹配当两路电机都能独立转动后真正的控制开始变复杂。双路直流有刷电机通常需要同步正反转和停车比如小车直行时两轮同速转向时让两侧速度相反到达终点时又需要快速停车。这一章把这几种场景拆开讲清楚状态切换和PWM操作顺序。4.1 双路独立控制的最小状态机从底层看支撑双路控制只需要三个状态正转、反转、停止。但停止又要区分滑行和刹车所以至少四个状态。用枚举定义比较直观typedef enum { MOTOR_IDLE 0, MOTOR_FORWARD, MOTOR_BACKWARD, MOTOR_BRAKE } MotorState;每个状态对应一组GPIO输出和PWM行为。状态切换时最容易出问题的是从正转直接切到反转。如果不给PWM一个低电平窗口直接把方向引脚翻转电机电感会产生反向电动势电流尖峰可能烧毁驱动芯片。所以在换向逻辑上我一般会先调用一次刹车状态并等待几毫秒再进入反向状态。比如小车从前进切换到后退可以写成void motor_set_state(Motor_t *m, MotorState next_state) { switch (next_state) { case MOTOR_FORWARD: HAL_GPIO_WritePin(m-ain1_port, m-ain1_pin, GPIO_PIN_SET); HAL_GPIO_WritePin(m-ain2_port, m-ain2_pin, GPIO_PIN_RESET); break; case MOTOR_BACKWARD: HAL_GPIO_WritePin(m-ain1_port, m-ain1_pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(m-ain2_port, m-ain2_pin, GPIO_PIN_SET); break; case MOTOR_BRAKE: HAL_GPIO_WritePin(m-ain1_port, m-ain1_pin, GPIO_PIN_SET); HAL_GPIO_WritePin(m-ain2_port, m-ain2_pin, GPIO_PIN_SET); break; case MOTOR_IDLE: HAL_GPIO_WritePin(m-ain1_port, m-ain1_pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(m-ain2_port, m-ain2_pin, GPIO_PIN_RESET); break; } }HAL_GPIO_WritePin的第二个参数是GPIO_PIN_x代码里为了简化直接用GPIO_PIN_SET/RESET。注意AIN1和AIN2同时拉低对应的是滑行同时拉高对应刹车这个区别取决于驱动芯片手册。使用L293D或L298N时逻辑类似但要注意使能引脚EN需要单独置高否则PWM信号无法到达输出级。4.2 开环速度匹配让两个电机尽量跑成一条直线双路电机控制中最常见的问题是给一样的占空比两轮的转速却不一样。原因是直流有刷电机之间的绕组电阻、永磁体强度、摩擦力有差异没有闭环时开环控制必然会导致小车跑偏。如果项目没有编码器做PID闭环可以先在开环下做一次简单的速度匹配。方法是在地上画一条直线让两个电机同时以同一个占空比正转看小车偏向哪边。如果向左偏说明右侧电机更有力或负荷更小增大小车右侧电机的占空比或减小左侧。把校正系数存成一个浮点比例因子float left_ratio 1.0f; float right_ratio 1.0f; motor_set_duty(motor_a, (int16_t)(base_speed * left_ratio)); motor_set_duty(motor_b, (int16_t)(base_speed * right_ratio));在校正时base_speed取一个中等占空比比如50级中的35级。左右比值每次调0.05跑一次看一次。这个方法的缺点是负载变化后还需要重新校准但作为没有编码器情况下的临时方案已经够用。要彻底解决双路同步问题需要给每个电机添加增量式编码器用定时器编码器模式捕获脉冲数再用PID分别控制两侧转速。那种做法和伺服电机485控制的设计思路类似都是闭环系统工程量也会大很多适合在底盘项目后期升级。4.3 常见坑PWM频率、死区、电流采样PWM频率直接影响电机噪音和驱动芯片温升。频率太低会听到明显的“滋滋”声而且电流纹波大电机换向时火花也更多。频率太高会增加开关管损耗。TB6612这类芯片推荐跑到20kHz左右L298N内部的达林顿管开关速度慢我一般会把PWM降到10kHz以下避免管子还没完全导通就被强行关断。有刷电机的H桥不需要自己加死区因为集成驱动芯片已经在内部做了上下管开关延时。如果自己用两颗半桥芯片搭则要检查数据手册里的死区时间是否足够否则同一桥臂上下管直通电流短路烧管只是瞬间的事。电流采样是保护双路电机的关键手段。可以在电机电源正极串联低阻值采样电阻用运放放大后输入STM32的ADC引脚。也可以通过ACS712这类霍尔电流传感器直接输出模拟电压。采集到的电流值不要直接用于控制先做软件滤波比如连续采样8次取平均。下面这段代码演示了用ADC通道读取电流并判断是否超过堵转阈值uint32_t adc_value; adc_value HAL_ADC_GetValue(hadc1); if (adc_value OVER_CURRENT_THRESHOLD) { motor_set_duty(motor_a, 0); motor_set_duty(motor_b, 0); }OVER_CURRENT_THRESHOLD需要根据实际采样电阻和放大倍数标定。堵转时电流会快速上升用这种轮询判断基本够用。对于更复杂的任务可以使用ADC的注入通道配合定时器触发让采样时序和PWM周期同步减少开关噪声干扰。5. 验证脱机PWM输出和在线调试的实用技巧5.1 用逻辑分析仪确认PWM频率和占空比不要一上来就直接接电机调试先把驱动板上的PWM信号抓出来看。把逻辑分析仪的CH1接到PA6GND接到STM32的GND然后给系统上电。在逻辑分析仪软件里设置采样率不低于2MHz触发边沿选上升沿。启动程序后能看到一个连续方波测量其频率是否为20kHz高电平时间与周期的比值就是占空比。如果没有逻辑分析仪也可以在ARM仿真器里查看定时器寄存器。以STM32CubeIDE为例断开程序后打开Peripherals窗口在TIM3里找到CCR1寄存器。它显示的值和ARR寄存器相除就是当前占空比。这种方式不需要搭电路但看不到真实波形适合排查数值是否正确不适合验证GPIO输出时序。5.2 用ADC读电流给双路电机加堵转保护堵转保护是双路电机调试里值得优先实现的功能。先给两个电机各配一个电流采样引脚用上一章的滤波代码把电流值读出来。调试时把PWM输出到50%占空比慢慢用手捏住电机轴观察ADC值的变化曲线。当电流超过正常工作值的1.5倍时让两个电机立即进入刹车状态并置一个错误标志位。if (error_flag) { motor_set_duty(motor_a, 0); motor_set_duty(motor_b, 0); while (1) { // 等待操作者通过按键复位 } }这段代码的作用很简单发现堵转后立刻封锁PWM输出进入死循环等待人工复位。实际产品里不会用死循环会改为自动恢复尝试比如3秒后重新软启动但调试阶段还是死循环直观方便确认保护动作是否触发。注意电机断电后还要等驱动芯片放电完成不能立刻重新启动否则容易烧芯片。5.3 用一个往返测试脚本检验双路驱动可靠性最后分享一个调试双路直流有刷电机时常用的往返测试思路。把两个电机架起来或者放在小车底盘上让它们同时执行以下动作正转2秒停止0.5秒反转2秒停止0.5秒。重复执行20次同时监听电流和声音。如果中途某次出现停顿或者巨响说明H桥换向时序有问题。这个测试可以在主循环里用阻塞延时完成也可以把延时换成定时器状态机方便后续接入遥控或巡线逻辑。测试时重点观察电机从正转到反转的瞬间是否有火花、芯片是否烫手、电流是否尖峰。把脱机PWM测量和堵转保护结合起来双路驱动系统就基本可靠了。接下来你要做的是把这套驱动搬进自己的项目里用具体负载调一个合适的占空比范围然后让车真正跑起来。本文还有配套的精品资源点击获取
返回列表