3. GPIO基础与电机驱动:GPIO配置、H桥电路原理、L298N驱动直流电机

各位同学,今天我们来聊聊电机控制里最基础、也最绕不开的一环——GPIO和H桥。

说实话,很多新手一上来就盯着PID算法、FOC控制这些高大上的东西。但我见过太多项目,最后卡在最简单的电机转不转、转反了、或者烧了驱动芯片这种问题上。嗯,咱们先把地基打牢。

3.1 GPIO配置——别小看这玩意儿

GPIO,全称General Purpose Input Output,通用输入输出口。说白了,就是芯片的“手脚”。

在STM32里,每个GPIO引脚可以配置成多种模式。驱动电机时,我们最常用的是推挽输出模式。

推挽输出 vs 开漏输出

推挽输出:既能输出高电平,也能输出低电平。驱动能力强,适合直接控制逻辑电平。

开漏输出:只能输出低电平,高电平要靠外部上拉电阻。一般用于I2C总线。

驱动电机驱动芯片(比如L298N),请用推挽输出。

我个人习惯,在初始化GPIO时,会顺带把时钟使能、模式、速度、上下拉全部配好。别偷懒,少配一个都可能出问题。

// 以STM32F103为例,配置PA0和PA1为推挽输出
void Motor_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    __HAL_RCC_GPIOA_CLK_ENABLE();   // 使能GPIOA时钟
    
    GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;  // 推挽输出
    GPIO_InitStruct.Pull = GPIO_NOPULL;          // 无上下拉
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速即可
    
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

小技巧:电机控制用的GPIO,速度不用设太高。设成LOW就够了。设高了反而容易引入噪声干扰。我在一个项目中吃过这个亏,GPIO速度设成HIGH,结果电机一启动,旁边的传感器数据就乱跳。

3.2 H桥电路原理——电机正反转的秘密

直流电机怎么让它正转、反转?答案就是H桥。

H桥这个名字,来源于它的电路形状像字母“H”。四个开关管(通常是MOS管或三极管)组成桥臂,电机接在中间。

你想想看:

  • 左上和右下导通:电流从左往右流过电机 → 正转
  • 右上和左下导通:电流从右往左流过电机 → 反转
  • 同一侧两个同时导通:短路!烧管子!

致命禁忌:绝对不能让同一侧的上下两个开关同时导通!这叫“直通”,瞬间电流会大到烧毁MOS管。我曾经亲眼见过一个实习生,代码里延时写错了,H桥直通,MOS管直接冒烟。嗯,那味道很难忘。

下面我用一张SVG图,把H桥的核心逻辑画出来。你看一眼就明白了。

H桥电路原理图 Q1 Q2 Q3 Q4 直流 电机 VCC (+12V) GND 正转电流方向: Q1→电机→Q4 Q1导通 Q2关断 Q3关断 Q4导通

正转时,Q1和Q4导通,电流从VCC→Q1→电机→Q4→GND。反转时,Q2和Q3导通,电流反向流过电机。

3.3 L298N驱动直流电机——实战来了

自己搭H桥?可以,但没必要。市面上有现成的驱动芯片,L298N就是最经典的一款。

L298N内部集成了两个H桥,可以同时驱动两个直流电机。它需要:

  • 一个逻辑电源(5V),给芯片内部逻辑供电
  • 一个电机电源(6V-12V),给电机供电
  • 四个控制引脚(IN1、IN2、IN3、IN4),控制两个电机的正反转
  • 两个使能引脚(ENA、ENB),用PWM控制电机速度
IN1 IN2 电机A状态
0 0 刹车(停止)
0 1 反转
1 0 正转
1 1 刹车(停止)

你看,控制逻辑其实很简单。两个引脚的电平组合,决定了电机的转向。

核心要点:使能引脚ENA/ENB接PWM,可以调速。占空比越大,电机转得越快。我一般用定时器的PWM输出,频率设在1kHz左右。太低了电机会嗡嗡响,太高了驱动芯片反应不过来。

下面是一个完整的驱动代码示例。我习惯把电机控制封装成一个结构体,这样代码清晰,也方便移植。

// 电机控制结构体
typedef struct {
    GPIO_TypeDef* IN1_Port;
    uint16_t      IN1_Pin;
    GPIO_TypeDef* IN2_Port;
    uint16_t      IN2_Pin;
    TIM_HandleTypeDef* htim;
    uint32_t      Channel;
} Motor_HandleTypeDef;

// 初始化电机
void Motor_Init(Motor_HandleTypeDef* hmotor)
{
    // GPIO初始化(略,参考前面代码)
    // PWM初始化(略)
    
    // 初始状态:停止
    HAL_GPIO_WritePin(hmotor->IN1_Port, hmotor->IN1_Pin, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(hmotor->IN2_Port, hmotor->IN2_Pin, GPIO_PIN_RESET);
}

// 设置电机方向和速度
void Motor_Set(Motor_HandleTypeDef* hmotor, int8_t direction, uint16_t speed)
{
    if(direction > 0)  // 正转
    {
        HAL_GPIO_WritePin(hmotor->IN1_Port, hmotor->IN1_Pin, GPIO_PIN_SET);
        HAL_GPIO_WritePin(hmotor->IN2_Port, hmotor->IN2_Pin, GPIO_PIN_RESET);
    }
    else if(direction < 0)  // 反转
    {
        HAL_GPIO_WritePin(hmotor->IN1_Port, hmotor->IN1_Pin, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(hmotor->IN2_Port, hmotor->IN2_Pin, GPIO_PIN_SET);
    }
    else  // 停止
    {
        HAL_GPIO_WritePin(hmotor->IN1_Port, hmotor->IN1_Pin, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(hmotor->IN2_Port, hmotor->IN2_Pin, GPIO_PIN_RESET);
    }
    
    // 设置PWM占空比
    __HAL_TIM_SET_COMPARE(hmotor->htim, hmotor->Channel, speed);
}

避坑指南:我曾经在调试一个AGV小车时,电机怎么都不转。查了半天,发现是L298N的5V逻辑电源没接。记住,L298N需要两路电源:一路给电机(VMS),一路给逻辑(VSS)。逻辑电源不接,芯片不工作。

还有一个常见问题:电机启动瞬间电流很大。L298N的峰值电流是2A,但持续电流只有1A左右。如果你的电机堵转或者启动电流超过这个值,芯片会过热保护,甚至烧毁。

我的建议:在电机电源线上并联一个大电容(470μF以上),可以吸收启动瞬间的尖峰电流。另外,L298N背面一定要加散热片。别问我怎么知道的——我烧过三块L298N才长记性。

好了,这一章的内容就到这里。GPIO配置、H桥原理、L298N驱动,这三块是电机控制的入门砖。你把这些吃透了,后面学PWM调速、编码器反馈、PID控制,都会轻松很多。


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