一、项目概述
该智能小车具有循迹、跟随、避障三种模式,可以通过语音识别实现三种模式的切换,并且在oled显示屏显示当前模式。在循迹模式下,小车可以通过循迹模块跟随赛道中的黑线位置实时改变方向,并沿赛道前行;在跟随模式下,小车可以通过跟随模块实时跟随前方物体而移动;在避障模式下,小车利用超声波模块检测前方距离,并根据距离判断障碍物位置,并及时躲避前方障碍。
二、硬件部分
1.小车底盘套件
2.51开发板
3.电池盒(4节)
4.面包板
5.L9110s电机驱动模块
6.循迹模块(2个)
7.红外跟随模块(2个)
8.SU-03语音模块
9.OLED显示屏
10.超声波
11.sg-90舵机
12.杜邦线若干
•循迹模块介绍
当发射出的红外线没有被反射回来或被反射回来但强度不够大时,红外接收管一直处于关断状态,此时模块的输出端为高电平,指示二极管一直处于熄灭状态被检测物体出现在检测范围内时,红外线被反射回来且强度足够大,红外接收管饱和,此时模块的输出端为低电平,指示二极管被点亮。
总结就是一句话,有感应到黑线,D0输出高电平 ,灭灯。
•跟随模块介绍
左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,物体在左边,需要左转;
右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,物体在右边,需要右转。
•语音模块介绍
语音模块配置网址如下:
配置好以后先连接USB转TTL进行固件烧录 测试(SU-03T 的RX是B6 TX是B7 ),测试无误后即可接入开发板使用。
三、软件部分
main.c
#include "reg52.h"
#include "hc04.h"
#include "delay.h"
#include "sg90.h"
#include "motor.h"
#include "Oled.h"
#define MIDDLE 0
#define LEFT 1
#define RIGHT 2
#define BZ 1
#define XJ 2
#define GS 3
#define ST 4
sbit A25 = P1^5;
sbit A26 = P1^6;
sbit A27 = P1^7;
sbit leftSensoXJ = P2^7;
sbit rightSensorXJ = P2^6;
sbit leftSensorGS = P2^5;
sbit rightSensorGS = P2^4;
char dir;
double disMiddle;
double disLeft;
double disRight;
void xunjiMode()
{
if(leftSensoXJ == 0 && rightSensorXJ == 0){
goForward();//前进
}
if(leftSensoXJ == 1 && rightSensorXJ == 0){
goLeft();//左转
}
if(leftSensoXJ == 0 && rightSensorXJ == 1){
goRight();//右转
}
if(leftSensoXJ == 1 && rightSensorXJ == 1){
stop();//停止
}
}
void gensuiMode()
{
if(leftSensorGS == 0 && rightSensorGS == 0){
goForward();//前进
}
if(leftSensorGS == 1 && rightSensorGS == 0){
goRight();//右转
}
if(leftSensorGS == 0 && rightSensorGS == 1){
goLeft();//左转
}
if(leftSensorGS == 1 && rightSensorGS == 1){
stop();//停止
}
}
void bizhangMode()
{
if(dir != MIDDLE){
sgMiddle();//中间
dir = MIDDLE;
Delay450ms();
}
disMiddle = get_distance();
if(disMiddle > 35){
//前进
goForward();
}else if(disMiddle < 10){
goBack();
}else{
//停止
stop();
//测左边距离
sgLeft();//左边
Delay450ms();
disLeft = get_distance();
sgMiddle();//中间
Delay450ms();
//测右边距离
sgRight();//右边
dir = RIGHT;
Delay450ms();
disRight = get_distance();
if(disLeft < disRight){
goRight();
Delay150ms();
stop();
}
if(disLeft > disRight){
goLeft();
Delay150ms();
stop();
}
}
}
void main()
{
int mark = 0;
Time0Init();//舵机用
Time1Init();//超声波用
sgMiddle();//中间
Delay450ms();
Delay450ms();
dir = MIDDLE;
OLED_Init();
OLED_Clear();
Oled_Show_Str(2,2,"----Ready----");
while(1){
//循迹模式
if(A25 == 0 && A26 == 1 && A27 == 1){
if(mark != XJ){
OLED_Clear();
Oled_Show_Str(2,2,"----XunJi----");
}
mark = XJ;
xunjiMode();
}
//跟随模式
if(A25 == 1 && A26 == 0 && A27 == 1){
if(mark != GS){
OLED_Clear();
Oled_Show_Str(2,2,"----GenSui----");
}
mark = GS;
gensuiMode();
}
//避障模式
if(A25 == 1 && A26 == 1 && A27 == 0){
if(mark != BZ){
OLED_Clear();
Oled_Show_Str(2,2,"----BiZhang----");
}
mark = BZ;
bizhangMode();
}
//停下
if(A25 == 0 && A26 == 0 && A27 == 0){
if(mark != ST){
OLED_Clear();
Oled_Show_Str(2,2,"----Stop----");
}
mark = ST;
stop();
}
}
}
hc04.c
#include "reg52.h"
#include "delay.h"
sbit Trig = P2^3;
sbit Echo = P2^2;
void Time1Init()
{
TMOD &= 0x0F;
TMOD |= 0x10;
TH1 = 0;
TL1 = 0;
//设置定时器1工作模式1,初始值设定0开始数数,不着急启动定时器
}
void startHC()
{
Trig = 0;
Trig = 1;
Delay10us();
Trig = 0;
}
double get_distance()
{
double time;
//定时器数据清零,以便下一次测距
TH1 = 0;
TL1 = 0;
//1. 给Trig一个至少10us高电平
startHC();
//2. 由低电平跳转到高电平,表示开始发送波
while(Echo == 0);
//波出去的那一下,开启定时器
TR1 = 1;
//3. 由高电平跳转到低电平,表示波回来了
while(Echo == 1);
//波回来的那一下,停止定时器
TR1 = 0;
//4. 计算出中间经过多少时间
time = (TH1 * 256 + TL1)*1.085;//二进制1左移1位,变成10(2),相当于乘以2,左移8位,相当于乘以2的8次方=256
//5. 距离=速度(340m/s)*时间/2
return(time * 0.017);
}
sg90.c
#include "reg52.h"
#include "delay.h"
sbit D5 = P3^7;
sbit D6 = P3^6;
sbit sg90_con = P1^1;
int jd;
int cnt = 0;
void Time0Init()
{
//1. 配置定时器0工作模式16位计时
TMOD &= 0xF0;
TMOD |= 0X01;
//2. 给初值,定一个0.5ms出来
TL0 = 0x33;
TH0 = 0xFE;
//3.打开定时器0中断
ET0 = 1;
//4.打开总中断EA
EA = 1;
//5. 开始计时
TR0 = 1;
TF0 = 0;
}
void sgMiddle()
{
//舵机开盖
jd = 3;//90度,1.5ms高电平
cnt = 0;
}
void sgLeft()
{
//舵机开盖
jd = 5;//135度,2.5ms高电平
cnt = 0;
}
void sgRight()
{
//舵机关盖
jd = 1;//0度,0.5ms高电平
cnt = 0;
}
void Time0Handler() interrupt 1
{
cnt++;//统计爆表次数
TL0 = 0x33;//重新给初值
TH0 = 0xFE;//重新给初值
/*控制PWM波*/
if(cnt < jd){
sg90_con = 1;
}else{
sg90_con = 0;
}
/*统计爆表次数*/
if(cnt == 40){//爆表了40次,经过了20ms
cnt = 0;
sg90_con = 1;
}
}
motor.c
#include "reg52.h"
sbit RightCon1A = P3^7;
sbit RightCon1B = P3^3;
sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;
void goForward()
{
LeftCon1A = 0;
LeftCon1B = 1;
RightCon1A = 0;
RightCon1B = 1;
}
void goBack()
{
LeftCon1A = 1;
LeftCon1B = 0;
RightCon1A = 1;
RightCon1B = 0;
}
void goLeft()
{
LeftCon1A = 0;
LeftCon1B = 0;
RightCon1A = 0;
RightCon1B = 1;
}
void goRight()
{
LeftCon1A = 0;
LeftCon1B = 1;
RightCon1A = 0;
RightCon1B = 0;
}
void stop()
{
LeftCon1A = 0;
LeftCon1B = 0;
RightCon1A = 0;
RightCon1B = 0;
}
Oled.c
#include "reg52.h"
#include "intrins.h"
#include "Oledfont.h"
sbit scl = P0^1;
sbit sda = P0^3;
void IIC_Start()
{
scl = 0;//防止雪花
sda = 1;
scl = 1;
_nop_();
sda = 0;
}
void IIC_Stop()
{
scl = 0;
sda = 0;
scl = 1;
_nop_();
sda = 1;
_nop_();
}
char IIC_ACK()
{
char flag;
sda = 1;//就在时钟脉冲9期间释放数据线
_nop_();
scl = 1;
_nop_();
flag = sda;
_nop_();
scl = 0;
_nop_();
return flag;
}
void IIC_Send_Byte(char dataSend)
{
int i;
for(i=0;i<8;i++){
scl = 0;//scl拉低,让sda做好数据准备
sda = dataSend & 0x80;//1000 0000获得dataSend的最高位
_nop_();//发送数据建立时间
scl = 1;//scl拉高开始发送
_nop_();
scl = 0;//发送完毕,拉低
_nop_();
dataSend = dataSend << 1;
}
}
void OLED_Write_Cmd(char dataCmd)
{
//1.START
IIC_Start();
//2.写入从机地址 b0111 1000 0x78
IIC_Send_Byte(0x78);
//3.ACK
IIC_ACK();
//4.cotrol byte:(0)(0)000000 写入命令 (0)(1)000000 写入数据
IIC_Send_Byte(0x00);
//5.ACK
IIC_ACK();
//6.写入指令/数据
IIC_Send_Byte(dataCmd);
//7.ACK
IIC_ACK();
//8.STOP
IIC_Stop();
}
void OLED_Write_Data(char dataData)
{
//1.START
IIC_Start();
//2.写入从机地址 b0111 1000 0x78
IIC_Send_Byte(0x78);
//3.ACK
IIC_ACK();
//4.cotrol byte:(0)(0)000000 写入命令 (0)(1)000000 写入数据
IIC_Send_Byte(0x40);
//5.ACK
IIC_ACK();
//6.写入指令/数据
IIC_Send_Byte(dataData);
//7.ACK
IIC_ACK();
//8.STOP
IIC_Stop();
}
void OLED_Init(void)
{
OLED_Write_Cmd(0xAE);//--display off
OLED_Write_Cmd(0x00);//---set low column address
OLED_Write_Cmd(0x10);//---set high column address
OLED_Write_Cmd(0x40);//--set start line address
OLED_Write_Cmd(0xB0);//--set page address
OLED_Write_Cmd(0x81); // contract control
OLED_Write_Cmd(0xFF);//--128
OLED_Write_Cmd(0xA1);//set segment remap
OLED_Write_Cmd(0xA6);//--normal / reverse
OLED_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
OLED_Write_Cmd(0x3F);//--1/32 duty
OLED_Write_Cmd(0xC8);//Com scan direction
OLED_Write_Cmd(0xD3);//-set display offset
OLED_Write_Cmd(0x00);//
OLED_Write_Cmd(0xD5);//set osc division
OLED_Write_Cmd(0x80);//
OLED_Write_Cmd(0xD8);//set area color mode off
OLED_Write_Cmd(0x05);//
OLED_Write_Cmd(0xD9);//Set Pre-Charge Period
OLED_Write_Cmd(0xF1);//
OLED_Write_Cmd(0xDA);//set com pin configuartion
OLED_Write_Cmd(0x12);//
OLED_Write_Cmd(0xDB);//set Vcomh
OLED_Write_Cmd(0x30);//
OLED_Write_Cmd(0x8D);//set charge pump enable
OLED_Write_Cmd(0x14);//
OLED_Write_Cmd(0xAF);//--turn on oled panel
}
void OLED_Clear()
{
int i,j;
for(i=0;i<8;i++){
OLED_Write_Cmd(0xB0 + i);//page0--page7
//每个page从0列
OLED_Write_Data(0x00);
OLED_Write_Data(0x10);
//0到127列,依次写入0,每写入数据,列地址自动偏移
for(j=0;j<128;j++){
OLED_Write_Data(0);
}
}
}
void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2
unsigned int i;
OLED_Write_Cmd(0xb0+(row*2-2)); //page 0
OLED_Write_Cmd(0x00+(col&0x0f)); //low
OLED_Write_Cmd(0x10+(col>>4)); //high
for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){
OLED_Write_Data(F8X16[i]); //写数据oledTable1
}
OLED_Write_Cmd(0xb0+(row*2-1)); //page 1
OLED_Write_Cmd(0x00+(col&0x0f)); //low
OLED_Write_Cmd(0x10+(col>>4)); //high
for(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){
OLED_Write_Data(F8X16[i]); //写数据oledTable1
}
}
/******************************************************************************/
// 函数名称:Oled_Show_Char
// 输入参数:oledChar
// 输出参数:无
// 函数功能:OLED显示单个字符
/******************************************************************************/
void Oled_Show_Str(char row,char col,char *str)
{
while(*str!=0){
Oled_Show_Char(row,col,*str);
str++;
col += 8;
}
}
四、项目实现
语音控制智能小车