4、STM32 GPIO控制电机:寄存器配置、PWM生成、正反转与调速实现

各位同学,今天咱们来聊聊电机控制里最基础、也最核心的一块——GPIO控制电机

说实话,我刚开始做嵌入式那会儿,觉得控制电机不就是给个高电平让它转,给个低电平让它停吗?后来真正上手做项目才发现,这里面的门道可不少。尤其是当你需要让电机正转、反转、调速,还要保证系统稳定不抖的时候,GPIO的玩法就完全不一样了。

这一节,我会带着大家从寄存器配置讲起,再到PWM生成,最后实现完整的正反转与调速。咱们一步一个脚印,把这块硬骨头啃下来。

4.1 为什么GPIO能控制电机?

先回答一个最基础的问题:GPIO凭什么能驱动电机?

你想想看,STM32的GPIO引脚,输出电流一般也就几毫安到二十毫安。而一个直流电机,启动电流轻轻松松几百毫安,甚至几安。直接拿GPIO去推电机,结果只有一个——烧引脚

所以,GPIO控制电机的本质是:GPIO输出控制信号,驱动电路(如H桥)去控制电机。GPIO只负责发指令,不负责出力。

我在项目中遇到过一位新手,直接把电机正极接到PA0,负极接到GND,然后代码里写了个GPIO_SetBits……结果你猜怎么着?芯片冒烟了。嗯,从那以后我再也不敢小看驱动电路了。

4.2 寄存器配置:从底层理解GPIO

很多同学喜欢用HAL库,点几下鼠标就生成代码了。但我个人习惯,至少要把寄存器配置看一遍。为什么?因为只有理解了寄存器,你才能真正明白GPIO的每一个模式是干什么的。

以STM32F103为例,控制一个GPIO引脚,主要涉及以下几个寄存器:

寄存器 功能 关键位
GPIOx_CRL / CRH 配置引脚模式(输入/输出/复用)和速度 CNF[1:0], MODE[1:0]
GPIOx_ODR 输出数据寄存器,写1输出高,写0输出低 ODRy
GPIOx_BSRR 位设置/复位寄存器,原子操作,效率高 BSy, BRy
GPIOx_BRR 位复位寄存器,只复位不清零 BRy

举个例子,配置PA0为推挽输出,50MHz速度:

// 开启GPIOA时钟
RCC->APB2ENR |= (1 << 2);

// 配置PA0为推挽输出,50MHz
// CRL寄存器:每4位控制一个引脚,PA0对应bit[3:0]
// MODE0[1:0] = 11 (50MHz输出)
// CNF0[1:0] = 00 (推挽输出)
GPIOA->CRL &= ~(0xF << 0);   // 先清零
GPIOA->CRL |= (0x3 << 0);    // 设置MODE0=11, CNF0=00

// 输出高电平
GPIOA->BSRR = (1 << 0);      // 原子操作,设置PA0为高

这里有个小技巧:用BSRR而不是ODR来输出。BSRR是原子操作,不会被中断打断,而ODR的读-改-写操作可能会出问题。我在做电机控制时,曾经因为中断里修改了ODR,导致主循环里的输出被意外覆盖,电机抖得像筛糠一样。后来换成BSRR,问题立刻解决。

4.3 PWM生成:让电机学会“呼吸”

电机调速,说白了就是控制平均电压。怎么控制?PWM

PWM的原理很简单:在一个周期内,高电平占的比例越大,平均电压越高,电机转得越快。这个比例叫占空比

STM32生成PWM,通常用定时器。以TIM2的通道1为例,配置步骤如下:

  1. 开启定时器时钟:RCC->APB1ENR |= (1 << 0);
  2. 配置GPIO为复用功能:PA0作为TIM2_CH1,需要配置为复用推挽输出
  3. 设置定时器参数:预分频器、自动重装载值
  4. 配置PWM模式:设置CCR1为占空比,选择PWM1或PWM2模式
  5. 使能定时器:启动计数

代码示例:

// 1. 开启TIM2时钟
RCC->APB1ENR |= (1 << 0);

// 2. 配置PA0为复用推挽输出
GPIOA->CRL &= ~(0xF << 0);
GPIOA->CRL |= (0xB << 0);   // 1011: 复用推挽输出, 50MHz

// 3. 设置预分频器,72MHz / 72 = 1MHz
TIM2->PSC = 71;
// 自动重装载值,1MHz / 1000 = 1kHz PWM频率
TIM2->ARR = 999;

// 4. 配置PWM模式1,占空比初始为50%
TIM2->CCMR1 |= (6 << 4);    // OC1M = 110 (PWM模式1)
TIM2->CCMR1 |= (1 << 3);    // OC1PE = 1 (预装载使能)
TIM2->CCR1 = 500;            // 占空比 = 500/1000 = 50%
TIM2->CCER |= (1 << 0);     // CC1E = 1 (使能输出)

// 5. 使能定时器
TIM2->CR1 |= (1 << 0);      // CEN = 1

这里要注意:PWM频率的选择。频率太低,电机会发出“嗡嗡”声,甚至抖动;频率太高,驱动电路的开关损耗会增大。我个人经验,1kHz到20kHz是比较常用的范围。做风扇控制,我一般用1kHz;做精密位置控制,可能会用到10kHz以上。

4.4 正反转实现:H桥与GPIO的配合

电机正反转,靠的是改变电流方向。最常用的电路是H桥

一个典型的H桥有4个开关管(Q1~Q4),两个输入端(IN1, IN2)。控制逻辑如下:

IN1 IN2 电机状态
1 0 正转
0 1 反转
0 0 停止(刹车)
1 1 禁止(短路)

用GPIO控制H桥,其实就是控制IN1和IN2的电平。配合PWM,还能实现带调速的正反转

举个例子,假设我们用PA0控制IN1,PA1控制IN2,TIM2_CH1的PWM输出接到使能端ENA:

// 正转:IN1=1, IN2=0, ENA=PWM
GPIOA->BSRR = (1 << 0);      // PA0 = 1
GPIOA->BRR  = (1 << 1);      // PA1 = 0
TIM2->CCR1 = speed;           // 设置占空比

// 反转:IN1=0, IN2=1, ENA=PWM
GPIOA->BRR  = (1 << 0);      // PA0 = 0
GPIOA->BSRR = (1 << 1);      // PA1 = 1
TIM2->CCR1 = speed;

// 停止:IN1=0, IN2=0, ENA=0
GPIOA->BRR  = (1 << 0);
GPIOA->BRR  = (1 << 1);
TIM2->CCR1 = 0;

我曾经犯过一个低级错误:在切换正反转时,没有先让电机停止,而是直接切换IN1和IN2。结果H桥的上下管瞬间导通,电流大得吓人,驱动芯片直接烧了。所以,切换方向前,一定要先刹车

4.5 调速实现:从开环到闭环

上面讲的PWM调速,其实是开环控制。你给50%占空比,电机不一定转50%的速度,因为负载、电压波动都会影响。

要想精确调速,需要闭环控制。最常用的方法是PID控制,但那是后面章节的内容。这一节,我们先掌握开环调速的基本方法。

调速的核心就是改变占空比。但要注意:

  • 占空比不能从0直接跳到100%,否则电机会“突跳”,机械冲击很大。我一般会做一个软启动,让占空比逐渐增加。
  • 占空比太低时,电机可能不转。因为启动转矩不够。这时候需要给一个“启动脉冲”,然后再降到目标占空比。

软启动代码示例:

void motor_soft_start(uint16_t target_speed, uint16_t step_ms) {
    uint16_t current_speed = 0;
    while (current_speed < target_speed) {
        current_speed += 10;   // 每次增加10
        if (current_speed > target_speed) {
            current_speed = target_speed;
        }
        TIM2->CCR1 = current_speed;
        delay_ms(step_ms);     // 每步延时
    }
}

嗯,这里要注意:step_ms不能太小,否则软启动效果不明显;也不能太大,否则启动太慢。我一般取10~50ms,具体看电机惯性和负载。

4.6 避坑指南:我踩过的那些雷

做电机控制这么多年,踩过的坑真不少。挑几个典型的跟大家说说:

  • 电源不够:电机启动瞬间电流很大,如果电源功率不足,会导致电压跌落,STM32复位。我遇到过好几次,后来养成了习惯——电机和MCU分开供电,共地即可。
  • PWM频率选错:有一次做小风扇,PWM频率设成了100Hz,结果风扇“嗡嗡”响,噪音大得离谱。后来改成25kHz,世界安静了。
  • 死区时间:H桥的上下管切换时,如果同时导通,会短路。所以一定要加死区时间。STM32的定时器有死区插入功能,记得用上。
  • GPIO初始化顺序:先配置GPIO模式,再配置定时器,最后使能输出。顺序错了,可能会在初始化瞬间产生一个错误电平,导致电机抖动一下。

4.7 本章小结

这一节,我们从GPIO的寄存器配置讲起,到PWM生成,再到正反转和调速的实现。说白了,电机控制的核心就是三件事:发信号(GPIO)、调电压(PWM)、换方向(H桥)

我个人觉得,学电机控制,动手比看书重要得多。你拿一块开发板,接一个L298N驱动模块,再连一个小电机,照着上面的代码跑一遍,比看十遍书都管用。

下一节,我们会把传感器加进来,让电机根据环境变化自动调整。嗯,那才是真正好玩的地方。

核心要点回顾:

  • GPIO不能直接驱动电机,必须通过驱动电路(如H桥)
  • 用BSRR/BRR代替ODR,避免中断干扰
  • PWM频率选择:1kHz~20kHz,根据应用场景调整
  • 正反转切换前,先刹车,防止短路
  • 软启动可以减小机械冲击,延长电机寿命

小技巧:调试电机时,先用一个LED代替电机,观察PWM波形是否正确。确认无误后再接电机,可以避免烧驱动芯片。

警告:千万不要在电机运行时用手去摸驱动芯片!H桥工作时温度很高,小心烫伤。我有个同事就吃过这个亏……

STM32 GPIO控制电机知识体系 STM32 微控制器 GPIO 寄存器配置 定时器 PWM 生成 H桥 正反转控制 电机输出:正转 / 反转 / 调速 / 停止 频率:1kHz~20kHz 占空比:0%~100% 核心:GPIO发信号 → 定时器调电压 → H桥换方向

公众号:蓝海资料掘金营,微信deep3321