51单片机智能小车 红外循迹+led+蜂鸣器+超声波避障

 学院举办了一次智能车比赛,有点感兴趣就报名参加了,从10月20号开始准备到11月20号比赛结束刚好历时一个月,由于不是计算机专业的只学了一门c语言,单片机也还没有学,于是在网上各种找资源自学,匆匆忙忙完成了这次比赛,获得了一等奖,在这里分享记录一下比赛过程一级制作思路。

最终的样子

学院比赛章程规定了要用的器件有:89c52rc单片机,l298n四路电机驱动,四路红外传感器,HC-SR04超声波模块,SG90舵机,蓝牙模块等。 要求要用一个单片机完成蓝牙手动操作,红外循迹,超声波避障三个赛道,并且中途不能换单片机和烧录程序,所以我就把所有程序放在了一起,我没有学过代码规范,全都挤在一堆,变量名也乱七八糟,但是我还是尽量解释一下我的整个思路

首先我花几天时间学习了一下51单片机的一些表面的知识,学完了定时器,中断,串口,然后用蓝牙模块简单的尝试了用手机操作小车前进后退拐弯等操作

设置定时器1发射波特率

蓝牙串口app

然后我用pwm方法,控制不同电机高电平所占的比例控制左右轮子的速度来达到左转右转的目的

先设置定时器0并打开中断

 在中断1内可以控制每个轮子的速度和转向,这是其中一个轮子

 然后就开始设置红外循迹模块,这里是整个赛道的样子,有十字和直角弯

初赛的时候非常着急,因为四路红外传感器坏了,只有最后一天的时间调试,所以最后赛道都没有走完,分数也很低,但是初赛结束后有七天时间,于是重新设计了新的方法,并且拆掉了一块亚克力板减少重量,最后36秒的时间通过了赛道。

红外传感器的位置

 比赛黑线为1.5厘米,我们将两个红外传感器放在中间,另外两个放在旁边

 中间两个传感器可以负责普通的直线和拐弯,如果没有直角弯都可以直接通过。

 红外传感器检测到黑线就是高电平也就是1,如果没有就是低电平,也就是0,转弯的角度从低到高分为了0,1,2三级,如果R2,R3都是1,就正常直行,如果R3变成了0,那么就右转_0,并且记录下转弯的趋势,如果R2,R3都变成了0,并且是往右转的趋势,那么就右转_1,并且直到R2,R3中有一个重新检测到黑线才停止转弯,左转也是一样

两边两个传感器主要是负责直角弯的检测

 当R1检测到黑线变为1,并且R2,R3中有一个也检测到黑线,这是防止被赛道边际所干扰,那就左转_2,并且记录转弯趋势,由于惯性冲出赛道后继续保持转弯,直到回到黑线才结束转弯。

最终的比赛流程

【智能小车】【红外循迹】51单片机+l298n电机+四路红外传感器_哔哩哔哩_bilibili

可以改进的地方还有很多,可以再调快一点速度,并且检测转弯的条件也可以多加一些,而且在十字路口那里有较小的几率会左右转弯,可以再优化一点转弯的限制。

避障模块下次有时间再写,先把所有的代码贴出来,非常乱,大家不懂的可以问我

#include<reg52.h>
#include<math.h>
#include<intrins.h>
#define uchar unsigned char
#define uf  unsigned float
#define uint  unsigned int
#define MAIN_Fosc  11059200
#define rank0 a[0][0]
#define rank1 a[0][1]
#define rank2 a[0][2]
#define rank3 a[0][3]
#define flag0 a[1][0]
#define flag1 a[1][1]
#define flag2 a[1][2]
#define flag3 a[1][3]
												   
uchar x;
uchar y;
uint count;
uchar tmp;
uint i=0;
uint b;
uint kk=0;
float time;
float times = 0;	   //超声模块
float juli;
uint a[2][4] = {0,0,0,0,0,0,0,0};


sbit IN1 = P0^0;	 
sbit IN2 = P0^1;	 
sbit IN3 = P0^2;
sbit IN4 = P0^3;
sbit IN5 = P0^4;
sbit IN6 = P0^5;
sbit IN7 = P0^6;
sbit IN8 = P0^7;

sbit R1=P1^0;
sbit R2=P1^1;
sbit R3=P1^2;
sbit R4=P1^3;

sbit RGB_R = P1^6;
sbit RGB_G = P1^5;
sbit sound = P2^2;
sbit RGB_B = P1^4;

sbit PWM = P2^3; 	//对应舵机的PWM引脚
sbit Trig = P2^0;
sbit Echo = P2^1;
sbit s = P2^7;

void UartInit(void)		//4800bps@11.0592MHz
{
	PCON |= 0x80;		//使能波特率倍速位SMOD
	SCON = 0x50;		//8位数据,可变波特率
	TMOD &= 0x0F;		//清除定时器1模式位
	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
	TL1 = 0xF4;		//设定定时初值
	TH1 = 0xF4;		//设定定时器重装值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
} 

void Timer0_Init()
{
  TMOD &= 0xF0;		//设置定时器模式
  TMOD |= 0x01;		//设置定时器模式
  TH0=0Xfe;
  TL0=0X33;       //计数初值设置为0.5ms
  ET0=1;          //打开定时器0的中断
  TR0=1;          //打开定时器0
  EA=1;           //开总中断
}
void Init(void)			
{
	EA  = 1;			//打开总中断
	ES  = 1;			//打开串口中断
	ET0 = 1;			//打开定时器0中断
	PT0 = 0; 			//定时器0中断为最低优先级中断
	PS  = 1;			//串口1  中断为较低优先级中断
	ET2 = 0;
}


 void delay(uint xms) // xms代表需要延时的毫秒
{
	uint x,y;
	for(x=xms;x>0;x--)
	for(y=114;y>0;y--);
}
void Delay10us()		//@11.0592MHz
{
	unsigned char i;

	i = 2;
	while (--i);
}		   
void Delay400us()		//@11.0592MHz
{
	unsigned char i;

	_nop_();
	i = 181;
	while (--i);
}

void Delay40ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 72;
	j = 181;
	do
	{
		while (--j);
	} while (--i);
}
//超声波模块
void ChaoSheng()
{
	Trig=1;
	Delay10us();
	_nop_();
	Trig=0;
		
}

unsigned long Distance()
{
	T2CON = 0;		//初始化控制寄存器
	TL2 = 0;		//设置定时初始值
	TH2 = 0;		//设置定时初始值
	RCAP2L = 0;		//设置定时重载值
	RCAP2H = 0;		//设置定时重载值
	ChaoSheng();
	while(!Echo);
	TR2 = 1;
	while(Echo);
	TR2 = 0;
	/*计算*/
	time = (TH2*256+TL2)*1.085;//单位为us
	TH2 = 0;
	TL2 = 0;
	juli = (time*1.7)/100; //单位为cm
	return juli;
}
//蜂鸣器
void Sound()
{
	sound = 0;
}

void Sound_0()
{
	 sound = 1;
}
//RGB
void RGB(uchar rgb)
{
    if(rgb==G)
    {
        RGB_G = 1;
	    RGB_R = 0;
	    RGB_B = 0;
    }
    if(rgb==R)
    {
        RGB_G = 0;
	    RGB_R = 1;
	    RGB_B = 0;
    }
    if(rgb==B)
    {
        RGB_G = 0;
	    RGB_R = 0;
	    RGB_B = 1;
    }
}

//小车运动

void stop()
{
	rank0 = 0;
	rank1 = 0;
	rank2 = 0;
	rank3 = 0;
}
void fwd()
{
   	RGB(G);
	rank0 = 40;
	rank1 = 40;
	rank2 = 40;
	rank3 = 40;
	flag0 = 1;
	flag1 = 1;
	flag2 = 1;
	flag3 = 1;	
}

void back()
{
	RGB(G);
	rank0 = 30;
	rank1 = 30;
	rank2 = 30;
	rank3 = 30;
	flag0 = 0;
	flag1 = 0;
	flag2 = 0;
	flag3 = 0;
}

void tl_0()
{
	RGB(G);
	rank0 = 20;
	rank3 = 20;
	rank2 = 70;
	rank1 = 70;
	flag0 = 1;
	flag1 = 1;
	flag2 = 1;
	flag3 = 1;
}
void tr_0()
{
	RGB(G);
	rank0 = 70;
	rank3 = 70;
	rank2 = 20;
	rank1 = 20;
	flag0 = 1;
	flag1 = 1;
	flag2 = 1;
	flag3 = 1;
}
void tl_1()
{
	RGB(G);
	rank0 = 30;
	rank3 = 30;
	rank2 = 80;
	rank1 = 80;
	flag0 = 0;
	flag1 = 1;
	flag2 = 1;
	flag3 = 0;
}


 void tr_1()
{
	RGB(G);
	rank0 = 80;
	rank3 = 80;
	rank2 = 30;
	rank1 = 30;
	flag0 = 1;
	flag1 = 0;
	flag2 = 0;
	flag3 = 1;
}
void tl_2()
{
	RGB(G);
	rank0 = 70;
	rank3 = 70;
	rank2 = 90;
	rank1 = 90;
	flag0 = 0;
	flag1 = 1;
	flag2 = 1;
	flag3 = 0;
}						 
void tr_2()
{
	RGB(G);
	rank0 = 90;
	rank3 = 90;
	rank2 = 70;
	rank1 = 70;
	flag0 = 1;
	flag1 = 0;
	flag2 = 0;
	flag3 = 1;
}

//红外循迹模块
void go()
{
	RGB(G);
	rank0 = 50;
	rank1 = 50;
	rank2 = 50;
	rank3 = 50;
	flag0 = 1;
	flag1 = 1;
	flag2 = 1;
	flag3 = 1;
}

void move()
{	
	
	if(R2==1&&R3==1)
	{
		if(R2==1&&R3==1)
		{	
			go();
		}
	}

	if(R2==1&&R3==0)
	{
		x = 'R'	;
		tr_0();
		Delay10us();
	}
	if(R2==0&&R3==1)
	{
		x = 'L';
		tl_0();
		Delay10us();
	}
	if(R2==0&&R3==0)
	{
		while(R2==0&&R3==0)
		{
			if(x == 'L')
			{
				tl_1();
				Delay10us();
			}
			else if(x == 'R')
			{
				tr_1();
				Delay10us();
			}
		}
	}
	if(R1==1&&R4==0&&(R2==1||R3==1))
	{
		y = 'L';
		tl_2();
		Delay10us();
	}
	if(R1==0&&R4==1&&(R2==1||R3==1))
	{
		y = 'R';
		tr_2();
		Delay10us();
	}
	if(R1==0&&y=='L')
	{
		while(R1==0&&y=='L')
		{
			tl_2();
			Delay10us();
		}
		y = 'N';
	}
	if(R4==0&&y=='R')
	while(R4==0&&y=='R')
	{
		tr_2();
		Delay10us();
	}
	y = 'N';
}
void up_3()
{
	rank0 = 23;
	rank1 = 20;
	rank2 = 20;
	rank3 = 23;
	flag0 = 1;
	flag1 = 1;
	flag2 = 1;
	flag3 = 1;
}
void tl_3()
{
	rank0 = 13;
	rank1 = 20;
	rank2 = 20;
	rank3 = 13;
	flag0 = 0;
	flag1 = 1;
	flag2 = 1;
	flag3 = 0;
}
void tr_3()
{
	rank0 = 20;
	rank1 = 13;
	rank2 = 13;
	rank3 = 20;
	flag0 = 1;
	flag1 = 0;
	flag2 = 0;
	flag3 = 1;
}
void down()
{
	rank0 = 70;
	rank1 = 70;
	rank2 = 70;
	rank3 = 70;
	flag0 = 0;
	flag1 = 0;
	flag2 = 0;
	flag3 = 0;	
}
void tl()
{
	rank0 = 89;
	rank1 = 60;
	rank2 = 60;
	rank3 = 89;
	flag0 = 0;
	flag1 = 1;
	flag2 = 1;
	flag3 = 0;	
}
void tr()
{

	rank0 = 89;
	rank1 = 60;
	rank2 = 60;
	rank3 = 89;
	flag0 = 1;
	flag1 = 0;
	flag2 = 0;
	flag3 = 1;
}
void bizhang()
{
/*
这里有两种方法,第一种是全程走直线,没有中途调整方向,如果做出来速度能非常快,难点只有要耐心调整小车能走直线和直角转弯,要非常精准不能碰到墙壁,但是我们的电机驱动或者是马达有问题,导致即使调整好了,过了一会测试的时候又走不了直线了,非常迷,所以我们换了种思路,用了第二种方法。
*/
/*
	kk = 2;
	delay(300);
	up();

	up();
	delay(300);
	stop();
	delay(400);
	tr();
	delay(340);
	stop();
	delay(400);
  
	if(Distance()<=20)
	{
		stop();
		delay(200);
		down();
		delay(20);
		stop();
		kk = 0;
		delay(300);
		if(Distance()>20)
		{
			tr();
			delay(340);
			stop();
			kk = 4;
			delay(300);
			while(1)
			{
				
			}
		}	
		else
		{
			kk = 4;
			delay(300);
			if(Distance()>20)
			{
				tl();
				delay(340);
			}
		}
		stop();
		delay(200);
	}
*/
	if(times!=1&&times!=3&&times!=4&&times!=9&&times!=11&&times!=12)
	{
		kk = 0;
		delay(300);
	}
	else
	{
		kk = 4;
		delay(300);
	}
	if(Distance()>=14.75&&Distance()<=15.25)
	{
		up_3();
	}
	else if(Distance()<14.75)
	{
		if(times!=1&&times!=3&&times!=4&&times!=9&&times!=11&&times!=12)
		{
			tl_3();
		}
		else
		{
			tr_3();
		}
	}
	else if(Distance()>15.25&&Distance()<40)
	{
		if(times!=1&&times!=3&&times!=4&&times!=9&&times!=11&&times!=12)
		{
			tr_3();
		}
		else
		{
			tl_3();
		}
	} 
	else if(Distance()>=40)
	{
		up_3();
		delay(800);
		if(times!=1&&times!=3&&times!=4&&times!=9&&times!=11&&times!=12)
		{
			tr();
			delay(340);
		}
		else
		{
			tl();
			delay(340);
		}
		up_3();
		delay(300);
		stop();
		delay(300);
		times += 1;
	}
	
	

}

void ctrl()
{
		switch(tmp)
		{
				case '1':
					fwd();break;
				case '2':
					back();break;
				case '3':
					tl_1();break;
				case '4':
					tr_1();break;
				case '5':
				{
					stop();
					RGB_R = 1;
					RGB_B = 0;
					RGB_G = 0;
					Sound_0();
				}break;
				case '6':
					tl_2();break;
				case '7':
					tr_2();break;
				case '9':
				{
					RGB_B = 1;
					RGB_R = 0;
					RGB_G = 0;
				}break;
				case 'A':
					kk = 0;break;
				case 'B':
					kk = 2;break;
				case 'C':
					kk = 4;break;
				case 'D':
					bizhang();break;
				case '0':
					move();break;		
				case '8':
					Sound();break;
				default:
					stop();
		}
}


void Timer() interrupt 1     
{
  TH0=0XFE;
  TL0=0X33;       //重新赋计数初值为0.5ms
  
  if(i<=kk)	//进入定时器的时间和高低电平临界值比较
  {
    PWM=1;		//输出PWM波形中的高电平
  }
  else
  {
    PWM=0;
  }
  i++;
	
  if(i>=40)		//计数40次,每次0.5ms,总共达到20ms周期后清零,为下一周期开始计数做准备
  {
    i=0;
  }
  //控制车轮转速
	count++;
    if(count >= 100)
    {
        count = 0;
	}
	if(count < rank0)
	{
		if(flag0 == 1)
		{
			IN1 = 1;
			IN2 = 0;
		}
		else
		{
			IN1 = 0;
			IN2 = 1;
		}
	}
	else
	{
		IN1 = 0;
		IN2 = 0;
	}
	//
	if(count < rank1)
	{
		if(flag1 == 1)
		{
			IN3 = 1;
			IN4 = 0;
		}
		else
		{
			IN3 = 0;
			IN4 = 1;
		}
	}
	else
	{
		IN3 = 0;
		IN4 = 0;
	}
	//
	if(count < rank2)
	{
		if(flag2 == 1)
		{
			IN5 = 1;
			IN6 = 0;
		}
		else
		{
			IN5 = 0;
			IN6 = 1;
		}
	}
	else
	{
		IN5 = 0;
		IN6 = 0;
	}
	//
	if(count < rank3)
	{
		if(flag3 == 1)
		{
			IN7 = 1;
			IN8 = 0;
		}
		else
		{
			IN7 = 0;
			IN8 = 1;
		}
	}
	else
	{
		IN7 = 0;
		IN8 = 0;
	}

}

void UART() interrupt 4
{
	EA = 0;
	if(RI == 1)
	{
		
		tmp = SBUF;
		if(SBUF == '0')
		{
			Sound();
			delay(200);
			Sound_0();
			ES = 0;
		}
	//	x = tmp;
		
		RI = 0;
	  
	}
	EA = 1; 
	
}


void main()
{
	UartInit();
	Init();
	Timer0_Init();
	stop();
	kk = 1;
	Sound_0();
	while(1)	 
	{
		tmp = SBUF;
		ctrl();
	}
}

版权声明:本文为CSDN博主「看这个大魔王」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_29705767/article/details/121470168

看这个大魔王

我还没有学会写个人说明!

暂无评论

相关推荐

步进电机实验

通过 ULN2003 驱动模块控制 28BYJ48 步进电机运行方向和速度,按下 KEY1 键调节电机旋转方向;按下 KEY2 键,电机加速;当按下 KEY3 键,电机减速。

普中51-单核-A2单片机

合伙创业、商业需求 Q:1274510382 https://www.bilibili.com/video/av286413345/ 功能与使用 单核A2产品 基于STC89C52 低功耗,高性能的51内核的CMOS 8位单片

基于8051单片机实现电子时钟+数字秒表设计

概述 电子时钟是一种利用数字电路来显示秒、分、时的计时装置,与传统的机械钟相比,它具有走时准确、显 示直观、无机械传动装置等优点,因而得到广泛应用。随着人们生活环境的不断改善和美化,在许