【51单片机篇】项目:智能小车

一、项目概述

该智能小车具有循迹、跟随、避障三种模式,可以通过语音识别实现三种模式的切换,并且在oled显示屏显示当前模式。在循迹模式下,小车可以通过循迹模块跟随赛道中的黑线位置实时改变方向,并沿赛道前行;在跟随模式下,小车可以通过跟随模块实时跟随前方物体而移动;在避障模式下,小车利用超声波模块检测前方距离,并根据距离判断障碍物位置,并及时躲避前方障碍。

二、硬件部分 

1.小车底盘套件

2.51开发板

3.电池盒(4节)

4.面包板

5.L9110s电机驱动模块

6.循迹模块(2个)

7.红外跟随模块(2个)

8.SU-03语音模块

9.OLED显示屏

10.超声波

11.sg-90舵机

12.杜邦线若干

•循迹模块介绍 

当发射出的红外线没有被反射回来或被反射回来但强度不够大时,红外接收管一直处于关断状态,此时模块的输出端为高电平,指示二极管一直处于熄灭状态被检测物体出现在检测范围内时,红外线被反射回来且强度足够大,红外接收管饱和,此时模块的输出端为低电平,指示二极管被点亮。

总结就是一句话,有感应到黑线,D0输出高电平 ,灭灯。
•跟随模块介绍

左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,物体在左边,需要左转;

右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,物体在右边,需要右转。
•语音模块介绍 

 语音模块配置网址如下: 

 智能公元/AI产品零代码平台 (smartpi.cn)

配置好以后先连接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;   
    }       
}

四、项目实现

语音控制智能小车

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿gao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值