L9110S电机驱动模块实战指南:从原理到智能小车应用
1. 从零认识L9110S:你的第一个电机“指挥官”
如果你玩过智能小车或者机器人,肯定对电机不陌生。但光有电机可不行,它就像一匹野马,你得有个“缰绳”和“马鞭”来控制它跑多快、往哪跑。L9110S电机驱动模块,就是这样一个给直流电机准备的“智能缰绳”。我自己在带学生做智能车项目时,L9110S几乎是入门必选的模块,因为它实在太“友好”了——价格便宜、接线简单、原理直观,特别适合新手来建立对电机控制的第一个感性认识。
简单来说,单片机(比如STM32、Arduino)的GPIO引脚输出能力很弱,电流只有几十毫安,根本带不动需要几百毫安甚至上安培电流的电机。强行连接,要么电机纹丝不动,要么单片机直接烧掉。L9110S模块的作用,就是充当一个“电流放大器”和“开关指挥官”。它接收来自单片机微弱的控制信号(比如“正转”、“反转”、“停”),然后用自己的电路,从外部电源(比如电池)取电,输出足够强的电流去驱动电机执行动作。你可以把它想象成一个听令于单片机(大脑)的强力执行官(肌肉)。
市面上常见的L9110S模块,通常集成了两颗L9110S芯片,所以能独立控制两个直流电机,或者一个四线两相步进电机。模块板子很小巧,比一枚硬币大不了多少,上面有绿色的接线端子,用杜邦线一插就行,非常方便。它的工作电压范围是2.5V到12V,这意味着你可以用3.7V的锂电池、7.4V的航模电池或者9V的方块电池给它供电,驱动相应电压的小型电机。不过要注意,它的持续输出电流能力大约在800mA左右,所以适合驱动那种小型TT马达(就是黄色齿轮箱那种)、小风扇或者小型水泵,驱动大功率的电机就得换更猛的驱动芯片了。
我第一次用L9110S的时候,就被它的简洁逻辑吸引了。控制一个电机,只需要两个信号引脚:IA和IB。它的真值表特别好记,我总结成一个口诀:“同高同低都不转,一高一低才能转;IA高来IB低,电机正转跑得急;IA低来IB高,反转方向就调好”。也就是说,给IA高电平(3.3V或5V)、IB低电平(0V),电机正转;反过来,IA低、IB高,电机就反转;如果IA和IB都是高或者都是低,电机就刹车停止。这种逻辑清晰明了,编程时用digitalWrite或者HAL_GPIO_WritePin函数设置两个引脚电平就能轻松搞定方向,完全不需要复杂的时序。
2. 核心原理拆解:PWM如何让电机“听话”地变速
搞定了电机的正反转,下一个问题就是速度。总不能让小车只有“狂奔”和“停止”两种状态吧?我们得让它能慢速巡逻、中速巡航、高速冲刺。这就轮到PWM(脉冲宽度调制)技术大显身手了。很多新手一听PWM就觉得头大,其实它的生活类比非常形象:你想想家里用的调光台灯,或者风扇的调速开关。它们并不是真的去改变供电电压的大小,而是通过快速开关电路,控制“开”的时间和“关”的时间的比例。如果一秒钟内,灯亮的时间占0.8秒,灭的时间占0.2秒,你就会觉得灯很亮;如果亮0.3秒、灭0.7秒,灯就显得很暗。虽然开关在快速动作,但由于人眼的视觉暂留,我们看到的是连续变暗的效果。
PWM控制电机速度,原理一模一样。我们不是去调节电机的平均电压(虽然效果等价),而是用一系列频率很高的方波脉冲去驱动它。脉冲“高电平”的时候,电机通电转动;“低电平”的时候,电机靠惯性滑行。如果脉冲频率足够高(比如几千赫兹以上),电机由于自身的机械惯性,就感觉不到这种微小的启停抖动,只会表现为平滑的转动,而转速则由高电平所占的时间比例决定。这个“高电平时间占整个周期的比例”,就是我们常说的“占空比”。占空比0%意味着一直没电,电机不转;占空比100%意味着一直供电,电机全速转动;占空比50%则是一半时间供电,电机以大约一半的速度旋转。
那么,这个快速开关的动作谁来执行呢?就是我们前面提到的L9110S。但这里有个关键:L9110S本身只是一个“受控开关”,它自己不会产生PWM波。产生PWM波是单片机定时器的任务。我们需要在单片机上配置一个定时器,让它工作在PWM输出模式,然后把产生PWM波的引脚连接到L9110S的IA或IB脚上(通常我们固定一个方向引脚的电平,用PWM控制另一个引脚)。举个例子,我们想让电机正转并调速:可以设置IA引脚为固定的高电平,然后将单片机生成的PWM波输出到IB引脚。当PWM波为高电平时,IA高、IB高,电机两端电压差为0,不转;当PWM波为低电平时,IA高、IB低,电机正转。你看,这样IB引脚上的PWM波就反过来控制了电机通电的时间比例,实现了调速。实际编程中,我们更常见的做法是使用两个PWM通道,分别控制IA和IB,这样可以更灵活地实现正反转和调速,后面在智能小车部分会详细讲。
理解了这个,你就能看懂代码里像__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 500)这样的函数在做什么了。它就是在调整PWM脉冲里高电平的宽度(即比较寄存器的值),这个值相对于定时器的自动重装载值(ARR)就决定了占空比。ARR决定了PWM的频率(频率=时钟频率/((ARR+1)*(PSC+1))),而比较值(CCR)决定了占空比(占空比=CCR/(ARR+1))。调大CCR值,占空比增大,电机转速就上去了。
3. 手把手硬件连接与基础代码实战
光说不练假把式,咱们现在就接上线,写代码,让电机真正转起来。我以最经典的STM32F103C8T6(蓝色小板)和Arduino Uno为例,分别说明,你可以根据手头的开发板选择。
对于STM32环境(使用HAL库):
首先,硬件连接。你需要准备:L9110S模块、一个5V左右的直流电机(TT马达)、一块STM32开发板、一个外接电源(比如4节5号电池盒或一块7.4V锂电池)。切记:L9110S的VCC和GND一定要接外部电源! 很多新手图省事,直接从开发板的5V引脚取电给模块,这样当电机启动瞬间电流很大时,很容易导致开发板电压被拉低,引起单片机复位或者程序跑飞。正确的接法是:电池正负极接到模块的VCC和GND;模块的GND还需要和开发板的GND连在一起,这叫“共地”,确保它们有相同的电压参考点。然后,我们控制电机A,将模块的A-IA和A-IB分别接到STM32的任意两个GPIO引脚,比如PA8和PA9。
接下来,在STM32CubeMX里配置。我们假设用TIM1的通道1(PA8)和通道2(PA9)输出两路PWM。
- 打开CubeMX,选择你的芯片型号。
- 在
Pinout & Configuration标签页,找到TIM1。 - 将
Channel1和Channel2的模式设置为PWM Generation CH1和PWM Generation CH2。 - 在右侧的参数配置中,设置
Prescaler(分频系数PSC)和Counter Period(自动重装载值ARR)。比如系统时钟72MHz,我们想要一个10kHz的PWM频率,计算一下:PSC设为71,ARR设为99。这样定时器时钟=72M/(71+1)=1MHz,频率=1M/(99+1)=10kHz。 - 将
CH1和CH2的Pulse(初始脉冲宽度,即CCR值)设为0。 - 生成代码。
在生成的工程里,主函数初始化后,启动PWM:HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); 和 HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);。
现在,写一个控制函数。比如,我们想让电机以50%占空比正转:
void MotorA_Forward(uint32_t speed) { // speed是CCR值,范围0-ARR
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, speed); // PA8输出PWM波
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 0); // PA9输出低电平
}
反转则是:
void MotorA_Backward(uint32_t speed) {
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0); // PA8输出低电平
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, speed); // PA9输出PWM波
}
停止:
void MotorA_Stop(void) {
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 0);
}
调用MotorA_Forward(50),电机就会以50%的功率正转了。你可以通过调整speed参数(0到ARR之间)来无级调速。
对于Arduino环境:
连接更简单:L9110S的VCC、GND接外部电池;模块的A-IA、A-IB分别接Arduino的数字引脚,比如D5和D6(这两个引脚都支持PWM输出)。
代码极其简洁:
int IA1 = 5; // 对应模块A-IA
int IB1 = 6; // 对应模块A-IB
void setup() {
pinMode(IA1, OUTPUT);
pinMode(IB1, OUTPUT);
}
void motorA_control(int speed, bool direction) { // speed: 0-255, direction: true=正转
if(direction) {
analogWrite(IA1, speed); // D5输出PWM
digitalWrite(IB1, LOW); // D6输出低电平
} else {
digitalWrite(IA1, LOW); // D5输出低电平
analogWrite(IB1, speed); // D6输出PWM
}
}
void loop() {
motorA_control(100, true); // 以较低速正转
delay(2000);
motorA_control(200, true); // 中速正转
delay(2000);
motorA_control(255, false); // 全速反转
delay(2000);
// 停止
digitalWrite(IA1, LOW);
digitalWrite(IB1, LOW);
delay(2000);
}
Arduino的analogWrite函数内部已经封装好了PWM,参数范围0-255对应占空比0%-100%,对于新手来说门槛更低,可以快速看到实验现象,建立信心。
4. 迈向智能小车:双电机差速控制与运动算法
单个电机转起来只是第一步,我们的目标是造一辆能跑能拐弯的智能小车。这就需要用到两个电机,也就是L9110S模块上的电机A和电机B,分别控制小车的左轮和右轮。智能小车最基本的运动模式:前进、后退、左转、右转、停止,全部基于对这两个电机的协同控制。
这里引入一个机器人学中核心的概念:差速转向。就像坦克一样,我们的智能小车没有方向盘,转弯全靠左右两个轮子的速度差。如果左轮和右轮速度相同且同向,小车就直行;如果左轮速度大于右轮速度,小车就向右转;反之则向左转;如果左右轮速度相同但方向相反,小车就能实现原地旋转。基于这个原理,我们可以定义小车的所有基本动作:
| 运动状态 | 左轮电机(MotorA) | 右轮电机(MotorB) | 实现方式 |
|---|---|---|---|
| 前进 | 正转,全速 | 正转,全速 | MotorA_Forward(255); MotorB_Forward(255); |
| 后退 | 反转,全速 | 反转,全速 | MotorA_Backward(255); MotorB_Backward(255); |
| 左转 | 低速正转/反转 | 高速正转 | MotorA_Forward(100); MotorB_Forward(255); (弧线转) |
| 右转 | 高速正转 | 低速正转/反转 | MotorA_Forward(255); MotorB_Forward(100); (弧线转) |
| 原地左转 | 反转,中速 | 正转,中速 | MotorA_Backward(150); MotorB_Forward(150); |
| 原地右转 | 正转,中速 | 反转,中速 | MotorA_Forward(150); MotorB_Backward(150); |
| 停止 | 刹车 | 刹车 | MotorA_Stop(); MotorB_Stop(); |
在代码中,我们可以把这些动作封装成函数,方便调用。但这里有个实际项目中必会遇到的坑:电机差异和摩擦力不均。即使你给左右电机完全相同的PWM值,由于电机本身制造上的微小差异、车轮安装的摩擦力不同,小车很可能走不直,会慢慢跑偏。这就需要引入“校准”环节。我的经验是,在小车程序初始化时,做一个简单的校准测试:让小车以固定速度前进一段距离(比如2米),测量其实际偏移。如果向右偏,说明左轮力量偏大,那么在前进函数里,就给左轮的PWM值减去一个微小的补偿值offset,比如MotorA_Forward(255 - offset); MotorB_Forward(255);。这个offset需要你根据实测反复调整,可能是个位数(比如3或5)。虽然粗糙,但对于入门级小车来说非常有效。
更进一步,如果你想实现更平滑、更精准的运动,比如让小车以设定的速度匀速前进,或者按照指定的转弯半径转弯,就需要用到简单的闭环控制思想——PID算法的雏形。虽然完整的PID对新手有点难,但我们可以先实现一个最基础的“P”(比例)控制。例如,用编码器测量电机的实际转速(如果电机带编码器的话),然后计算目标转速和实际转速的误差,误差越大,就越大幅度地增加或减少PWM值,让实际转速紧紧“跟随”目标转速。这样即使负载变化(比如爬坡),小车也能努力维持速度不变。当然,用L9110S驱动带编码器的电机时,要注意编码器的供电和信号线别接错了,避免干扰。
5. 项目进阶:集成传感器打造真正“智能”小车
让小车能动起来只是完成了“车”的部分,“智能”二字还需要各种传感器来赋予它感知环境的能力。结合L9110S这个可靠的运动底盘,我们可以轻松扩展出很多有趣的功能。
方案一:红外避障小车
这是最经典的入门项目。在车头左右两侧安装两个红外避障传感器(常见的是TCRT5000模块)。它发射红外光,遇到障碍物反射回来被接收管检测到,输出电平跳变。编程逻辑很简单:在loop循环里不断读取两个传感器的状态。如果左传感器检测到障碍,就让小车右转避开;如果右传感器检测到障碍,就让小车左转;如果都没检测到,就前进。代码框架如下(以Arduino为例):
int leftSensor = 2; // 接左传感器DOUT
int rightSensor = 3; // 接右传感器DOUT
void loop() {
bool leftObstacle = digitalRead(leftSensor); // 通常有障碍时输出低电平
bool rightObstacle = digitalRead(rightSensor);
if (!leftObstacle && !rightObstacle) {
// 前方无障碍,前进
goForward();
} else if (leftObstacle && !rightObstacle) {
// 左边有障碍,右转
turnRight();
delay(300); // 转一段时间
} else if (!leftObstacle && rightObstacle) {
// 右边有障碍,左转
turnLeft();
delay(300);
} else {
// 前方都有障碍,后退然后随机转个弯
goBackward();
delay(500);
turnAroundRandomly();
}
}
这个项目能让你立刻体会到“智能”的成就感,看着小车自己在房间里游走,避开桌椅腿,感觉非常奇妙。
方案二:超声波跟随/避障小车 用超声波模块(如HC-SR04)替代红外传感器,可以测量更精确的距离。你可以设定一个“安全距离”(比如20厘米)。当测量距离大于30厘米时,小车前进靠近;当距离小于10厘米时,小车后退远离;保持在10-30厘米之间则停止。这就实现了一个简单的“跟随”或“避障”功能。超声波模块的触发和回声引脚需要接到单片机的普通IO上,通过测量高电平脉冲时间来换算距离。记得在控制电机动作之间,加入适当的延时,避免因为超声波测距周期和电机响应太快而导致动作抽搐。
方案三:蓝牙遥控小车
通过手机APP蓝牙遥控小车,是另一个受欢迎的项目。你需要一个HC-05或HC-06蓝牙模块,与单片机串口连接。在手机上下载一个蓝牙串口调试APP,或者自己用App Inventor等工具写一个简单的遥控界面。APP发送特定的字符(比如‘F’、‘B’、‘L’、‘R’、‘S’分别代表前、后、左、右、停),单片机串口中断接收到字符后,调用对应的电机控制函数。这里要注意,主循环要处理串口数据,不能有长时间的delay阻塞,否则遥控指令会不跟手。通常用millis()函数进行非阻塞的定时控制会更流畅。
在这些项目中,L9110S扮演着默默无闻但至关重要的角色。它稳定地将你的控制逻辑转化为车轮实实在在的转动。我遇到过有学生接线松动导致电机时转时不转,或者PWM频率设置太低导致电机啸叫(发出刺耳的滋滋声),通常把频率调到10kHz以上就能解决。还有一点,当小车需要急停或快速切换方向时,在代码里最好做一个短暂的“刹车”状态(即两个控制引脚都置低),让电机快速消耗掉惯性,而不是直接从正转切换到反转,这样对电机和驱动芯片都更友好。
玩转L9110S和智能小车,最大的乐趣在于从一个个简单的功能模块开始,最终将它们有机组合,创造出有自己想法的小作品。这个过程中遇到的接线问题、电源问题、代码逻辑问题,都是宝贵的经验。当你看着自己亲手组装、编程的小车按照预设的规则跑起来时,那种动手实现想法的满足感,是单纯看书学习无法比拟的。
更多推荐
所有评论(0)