L293D电机驱动板控制麦克纳姆轮小车
前言:
第一次接触74HC595芯片搞Arduino的时候也没想过要写实现原理,过程也查了些资料,慢慢喜欢研究功能背后的东西,能力有限不算深挖,简单记录一下。
准备
主控板(Ardunio UNO)X 1
电机驱动板 X1
TT马达电机 X4
轮子 X 4
蓝牙模块 X 1
底板 X 1
杜邦线 若干
蓝牙调试器:用来发送蓝牙信息控制小车
1.介绍L293D电机驱动板
1.1外观
(为了兼容Arduino UNO和引出IO0、IO1做蓝牙通讯,已做了一些排针调整)
1.2 参数
电机驱动模块,可驱动四通直流电机;
采用芯片串/并转换器,称为74HC595。该芯片有8个输出(完美)和3个输入,用它来一次一位地输入数据。在接收脉冲时如果数据引脚为高电平,则将1移入移位寄存器;否则为0。当所有8个脉冲都接收到后,将8个值复制到锁存寄存器。
该芯片还有一个输出使能(OE)引脚,用于启用或禁用一次所有输出。
您可以将其连接到PWM引脚,然后使用“analogWrite()”函数来控制电机的速度。
2.介绍麦克纳姆轮
2.1 外观
2.2 特点
麦克纳姆轮是一种带有周边轮轴的机轮,一般分两种,一种周边轮轴向左倾斜,另一种轮轴向右倾斜。这些成角度的周边轮轴把一部分的机轮转向力转化到一个机轮法向力上面,所以能够使汽车实现左右平移的运动。
2.3.运动原理
2.3.1组装要点(俯视视角)
2.3.2 运动方向
轮子不同旋转方向对应前进、后退移动:
轮子不同旋转方向对应逆时针旋转和顺时针旋转:
轮子不同旋转方向对应向左、向右平移:
左下角和右上角侧移:
左上角和右下角侧移:
3.接线
3.1 电机接线
3.2 蓝牙接线
4.编程实现
4.1定义
//PWM控制引脚
const int PWM2A = 11; //M1 motor
const int PWM2B = 3; //M2 motor
const int PWM0A = 6; //M3 motor
const int PWM0B = 5; //M4 motor
//芯片引脚
const int DIR_CLK = 4; // Data input clock line 输入时钟
const int DIR_EN = 7; //Equip the L293D enabling pins 使能端
const int DATA = 8; // USB cable
const int DIR_LATCH = 12; // Output memory latch clock 输出锁存器
//作为shiftOut函数的Dir参数,控制运动状态
const int Forward = 216; //216 save to Forward 前进
const int Back = 39; //39 save to Back 后退
const int Left = 116; //116 save to Left 左移
const int Right = 139; //139 save to right 右移
const int Stop = 0; //Parking variable 停止
const int L_turn = 198; //turn left 左转
const int R_turn = 57; //turn right 右转
//电机速度变量 1 and 255,越大速度越快
int Speed1 = 180;
int Speed2 = 180;
int Speed3 = 180;
int Speed4 = 180;
//其他变量
char cmd;
4.2 初始设置
设置波特率9600用来调试,再设置引脚为输出
void setup()
{
Serial.begin(9600); //Set the serial port baud rate 9600
//Configure as output mode
pinMode(DIR_CLK,OUTPUT);
pinMode(DATA,OUTPUT);
pinMode(DIR_EN,OUTPUT);
pinMode(DIR_LATCH,OUTPUT);
pinMode(PWM0B,OUTPUT);
pinMode(PWM0A,OUTPUT);
pinMode(PWM2A,OUTPUT);
pinMode(PWM2B,OUTPUT);
}
4.3 主程序部分
void loop()
{
control_func(); //Call the Bluetooth car control function
// Motor(mytest,Speed1,Speed1,Speed1,Speed1); //test
}
void control_func()
{
if(Serial.available() > 0) //判断串口是否有接收到蓝牙发来的数据
{
serialData = Serial.read(); //将数据保存到变量
if ('F' == serialData ) cmd = 'F'; //如果接收到字符 F, 将 F 保存到变量cmd
else if('B' == serialData ) cmd = 'B';
else if('L' == serialData ) cmd = 'L';
else if('Y' == serialData ) cmd = 'Y';
else if('S' == serialData ) cmd = 'S';
else if('C' == serialData ) cmd = 'C';
else if('D' == serialData ) cmd = 'D';
else if( serialData == '+' && Speed1 < 245) //接收到字符“+”,速度就增加
{
Speed1 += 10; //每次加10
Speed2 = Speed1;
Speed3 = Speed1;
Speed4 = Speed1;
}
else if( serialData == '-' && Speed1 > 30)
{
Speed1 -= 10;
Speed2 = Speed1;
Speed3 = Speed1;
Speed4 = Speed1;
}
// Serial.println(serialData);
}
//变量cmd确实为“F”字符,证明遥控那边按下按钮发送了字符过来,这时小车执行前进
if('F' == cmd)
{
Motor(Forward,Speed1,Speed2,Speed3,Speed4); //调用电机控制函数,执行Forward前进
}
else if('B' == cmd)
{
Motor(Back,Speed1,Speed2,Speed3,Speed4);//后退
}
else if('L' == cmd)
{
Motor(Left,Speed1,Speed2,Speed3,Speed4); //左移
}
else if('Y' == cmd)
{
Motor(Right,Speed1,Speed2,Speed3,Speed4); //右移
}
else if('S' == cmd)
{
Motor(Stop,0,0,0,0); //停止
}
else if('C' == cmd)
{
Motor(L_turn,Speed1,Speed2,Speed3,Speed4); //左转
}
else if('D' == cmd)
{
Motor(R_turn,Speed1,Speed2,Speed3,Speed4); //右转
}
}
void Motor(int Dir,int Speed1,int Speed2,int Speed3,int Speed4)
{
analogWrite(PWM2A,Speed1); //Motor PWM speed regulation
analogWrite(PWM2B,Speed2); //Motor PWM speed regulation
analogWrite(PWM0A,Speed3); //Motor PWM speed regulation
analogWrite(PWM0B,Speed4); //Motor PWM speed regulation
digitalWrite(DIR_LATCH,LOW); //DIR_LATCH 设置低电平
shiftOut(DATA,DIR_CLK,MSBFIRST,Dir);//不同的Dir值对应不同运动状态
digitalWrite(DIR_LATCH,HIGH);//设置高电平
}
至此已完成了所有代码,并不多,后面是相关的解析。
4.4代码解析
通过74HC595芯片控制各个电机旋转方向,该芯片对应的一个函数
shiftOut(DATA,DIR_CLK,MSBFIRST,Dir);
前三个参数设置时钟等,最后一个参数 Dir为一个10进制数,该数值由一个8位二进制数转换而来,不同的数值将影响汽车的移动状态。
已知小车每个轮子各有“前进”和“后退”两种旋转状态,共有8种情况,每次控制小车移动时就需要控制轮子的旋转。一个8位二进制每一个“位”对应一个轮子状态,1为高电平起作用,0为低电平不起作用。
以前进“Forward”变量为例,设置变量值为216(换成8位二进制为1101 1000),也就是从左到右第1/2/4/5位高电平1起作用,相应地在扩展板中(如下图)控制电机端口M3-A、M4-A、M2-B、M1-B为高电平,这时候对应接到该端口的电机就旋转起来。可以看出来,如果同一个电机A和B互换,轮子就会反转,并且A和B不能同时高电平(同时高电平意味着同时存在正转和反转,这是不可能的),否则电机不转,那么8位二进制就只能其中4个为1,另4个为0。
测试获取每种运动状态对应的编码值
我们如何知道8位二进制哪些“位”为1呢,可以通过以下方式测试而来:
新定义一个变量mytest并赋值1(转换8位二进制为0000 0001)
const int mytest = 1;
在loop()中再添加一条代码调用函数Motor(测试完成后记得删除)语句,然后观察电机旋转情况。
void loop()
{
control_func();
Motor(mytest,Speed1,Speed1,Speed1,Speed1); //新加的测试,改变mytest变量测试轮子运动
}
当mytest=1,烧录代码后可以看到只有一个电机在旋转,就代表0000 0001为该电机该旋转方向的编码。
如下图所示,当设置变量为2(二进制为0000 0010),代表另一个电机另一种旋转状态。当设置变量为4(二进制为0000 0100)又代表另一个电机另一种旋转状态。将八位2进制逐位给1来测出4个电机正反2种旋转方式共8种状态对应的编码。
总结数据记录,想要汽车前进就要让四个电机保持往前旋转状态,也就是1101 1000,转换成十进制为216.最后得出所有运动状态的所有对应编码。特别地,控制同一个电机的电位不能同时为1(高电平),这样会导致失效。
对应的编码表如下图:
正如前面所述,小车左移时四个轮子的旋转方向是有要求的,就是右前轮前进,右后轮后退、左后轮前进和左前轮后退,所以对应01110100,每一个1代表该位为高电平,0代表低电平,换成十进制116。
将最右边一列的数值传入函数shifOut的变量Dir,即可得到不同运动状态。
蓝牙遥控部分
重点是要做判断成功接收蓝牙信息(串口接收大于0),并保存到变量serialData ,然后利用serialData 做字符判断即可。
if(Serial.available() > 0)
{
serialData = Serial.read(); //Receiving function