拓冰建站拓冰建站
首页 / 资讯中心 / 正文

STM32直流有刷电机驱动实战:PWM调速、H桥正反转与过流保护

简介面向嵌入式开发者和STM32学习者的直流有刷电机驱动工程围绕单路电机控制完整呈现从原理到落地的实现路径适合课程设计、竞赛开发或工业控制入门参考。压缩包共236个文件以C源文件与H头文件为主包含定时器PWM配置、HAL库驱动、主程序与中断处理逻辑同时附有Keil工程文件uvprojx/uvoptx、调试配置scvd以及hex/bin固件镜像整体仅1.67MB便于直接导入工程或烧录验证。内容覆盖直流有刷电机换向原理、STM32定时器与GPIO接口配置、单极性与双极性驱动策略、过流/过热/短路保护机制以及基于编码器的闭环调速思路并基于STM32F4 HAL库降低底层寄存器操作难度。已有321人学习下载可对照源码理解每个配置项理清PWM占空比与转速、方向的关系并在此基础上扩展双路驱动或多电机协调控制。1. 直流有刷电机驱动不是接上电源就转手头有一套正点原子 ATK-F407 的 HAL 库工程里面带着 stm32f4xx_hal_tim.c 和完整的定时器驱动代码稍微改一改就能把单路直流有刷电机跑起来。直流有刷电机看似简单两根线一接就转但真要控制转速和方向核心就两件事用 PWM 调节平均电压调速用 H 桥切换电枢电流方向换向。STM32 只负责输出逻辑信号真正驱动电机的是功率电路。这套工程适合刚接触电机控制的嵌入式开发者也适合做小车底盘、云台、小型传送带的工程师。本文从硬件拓扑讲起把定时器 PWM 的每个寄存器参数算清楚再落到过流保护和调试排错全程可复现。2. 硬件接口与驱动拓扑为什么 MCU 不能直连电机2.1 直流有刷电机的工作特性直流有刷电机的电枢绕组经换向器和电刷引入电流换向器随转子旋转保证绕组在磁场中持续产生同方向的转矩。堵转启动时电流可达额定电流的 5 到 8 倍所以驱动电路必须能承受瞬间大电流。MCU 的 GPIO 输出能力通常在 8mA 到 25mA 之间而一枚小型 370 电机在 6V 电压下启动电流就可能到 1A 以上直接用 GPIO 驱动会烧毁引脚这就是必须引入功率放大电路的原因。电机两端的电压决定了转速电压方向决定了旋转方向。要实现对这两个变量的控制最常见的就是 H 桥拓扑四个开关管组成桥臂对角导通时电流正向流过电机换一组对角导通则电流反向。只要让某一对开关管以 PWM 频率高速通断就能在改变平均电压的同时保持电流方向。2.2 STM32 与驱动芯片的接口设计工程中使用 STM32F407 作为主控外接驱动芯片方案比用分立 MOS 管搭 H 桥更稳妥。L298N 是经典的 H 桥驱动芯片但压降偏高且发热大TB6612FNG 最大连续电流 1.2A峰值 3.2A体积小适合驱动小型有刷电机。如果电机功率更大可以换用 BTN7971 或 DRV8701 加外部 MOS 管的方案。选择驱动芯片的参考依据是电机额定电压、堵转电流和控制频率。接口设计上STM32 输出三路信号给驱动芯片一路 PWM 调速两路方向控制。以 TB6612 为例A01/A02 接电机的两根线PWMA 接 STM32 的定时器通道输出AIN1 和 AIN2 接普通 GPIO 控制方向。当 AIN11、AIN20 时电流从 A01 流向 A02电机正转反之间断换向则反转。如果 AIN1 和 AIN2 电平相同电机进入制动状态。以下是我常用的一组引脚分配改动工程时直接对应修改功能引脚定时器配置说明PWM 输出PA6TIM3_CH1推挽复用输出10kHz方向控制 1PB0GPIO推挽输出控制驱动芯片 AIN1方向控制 2PB1GPIO推挽输出控制驱动芯片 AIN2电流采样PA3ADC1_IN3采样运放输出可省编码器 A/BPC6/PC7TIM8_CH1/CH2需要闭环时启用接线时注意把驱动芯片的 GND 与 STM32 的 GND 共地否则 PWM 信号没有参考电平电机无法控制这是新手最容易忽略的问题。驱动芯片的 VM 接电机电源VCC 接 3.3V 或 5V两者不能接反。2.3 电源与地回路直流电机启动和换向瞬间会在电源线上产生较大纹波如果主控和驱动共用同一个电源容易导致 MCU 复位或 ADC 采样跳动。常见做法是用双电源隔离STM32 用 USB 或稳压芯片供电电机用独立的 6V 或 12V 电源。如果只能共用电源则在电机电源输入端并联一个大容量的电解电容470uF 起步并在靠近驱动芯片的位置加 0.1uF 陶瓷电容滤高频。提示电机电源负极与逻辑电源负极必须连在一起但正极分开。单点共地能避免地环路形成干扰。3. HAL 库定时器 PWM 配置从寄存器到代码3.1 定时器时钟树与 PWM 频率计算STM32F407 的高级定时器 TIM1/TIM8 挂在 APB2 总线上时钟可达 168MHz通用定时器 TIM2 到 TIM5 挂在 APB1 总线上时钟为 84MHz。关键点是定时器时钟并不等于 APB 总线的最大频率当 APB1 预分频系数大于 1 时定时器时钟频率是 APB1 频率的两倍。正点原子 F407 的 system_clock_64 初始化会把 APB1 设为 42MHz此时 TIM2 到 TIM5 的实际时钟是 84MHz。PWM 频率由预分频器 PSC 和自动重载值 ARR 共同决定公式为PWM频率 定时器时钟 / (PSC 1) / (ARR 1)占空比由比较寄存器 CCR 决定CCR 的取值范围是 0 到 ARRCCR 为 0 时输出低电平CCR 等于 ARR 时输出全高。电机驱动常用的 PWM 频率在 10kHz 到 20kHz 之间太低会产生可闻噪声太高的开关损耗会明显增加而且驱动芯片的栅极驱动能力未必跟得上。以 TIM3 输出 10kHz、10 位精度为例参数计算如下定时器时钟 84MHz PSC 84 - 1 83 → 计数器时钟 1MHz ARR 100 - 1 99 → PWM 频率 1MHz / 100 10kHz 占空比分辨率 1/100 1%如果希望占空比调节更细腻可以把 PSC 设为 83、ARR 设为 999PWM 频率变成 1kHz分辨率提升到 0.1%但 1kHz 频率会让电机产生明显的啸叫。折中方案是保持 20kHz 频率和 8 位精度也就是 PSC 设为 83、ARR 设为 249。3.2 GPIO 复用配置在 HAL 库中PWM 引脚的配置分为 GPIO 初始化和定时器初始化两步。GPIO 要配置为复用推挽输出模式并指定复用功能号。TIM3 的通道 1 映射到 PA6复用功能号为 AF2。以下是 MX_GPIO_Init 中的关键片段void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /* 方向控制引脚普通推挽输出 */ GPIO_InitStruct.Pin GPIO_PIN_0 | GPIO_PIN_1; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull GPIO_PULLUP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, GPIO_InitStruct); /* PWM 输出引脚复用推挽复用功能 AF2 */ GPIO_InitStruct.Pin GPIO_PIN_6; GPIO_InitStruct.Mode GPIO_MODE_AF_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate GPIO_AF2_TIM3; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); }这里的 Pull 设置为 GPIO_PULLUP 是为了保证方向引脚在上电瞬间保持确定电平避免驱动芯片误动作导致电机突然转动。Speed 选 HIGH 用于 PWM 引脚确保 PWM 信号的上升沿和下降沿足够陡峭减少开关损耗方向引脚选 LOW 足够因为方向切换频率很低。3.3 定时器参数初始化与占空比控制定时器初始化在 MX_TIM3_Init 中完成使用 HAL_TIM_PWM_Init 和 HAL_TIM_PWM_Start 配合启动。以下代码在 F407 上验证过可直接抄入工程void MX_TIM3_Init(void) { TIM_OC_InitTypeDef sConfigOC {0}; htim3.Instance TIM3; htim3.Init.Prescaler 84 - 1; /* PSC 8384MHz 分频到 1MHz */ htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 100 - 1; /* ARR 99PWM 频率 10kHz */ htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim3.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_ENABLE; HAL_TIM_PWM_Init(htim3); sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 0; /* CCR 初始值即初始占空比 */ sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim3, sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); }CounterMode 选择向上计数模式输出波形在计数值小于 CCR 时为高电平、大于 CCR 时为低电平这就是 PWM1 模式。AutoReloadPreload 必须使能否则 ARR 更新时可能立刻生效导致输出一个异常宽度的脉冲。Pulse 初始设为 0是为了上电后电机不转等主程序初始化完成后再开始调速。运行期修改占空比用宏不需要重新调用 Init 函数__HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, speed); /* speed: 0~99 */speed 就是 CCR 的值与 arr 的比值即占空比。建议在代码中定义#define PWM_PERIOD 100 这类常量避免魔法数字出现在业务逻辑里。需要改变 PWM 频率时直接改 Prescaler 和 Period 这两个参数即可前提是 GPIO 复用配置不变。4. 正反转、刹车与过流保护控制逻辑的完整闭环4.1 方向与刹车控制逻辑有了 PWM 通道方向控制和刹车还是需要单独实现的。驱动芯片的输入信号与电机状态的关系如表所示这套逻辑与 L298N、TB6612、DRV8833 都兼容AIN1AIN2电机状态适用场景00惰性滑行coast断电手动推转省电11刹车brake急停电机端短路10正向转动前进PWM 控制速度01反向转动倒退PWM 控制速度惰性和刹车是两种完全不同的停止方式。惰性时电机两端悬空转子靠机械阻力缓慢停转刹车时电机两端短路电枢产生的反电动势形成环流制动力矩大停车距离短。实际项目中启停频繁的机构建议用刹车模式。对应 STM32 的控制代码封装一个速度控制函数方向由符号决定void Motor_Control(int16_t speed) { if (speed PWM_PERIOD) speed PWM_PERIOD; if (speed -PWM_PERIOD) speed -PWM_PERIOD; if (speed 0) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); /* AIN11 */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); /* AIN20 */ __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, speed); } else if (speed 0) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); /* AIN10 */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); /* AIN21 */ __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, -speed); } else { /* speed 0 时切刹车而不是切惰性 */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, 0); } }函数内部先对 speed 做范围钳制防止传入超界值导致 PWM 占空比超过 ARR。正值走正转分支、负值走反转分支、零值切刹车注意这里 speed 为零时没有把 PWM 置为 0 后让电机惰性转动而是切刹车因为工业场景下停止后不允许电机被外力拖动。4.2 过流保护ADC 采样与快速关断电机堵转或负载异常时电流会急剧上升仅靠驱动芯片的过流保护往往不够及时因为芯片保护阈值偏高。更可靠的做法是在电机供电回路串一个采样电阻把电流信号放大后送入 STM32 的 ADC由程序判断是否过流。采样电阻选 0.05 欧姆流过 2A 电流时压降只有 0.1V需要用差分运放放大后再进 ADC。常见的低成本运放方案是 INA240增益设为 20 倍0.1V 放大到 2V适配 STM32 的 3.3V 参考电压。如果不需要精确电流值也可以直接用电阻分压后接 ADC但精度会受 PWM 开关噪声影响。ADC 采样要注意同步问题。电机电流在 PWM 高电平期间流过电机低电平期间通过续流二极管回流如果随意时刻采样读到的电流值波动很大。常见做法是把 ADC 触发信号连接到定时器的更新事件让 ADC 恰好在一个 PWM 周期的固定时刻采样这样每次读到的是同一相位点的电流可比性更强。保护逻辑写在中断里电流超过阈值就立刻停 PWMvoid HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) { uint16_t adc_val HAL_ADC_GetValue(hadc1); /* 假设满量程 4096 对应 3.3V运放增益 20采样电阻 0.05 欧姆 */ float current (adc_val * 3.3f / 4096.0f) / 20.0f / 0.05f; if (current 2.0f) /* 堵转电流阈值 2A */ { HAL_TIM_PWM_Stop(htim3, TIM_CHANNEL_1); Motor_Control(0); /* 切刹车并置零占空比 */ Error_Flag 1; } }回调函数中的 current 计算逻辑是ADC 原始值先换算成电压再除以运放增益得到采样电阻两端电压最后除以电阻值得到电流。阈值 2A 需要根据实际电机的额定电流调整不能盲目套用。另外要注意 HAL_ADC_Start_IT 的调用位置建议放在电机启动之后、主循环之前保证 ADC 一直处于采集状态。4.3 软启动与堵转判断直接给满占空比启动电机会产生很大的冲击电流对齿轮箱和驱动芯片都不友好。软启动的思路是让占空比按斜坡从零逐渐爬升爬升时间一般设为 300ms 到 1s。void Motor_SoftStart(int16_t target, uint16_t ramp_ms) { int16_t cur 0; uint16_t step_delay ramp_ms / PWM_PERIOD; while (cur ! target) { if (cur target) cur; else cur--; Motor_Control(cur); HAL_Delay(step_delay); } }step_delay 是每增加 1% 占空比的间隔时间。比如 target80、ramp_ms400那么 step_delay 5ms整个加速过程持续 400ms。这个函数修改方向时也存在从正转直接切反转的隐患调用前先让 motor 回到零位并延时 50ms 以上避免电流突变。堵转判断则利用电流检测结果或编码器速度反馈若 PWM 输出不为零但 500ms 内编码器脉冲数没有增加判定为堵转执行关断。没有编码器时可以只靠电流阈值判断但响应速度会比较慢因为电机过流需要时间累积。5. 调试验证与编码器测速波形、频率与闭环修正5.1 用逻辑分析仪验证 PWM 输出手边没有示波器时逻辑分析仪是检查 PWM 波形的最低成本工具。把 PA6 接到逻辑分析仪的通道上设置采样率不低于 10MHz抓取一段运行中的 PWM 信号。重点检查三点频率是否等于计算值 10kHz、占空比是否随着 speed 参数的修改线性变化、电机启动瞬间是否有异常的宽脉冲。频率偏差通常说明 PSC 或 ARR 算错或者定时器时钟频率与预期不符。异常宽脉冲说明 AutoReloadPreload 没有使能ARR 在更新途中被修改过。占空比不线性变化则要检查 CCR 是否被意外截断比如 speed 大于 ARR 时PWM 会一直输出高电平电机全速运转这时候看波形就是一条直线。5.2 PWM 频率选择的实测经验不同电机对 PWM 频率的响应差异非常明显。实测中7V 空载转速 12000rpm 的 720 空心杯电机在 1kHz PWM 下噪声很大转速波动明显提升到 20kHz 后噪声基本消失。但对于大惯量减速电机20kHz 反而可能出现低速段脉动因为电枢电感大、电流纹波小转矩波动被机械滤波掉了可以降低到 10kHz 试验。一个值得注意的边界条件PWM 频率越高驱动芯片的开关损耗越大芯片温升越明显。TB6612 在 12V 电源、2A 电流、20kHz 下连续运行 10 分钟外壳温度可能到 60 到 70 度这时需要降频或加强散热。5.3 编码器测速与速度闭环要进一步提高速度控制精度可以加一个霍尔编码器把电机转速反馈给 STM32。F407 的定时器支持编码器模式PC6 和 PC7 分别接编码器的 A、B 相输出硬件自动完成正交解码不需要在中断里手动数脉冲。void MX_TIM8_Encoder_Init(void) { htim8.Instance TIM8; htim8.Init.Prescaler 0; htim8.Init.CounterMode TIM_COUNTERMODE_UP; htim8.Init.Period 0xFFFF; HAL_TIM_Encoder_Init(htim8, sConfig); HAL_TIM_Encoder_Start(htim8, TIM_CHANNEL_ALL); }编码器模式下定时器工作在外部时钟模式Prescaler 应设为 0Period 设为 0xFFFF 让计数器自由计数。读取速度时先取计数器值计算与上一次读取值的差值再除以时间间隔就得到每秒的脉冲数。例如电机每转输出 13 个脉冲1 秒内计数增长了 260则转速为 260 / 13 20 转每秒。把误差作为 PID 控制器的输入输出作为 PWM 占空比就能实现恒速控制。这部分代码在中断里以固定周期执行控制周期通常取 10ms。如果出现电机低速段抖动多半是 PID 的积分项过强解决办法是只在误差小于一定范围内才启用积分。调试时先用开环让电机稳定转动记录不同占空比下的实际转速曲线再据此设定 PID 初值能省掉大半试凑时间。整个驱动链路从 PWM 输出到电流保护再到编码器反馈层层验证下来系统才能可靠地运行在无人值守的场景里。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门