4、GPIO控制电机:GPIO初始化、高低电平输出、控制电机正反转、软件延时实现PWM
各位同学,今天我们聊点最基础、也最实在的东西——怎么用GPIO把电机转起来。
你可能会想,GPIO不就是拉高拉低吗?有什么好讲的?
嗯,话是这么说。但我在项目里见过太多人,初始化没配好,电机抖两下就不动了。或者正反转逻辑写反,一上电窗帘就往反方向跑。这些小坑,踩一次就记住了。
4.1 GPIO初始化:别让电机“抖”起来
GPIO初始化,说白了就是告诉芯片:这个引脚要干嘛用。
控制电机,我们一般用推挽输出模式。为什么?因为推挽输出能提供足够的驱动电流,而且高低电平都很“干脆”。
我个人习惯,初始化时会把所有电机控制引脚先拉低。为什么?防止上电瞬间电机乱转。你想想看,如果默认是高电平,电机一上电就全速跑,那多吓人。
核心要点:
- 模式选择:推挽输出(Push-Pull)
- 初始电平:全部拉低
- 速度配置:如果是STM32,建议配成高速(50MHz)
- 别忘了使能GPIO时钟
代码长这样:
// 以STM32为例
void Motor_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
// 配置PB0, PB1为推挽输出
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_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// 初始全部拉低
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0 | GPIO_PIN_1, GPIO_PIN_RESET);
}
我的小经验:
我曾经在初始化时忘了使能时钟,结果GPIO死活拉不高。查了半天,最后发现是时钟没开。嗯,这种低级错误,犯一次就够了。
4.2 高低电平输出:让电机“听话”
初始化完了,就该让引脚输出高低电平了。
控制直流电机,其实就两个状态:正转、反转。怎么实现?用H桥电路。两个GPIO控制两个开关管,一个拉高、一个拉低,电流就流过去了。
代码很简单:
// 电机正转
#define MOTOR_FORWARD() do{ \
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); \
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); \
}while(0)
// 电机反转
#define MOTOR_BACKWARD() do{ \
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); \
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); \
}while(0)
// 电机停止
#define MOTOR_STOP() do{ \
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); \
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); \
}while(0)
注意:
千万不要同时拉高两个引脚!那相当于把电源和地短路了。我见过有人调试时手抖了一下,MOS管直接冒烟。嗯,那味道,记忆犹新。
4.3 控制电机正反转:逻辑要清晰
正反转的逻辑其实很简单:
| GPIO0 | GPIO1 | 电机状态 |
|---|---|---|
| 0 | 0 | 停止(刹车) |
| 1 | 0 | 正转 |
| 0 | 1 | 反转 |
| 1 | 1 | 禁止(短路) |
你可能会问:为什么停止是拉低两个引脚?
因为这样电机两端电压为0,靠惯性慢慢停。如果你想要急停,可以同时拉高两个引脚(但前提是你的H桥支持)。不过窗帘电机一般不需要急停,慢慢停反而更优雅。
4.4 软件延时实现PWM:没有定时器也能调速
好,现在电机能转了。但问题来了——怎么调速?
最直接的办法:用软件延时模拟PWM。
说白了,就是让引脚在“高”和“低”之间快速切换。高电平时间长,电机转得快;低电平时间长,电机转得慢。
代码示例:
// 软件PWM,占空比0-100
void Soft_PWM(uint8_t duty, uint32_t period_ms)
{
uint32_t high_time = period_ms * duty / 100;
uint32_t low_time = period_ms - high_time;
MOTOR_FORWARD(); // 拉高
delay_ms(high_time); // 延时高电平时间
MOTOR_STOP(); // 拉低
delay_ms(low_time); // 延时低电平时间
}
关键点:
- 周期不能太长,否则电机会一抖一抖的。我一般用10-20ms,也就是50-100Hz。
- 占空比不能太低,低于10%电机可能转不起来。因为启动需要一定的能量。
- 软件延时不精确,但窗帘电机对速度精度要求不高,够用了。
避坑指南:
我曾经用软件PWM控制窗帘电机,发现低速时电机嗡嗡响。后来一查,是PWM频率太低了,落在了人耳可听范围。把频率提到100Hz以上,声音就消失了。嗯,有时候问题就这么简单。
4.5 完整示例:让窗帘动起来
把上面所有东西串起来,就是一个完整的电机控制程序:
int main(void)
{
HAL_Init();
Motor_GPIO_Init();
while(1)
{
// 正转3秒,占空比80%
for(int i=0; i<150; i++)
{
Soft_PWM(80, 20); // 20ms周期,80%占空比
}
// 停止1秒
MOTOR_STOP();
delay_ms(1000);
// 反转3秒,占空比50%
for(int i=0; i<150; i++)
{
Soft_PWM(50, 20);
}
// 停止1秒
MOTOR_STOP();
delay_ms(1000);
}
}
你看,代码其实不多。但每一步都有讲究。
初始化要配好,电平要拉对,正反转逻辑不能错,PWM频率要合适。把这些基础打牢了,后面用定时器、用硬件PWM,都是锦上添花的事。
好,这一章就到这。下一章我们聊聊怎么用定时器中断来做更精确的PWM控制。到时候你会发现,软件延时虽然简单,但CPU被占着啥也干不了。嗯,那又是另一个故事了。