目录
省流助手(方案清单)
前言
一、小车模型
二、电机
三、电源
1、电池:
2、PCB电源选择:
3、MCU供电方案:
4、MCU和电机的供电方案:
四、主控MCU最小系统
五、电机驱动
1、让电机动起来:(电机驱动芯片)
2、电机正反转:
3、调速:
六、光电传感器红外对管模块
七、显示器
LCD1602.c
LCD1602.h
八、蓝牙遥控车
九、整体小车制作与调试过程
1、小车的组装:
2、前进后退:
3、加速减速:
4、左转右转:
5、调试过程:
总结
单片机:89C52或STC12足以
电机驱动:TB6612
红外对管:TCRT5000红外反射式光电传感器
电压比较器:LM393或LM339
显示器:LCD1602
蓝牙:HC06或HC05
循迹小车可能是很多刚学习51单片机的同学会接触到的一个项目或者比赛。对于整辆循迹小车,硬件部分和软件部分的占比是五五开的,但硬件和软件都不是很困难。下面我就分模块并结合PCB进行介绍。
这个是小车循迹的演示:51单片机循迹小车-CSDN直播
小车模型比较简单,我采用的是下面这一款车模,长260mm,宽140mm,高35.5mm。车身板子是亚克力材质。
然后对于我们制作的PCB电路板要安装在小车哪个位置的问题,小车车身板子上是有预留很多M3的铜柱定位孔,制作PCB时可放置对应的定位孔以便PCB板可以用铜柱固定在小车上。这样的好处是PCB板容易拆卸,一旦PCB电路有问题方便拆下来检查和更换。
与上面小车模型相互配套的电机是130直流电机(带减速箱),如图
电机
工作电压:3-6V (实测电压是可以上到12V的) ; 减速比:1:48 ; 双轴(两边白色的轴就是双轴的含义)
这种电机是可以接测速码盘的,电机有凸起标志端为车轮安装端,另一端为测速码盘安装端,如图
这个时候,可能就有小伙伴在犹豫要用什么电池,多少伏的电池。
首先排除一个选项:普通的5号电池。有的同学可能会去尝试把4颗5号电池串联得到6V去驱动整辆车子(其实我当时就这么干过>_<),电压虽然够了,但电流不够整辆车子运作,即使可以也支撑不了整辆车子跑完一圈。
那么得用什么呢?18650电池是首选。正常单颗18650电池是3.7V,当然我们没必要买单一的电池和电池槽来串联得到12V,这样很麻烦而且不安全,网上有很多也很常见的现成12V或7.4V电池组,自带充电放电保护板,还有配套充电器充电。
电池还有一个重要参数(mAh):比如3000mAh是什么意思,指的是这颗电池可以以3000mA电流大小放电1小时。当然如果你是放电电流大小是1000mA,那么它就可以放电3小时。
(购买电池的时候记得买充电器)
上面说了常见的18650电池组有12V和7.4V两种,那么我们的PCB板子要用12V还是7.4V呢?
一句话:“小孩子才做选择,而我全到要”。我们的PCB板可同时装载12V和7.4V的电池组接口(如图),这样如果调试的时候7.4V没法满足你的需求时,方便换上12V电池。
警告:不要把两颗电池同时接上去,别以为同时接上去就是19.4V。-_-
如果了解过单片机的同学可能就知道,单片机是有最大工作电压的,就STC89C52而言,它的最大输入电压为5V,这时你如果用7.4V或12V电池直接给单片机供电,很快就会闻到一股烧焦的味道(不要去尝试啊!)。
那得怎么办,很明显我们需要把7.4V或12V的电池电压降压到5V,再给单片机供电,这里就需要介绍一下降压稳压芯片和模块了,有多个方案给大家选择。
方案一:7805
输入电压范围不大于35V,输入输出压差2V,最大输出电流1.5A,下图为原理图,其原理简单,使用方便。但是其不足之处就是效率低,特别是压差越大时其效率越低。可将输入芯片的7V~20V电压,稳定输出为5.0V电压。
注意:该元件一般与散热片同步使用
方案二:LM2596(我本人比较喜欢用的)
这是一款常用的开关电源芯片,最高输入电压为40V,最高输出电压为37V,LM2596有4个版本,3个固定输出版本3.3V、5V、 12V,以及一个ADJ可调版本,最大输出电流3A,转换效率可达80%~90%左右,使用DCDC模块1205模块实现12V转5V输出,使用1205模块进行电压转换原理也简单,还有一个优点就是输入与输出隔离,该芯片输 出功率2W,最大输出电流400mA。
电路图可参考:
5V版本:
ADJ可调版本:
在这个电路中,我放置了两个六角开关和两个排针充当电压检查点,这样的设计是比较安全的,电池接入时,可先用万用表测一下输入电压检测点,电池电压正常的情况下按下输入开关,电流开始流经LM2596进行降压稳压,之后再用万用表测一下输出电压检测点看看输出是否是5V,如果正常,按下输出开关,电流就可以进入你的MCU主板了。
(做个设想,如果没有这两个六角开关和检查点,一旦电池接入后某个环节出问题,可能就会把LM2596芯片甚至连同单片机一起烧坏。)
有人肯定会问,我单片机用5V,那我电机还怎么用7.4V或12V呢?
这个问题很好解决的啦!如图:
芯片的选型是一个工程的开始。
对于简易循迹小车来说,普通的89C52是可以满足它的要求的,或者可以直接用STC12单片机(89C52的升级版),因为循迹小车有时候需要对车速进行控制,需要使用到PWM的知识,89C52可以通过定时器生成PWM波形的,但STC12不一样,它有几个IO口是可以直接输出PWM波形的,而且很精确,很方便我们控制小车车速。STC12完全兼容89C52的代码所有会用89C52基本就会STC12。(而且便宜,成本低^_^)。
除了STC12是89C52的升级版,还有STC15,STC8(终极版)等很多型号(我本人很喜欢用STC8,如果89C52学得差不多的同学可以去入手STC8,它的功能很强大很丰富)。
当然,只要钱到位,直接上STM32肯定能解决问题,不过我个人认为做一个简易的循迹小车用STM32有点杀鸡用牛刀的感觉,成本有点高(不过复杂的循迹小车比如飞卡那种,基本都是上STM32的)。
首先说一说电机在整辆车子中需要实现的动作:加减速、正反转、停止。
当然是通电它就转起来了呀!
给电机两端加上5V或者7.4V12V的电压,电机就开始嘎嘎转起来。所有这时很多同学就会根据这个原理,使用单片机的两个IO口,让一个IO口为高电平另一个为低电平,然后杜邦线接在电机上,试着给电机两端一个电压,接着就会发现电机连转都不转,理都不理你,或者也可能会非常敷衍地进行低速转动,让你感受人生的大起大落。
(别慌!很多人都是从这样过来的,吃一堑长一智。)
为什么不动?首先电机转动的前提是电压够,而且还得电流够,很多单片机的IO口输出电流是很小的,完全不足以驱动电机。
“单片机是一个大脑,可以起发号施令的作用,但绝对不要让大脑来做苦力”
这时候就需要一个叫做电机驱动芯片或者电机驱动模块的东西了,单片机给电机驱动芯片发送命令,让电机驱动芯片为单片机做苦力,这里也有多个方案。
方案一:ULN2003(不推荐)
如果用过某51单片机开发板可以就会接触到这个芯片,但首先声明这个芯片主要是为步进电机服务的,它可以驱动普通的电机并控制速度,但没法让电机正反转。所有这里不做考虑。
方案二:L298N
L298N是一款可接受高电压、大电流双路全桥式电机驱动芯片,工作电压可达46V,输出电流最高可至4A,通过控制主控芯片上的I/O输入端,直接通过电源来调节输出电压,即可实现电机的正转、反转、停止,由于电路简单,使用方便,通常情况下L298N可直接驱动继电器(四路)、螺线管、电磁阀、直流电机(两台)以及步进电机(一台两相或四相)。
管脚功能和电路图如下:
我本人是不怎么喜欢用这个芯片的,首先是因为它的模块和整体电路占的体积过大,10*10的板子至少占了四分之一。
方案二:TB6612
TB6612FNG 是一种驱动 IC,用于带低导通电阻器的 LD MOS 结构中带输出晶体管的 DC 马达。可利用 IN1 和 IN2 这两个输入信号,选择 CW,CCW,短路制动器、和停机等四种模式的其中一种模式。
电源电压 VM = 15 V(最大值);输出电流 I OUT = 1.2 A(平均值)/3.2 A (峰值);输出低导通电阻器:0.5 Ω (高+低典型值@ VM ≥ 5 V)
管脚功能:
控制方法:
电路图(非模块):
这是我比较喜欢用的电机驱动芯片,主要是体积很小,接线方便精简,而且便宜。
电机的正反转涉及到H桥的知识点,比较简单,在这里不进行介绍。
我就拿TB6612电机驱动芯片举例子,一颗TB6612芯片可接两个电机。
操作可以简单的概括为:
当AIN1=1;AIN2=0时,电机正转
当AIN1=0;AIN2=1时,电机反转。
PWMA是输入PWM波形进行小车调速,当然,你如果想全速前进的话,PWMA可以直接接5V。
调速需要使用到PWM的知识,CSDN上有很多更详细的文章可以学习,这里就不做讲解了
首先有一个知识点需要知道:黑色会吸收所有色光,白色反射所有色光。
可以使用TCRT5000红外反射式光电传感器。
TCRT5000红外反射式光电传感器也称红外对管,蓝色LED状的为红外发射管,黑色LED状的为红外接收管。
红外对管的内部图:红外发射管内部是LED灯,红外接收管内部是三极管
原理就是:红外发射管不断发射红外线,当红外线碰撞到白色会放射回红外线,红外接收管可以接收到反射回的红外线,其内部的三极管导通开始流过电流。当红外线碰撞到黑色不会放射回红外线,红外接收管没有接收到反射回的红外线,其内部的三极管不导通没有电流。所有,我们要做的就是用单片机检测红外接收管(三极管)的发射极有没有电压,如果没有电压则证明检测到了黑线。
但是如果用单片机来直接采集原始的发射极电压,对于没学过ADC的同学会有点麻烦,即使学过有时也会有意想不到的问题。
所有为了操作方便,我们需要一块芯片来单独处理三极管输出的电压,然后把三极管输出电压的高低判断转化为数字信号1和0发送给单片机,这样单片机只需要判断1和0就可以了。
有两种芯片可以选择LM393或LM339,这两款芯片差不多,区别就是LM393一次性可以接两个红外对管,LM339一次性可以接四个红外对管。
电路图如下:(使用的是LM393)
那么对于这个电路图,可能会问的几个问题:
(1)那个蓝白电位器是干什么的?
之前说LM393是用来判断三极管输出电压高低的,那么怎么样才算高,怎么样才算低。高和低的判断需要一个参考系,蓝白电位器让VCC分压给芯片通过一个参考系电压。举个例子:参考系电压为2.5V,那么这时候如果红外接收管输出电压小于2.5V,则判断为低电平,如果输出电压大于2.5V,则判断为高电平。
(2)原理图旁边的LED灯是干什么的?
LM393的输出的输出数字信号1和0,那我们在芯片的输出端加上LED灯,如果输出1,LED灭;输出0,LED亮。这样的好处是方便我们观察红外对管是否检测到黑线,也方便了后面调试。(说句心里话,这个LED灯很重要,有LED灯时的调试时间至少比没有LED灯时的调试时间少一半)。
在满足整辆车子能用情况下,做点拓展,可以加个显示屏显示时间,速度什么的。
对于新手,LCD1602就很香的。当然学得深一点的可以用OLED屏幕(不过89C52好像没法用OLED,内存不够)。
#include
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//函数定义:
/**
* @brief LCD1602延时函数,12MHz调用可延时1ms
* @param 无
* @retval 无
*/
void LCD_Delay()
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
/**
* @brief LCD1602写命令
* @param Command 要写入的命令
* @retval 无
*/
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602写数据
* @param Data 要写入的数据
* @retval 无
*/
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602设置光标位置
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @retval 无
*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else if(Line==2)
{
LCD_WriteCommand(0x80|(Column-1+0x40));
}
}
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*/
void LCD_Init()
{
LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x01);//光标复位,清屏
}
/**
* @brief 在LCD1602指定位置上显示一个字符
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @param Char 要显示的字符
* @retval 无
*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
/**
* @brief 在LCD1602指定位置开始显示所给字符串
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串
* @retval 无
*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
/**
* @brief 返回值=X的Y次方
*/
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i0;i--)
{
LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以有符号十进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-32768~32767
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
unsigned char i;
unsigned int Number1;
LCD_SetCursor(Line,Column);
if(Number>=0)
{
LCD_WriteData('+');
Number1=Number;
}
else
{
LCD_WriteData('-');
Number1=-Number;
}
for(i=Length;i>0;i--)
{
LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以十六进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFF
* @param Length 要显示数字的长度,范围:1~4
* @retval 无
*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i,SingleNumber;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
SingleNumber=Number/LCD_Pow(16,i-1)%16;
if(SingleNumber<10)
{
LCD_WriteData(SingleNumber+'0');
}
else
{
LCD_WriteData(SingleNumber-10+'A');
}
}
}
/**
* @brief 在LCD1602指定位置开始以二进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
}
}
#ifndef __LCD1602_H__
#define __LCD1602_H__
//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
#endif
有的同学可能会玩点花的,接上个蓝牙,做成蓝牙遥控车,这里蓝牙可以用HC05或HC06,需要学习到串口知识
然后什么是主机从机?
从机:从机蓝牙只能乖乖地等待别的主机蓝牙来连接它
主机:主机蓝牙可以主动去找别的从机蓝牙连接
主从一体机:主从一体机蓝牙可以配置为从机也可以配置为主机,通过AT指令设置(自行CSDN学习,放心,很简单)
如果你想用手机控制,使用从机就好,手机充当主机;如果你还想搞个遥控器,那就需要买一个从机和一个主从一体机。
首先整辆小车可以实现的动作有:前进后退,加速减速,左转右转
买的时候是有说明书的,跟着装就对了,不过我不知道是不是我运气问题,我的车模轮子特别难装,需要暴力出奇迹。
然后就是电机接线问题:一个TB6612可以接两个电机,但我们小车有四个轮子,怎么办呢?
方法一:如图,这样可以确保小车两边的电机是同时转的。
方法二:如图,一块TB6612不够就用两块嘛,反正又不贵-_-,而且这样可以精确控制到每个轮子,包括在转向也可以更精确,我是比较喜欢这种。
那就是把所有轮子调为全部正转或反转咯
得去学习PWM调速的知识
我使用的车模的单个车轮是不可以转向的,所有我们只能使用差速转向——比如小车右边轮子前进,左边轮子后退,那么整辆车的效果就是向右转。
做任何设计,过程是非常重要,如果把整个过程划分好步骤,分为多个阶段性小过程,那对整个设计来说有事半功倍的效果。
接下来就说说制作和调试的过程的一些阶段性步骤如图:(这是我本人制作时所使用也认为比较好,容错率比较高的一种调试过程)
有可能调试小车的时间会比制作小车的时间长,不过都是正常现象。大家如果看其他资料可能会看到一个叫PID的东西,PID可以让小车转向很丝滑,不过可能对于新手来说有点烧脑。实现简易的循迹小车完全不用用到PID,如果你想尝试一下PID也行,不过先把小车的基本功能完全实现了再说。
基本把整辆车子的设计方案都介绍了一下,不过都是我个人制作过程中的选型和心得看法,仅供参考,并不是专业介绍。欢迎大家提供更好的方案。