自动避障小车仿真

描述

课题的技术要求:本次课题最终要求小车能在无人操作的条件下实现自动避障,避障基于超声波测距的原理实现的,当超声波检测到小车与障碍物的距离大于设定值时,小车按照原先设定的速度正常行驶;当超声波检测到小车和障碍物的距离小于设定值时,单片机控制蜂鸣器发声同时控制小车停止。因为本次设计的小车时两轮驱动的小车,也没有安装舵机,所以小车的转弯通过控制两个车轮间的转速差实现。
课题研究的主要内容:本次设计以超声波避障为研究对象,以自动避障为核心控制功能。要研究内容包括:以STC89C52为控制核心的智能系统的平台搭建、各个模块的选型、多传感器的组合应用、PWM控制电机驱动的相应动作、外置储存器的选型、测距系统的选择与搭建、报警与显示系统的选择与搭建等。

避障小车

系统总体流程图

避障小车

 

STC89C52单片机接通电源后开始运行,首先对LCD1602的寄存器发送数据进行初始化,完成最基本的屏幕显示配置,然后在界面上显示主界面的框架,接下来在While循环中不断采集超声波发送的数据,进行转换后变为整数字节型显示在LCD1602屏幕上,若采集的数值大于设定值即报警。

键盘程序流程图

避障小车

在键盘处理函数中,分别判断按下的为哪个按键,若按下设定值增加按键,则设定值的数值增加10;若按下设定值减少按键,设定值的数字减少10,并在变动后均存入外部储存器。

避障小车
#include "reg51.h"
#include < intrins.h >
#define uchar unsigned char
#define uint unsigned int
#define LCD1602_DATAPINS P0
sbit LCD1602_E=P2^7;
sbit LCD1602_RW=P2^6;
sbit LCD1602_RS=P2^5;
sbit Trig = P1^1;
sbit Echo = P1^0;
sbit BEEP = P2^4;
sbit MotorA_1 = P2^0;
sbit MotorA_2 = P2^1;
sbit MotorB_1 = P2^2;
sbit MotorB_2 = P2^3;
sbit I2C_SDA =      P1^2;  		
sbit I2C_SCL =      P1^3; 
unsigned char Set_dis = 80;
unsigned char code ASCII[15] =    {'0','1','2','3','4','5','6','7','8','9','.','-','M'};
static unsigned char DisNum = 0; //显示用指针				  
unsigned int  time=0;
unsigned long S=0;
bit      flag =0;
unsigned char disbuff[4]	   ={ 0,0,0,0,};
unsigned int DIstance;



void D_10()
{
	uchar a, b;
	for(b=1; b >0; b--)
	{
		for(a=2; a >0; a--);
	}
}

void IIC_S()
{
	I2C_SDA = 1;
	D_10();
	I2C_SCL = 1;
	D_10();
	I2C_SDA = 0;
	D_10();
	I2C_SCL = 0;			
	D_10();		
}

void IIC_ST()
{
	I2C_SDA = 0;
	D_10();
	I2C_SCL = 1;
	D_10();
	I2C_SDA = 1;
	D_10();		
}

uchar WD_SB(uchar dat, uchar ack)
{
	uchar a = 0,b = 0;
			
	for(a=0; a< 8; a++)
	{
		I2C_SDA = dat > > 7;
		dat = dat < < 1;
		D_10();
		I2C_SCL = 1;
		D_10();
		I2C_SCL = 0;
		D_10();
	}

	I2C_SDA = 1;
	D_10();
	I2C_SCL = 1;
	while(I2C_SDA && (ack == 1))
	{
		b++;
		if(b > 200)
		{
			I2C_SCL = 0;
			D_10();
			return 0;
		}
	}

	I2C_SCL = 0;
	D_10();
 	return 1;		
}

uchar WD_RB() 
{
	uchar a = 0,dat = 0;
	I2C_SDA = 1;
	D_10();
	for(a=0; a< 8; a++)
	{
		I2C_SCL = 1;
		D_10();
		dat < <= 1;
		dat |= I2C_SDA;
		D_10();
		I2C_SCL = 0;
		D_10();
	}
	return dat;		
}
void CC_WR(uchar addr,uchar dat)
{
	IIC_S();
	WD_SB(0xa0, 1);
	WD_SB(addr, 1);
	WD_SB(dat, 0);
	IIC_ST();
}


uchar CC_RD(uchar addr)
{
	uchar num;
	IIC_S();
	WD_SB(0xa0, 1);
	WD_SB(addr, 1);
	IIC_S();
	WD_SB(0xa1, 1);
	num=WD_RB();
	IIC_ST();
	return num;	
}
void RUN_UP()
{
	MotorA_1 = 1;
	MotorA_2 = 0;
	MotorB_1 = 1;
	MotorB_2 = 0;
}

void RUN_DOWN()
{
	MotorA_1 = 0;
	MotorA_2 = 0;
	MotorB_1 = 0;
	MotorB_2 = 0;
}

void RUN_RIGHT()
{
	MotorA_1 = 1;
	MotorA_2 = 0;
	MotorB_1 = 0;
	MotorB_2 = 1;
}

/*******************************************************************************
* 函 数 名         : Lcd1602_Delay1ms
* 函数功能		   : 延时函数,延时1ms
* 输    入         : c
* 输    出         : 无
* 说    名         : 该函数是在12MHZ晶振下,12分频单片机的延时。
*******************************************************************************/

void Lcd1602_Delay1ms(uint c)   //误差 0us
{
    uchar a,b;
	for (; c >0; c--)
	{
		 for (b=199;b >0;b--)
		 {
		  	for(a=1;a >0;a--);
		 }      
	}
    	
}

/*******************************************************************************
* 函 数 名         : LcdWriteCom
* 函数功能		   : 向LCD写入一个字节的命令
* 输    入         : com
* 输    出         : 无
*******************************************************************************/
void LcdWriteCom(uchar com)	  //写入命令
{
	LCD1602_E = 0;     //使能
	LCD1602_RS = 0;	   //选择发送命令
	LCD1602_RW = 0;	   //选择写入
	
	LCD1602_DATAPINS = com;     //放入命令
	Lcd1602_Delay1ms(1);		//等待数据稳定

	LCD1602_E = 1;	          //写入时序
	Lcd1602_Delay1ms(5);	  //保持时间
	LCD1602_E = 0;
}
/*******************************************************************************
* 函 数 名         : LcdWriteData
* 函数功能		   : 向LCD写入一个字节的数据
* 输    入         : dat
* 输    出         : 无
*******************************************************************************/		   	   
void LcdWriteData(uchar dat)			//写入数据
{
	LCD1602_E = 0;	//使能清零
	LCD1602_RS = 1;	//选择输入数据
	LCD1602_RW = 0;	//选择写入

	LCD1602_DATAPINS = dat; //写入数据
	Lcd1602_Delay1ms(1);

	LCD1602_E = 1;   //写入时序
	Lcd1602_Delay1ms(5);   //保持时间
	LCD1602_E = 0;
}


/*******************************************************************************
* 函 数 名       : LcdInit()
* 函数功能		 : 初始化LCD屏
* 输    入       : 无
* 输    出       : 无
*******************************************************************************/		   
void LcdInit()						  //LCD初始化子程序
{
 	LcdWriteCom(0x38);  //开显示
	LcdWriteCom(0x0c);  //开显示不显示光标
	LcdWriteCom(0x06);  //写一个指针加1
	LcdWriteCom(0x01);  //清屏
	LcdWriteCom(0x80);  //设置数据指针起点
}




//按指定位置显示一个字符
void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData)
{
 Y &= 0x1;
 X &= 0xF; //限制X不能大于15,Y不能大于1
 if (Y) X |= 0x40; //当要显示第二行时地址码+0x40;
 X |= 0x80; // 算出指令码
 LcdWriteCom(X); //这里不检测忙信号,发送地址码
 LcdWriteData(DData);
}
//按指定位置显示一串字符
void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData)
{
 unsigned char ListLength;

  ListLength = 0;
 Y &= 0x1;
 X &= 0xF; //限制X不能大于15,Y不能大于1
 while (DData[ListLength] >=0x20) //若到达字串尾则退出
  {
   if (X <= 0xF) //X坐标应小于0xF
    {
     DisplayOneChar(X, Y, DData[ListLength]); //显示单个字符
     ListLength++;
     X++;
    }
  }
}

void  StartModule() 		         //启动模块
{
	  Trig=1;			                     //启动一次模块
	  _nop_(); 
	  _nop_(); 
	  _nop_(); 
	  _nop_(); 
	  _nop_(); 
	  _nop_(); 
	  _nop_(); 
	  _nop_(); 
	  _nop_(); 
	  _nop_(); 
	  _nop_(); 
	  _nop_(); 
	  _nop_(); 
	  _nop_(); 
	  _nop_(); 
	  _nop_(); 
	  _nop_();
	  _nop_(); 
	  _nop_(); 
	  _nop_(); 
	  _nop_();
	  Trig=0;
}

/*******************************************************************************
* 函 数 名         : main
* 函数功能		   : 主函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Conut(void)
	{
	 time=TH0*256+TL0;
	 TH0=0;
	 TL0=0;
	
	 S=(time*1.7)/100;     //算出来是CM
	 DIstance = S;
	 if((S >=700)||flag==1) //超出测量范围显示“-”
	 {	 
	  flag=0;
	 
	  DisplayOneChar(0, 1, ASCII[11]);
	  DisplayOneChar(1, 1, ASCII[10]);	//显示点
	  DisplayOneChar(2, 1, ASCII[11]);
	  DisplayOneChar(3, 1, ASCII[11]);
	  DisplayOneChar(4, 1, ASCII[12]);	//显示M
	 }
	 else
	 {
	  disbuff[0]=S%1000/100;
	  disbuff[1]=S%1000%100/10;
	  disbuff[2]=S%1000%10 %10;
	  DisplayOneChar(0, 1, ASCII[disbuff[0]]);
	  DisplayOneChar(1, 1, ASCII[10]);	//显示点
	  DisplayOneChar(2, 1, ASCII[disbuff[1]]);
	  DisplayOneChar(3, 1, ASCII[disbuff[2]]);
	  DisplayOneChar(4, 1, ASCII[12]);	//显示M
	 }
}


void zd0() interrupt 1 		 //T0中断用来计数器溢出,超过测距范围
{
    flag=1;							 //中断溢出标志
}

void main()
{
	 TMOD=0x01;		   //设T0为方式1,GATE=1;
	 TH0=0;
	 TL0=0;          
	 ET0=1;             //允许T0中断
	 EX0=1;       //外部中断0开()  EX1 为外部中断1 (P3^3)
	 IT0=1;        //低电平触发   IT1为中断1
	 EX1=1;       //外部中断0开()  EX1 为外部中断1 (P3^3)
	 IT1=1;        //低电平触发   IT1为中断1
	 EA=1;			   //开启总中断	
	 Set_dis = CC_RD(0x01);
	LcdInit();
	DisplayListChar(4,0,"Car system");
	while(1)
	{
		StartModule();
	  while(!Echo);		//当RX为零时等待
	  TR0=1;			    //开启计数
	  while(Echo);			//当RX为1计数并等待
	  TR0=0;				//关闭计数
    Conut();			//计算
		DisplayOneChar(8,1,(char)(Set_dis/100)+'0');
		DisplayOneChar(9,1,(char)(Set_dis/10%10)+'0');
		DisplayOneChar(10,1,(char)(Set_dis%10)+'0');
		if(DIstance >Set_dis)
		{
			BEEP = 0;
			RUN_RIGHT();
		}else
		{
			BEEP = 1;
			RUN_UP();
		}

	}
}


void Key1_INT( )  interrupt 0  // 这里0对应下表
{
    Set_dis = Set_dis+10; 
		CC_WR(0x01,Set_dis);
}


void Key2_INT( )  interrupt 2  // 这里0对应下表
{
	   Set_dis =Set_dis-10;
			CC_WR(0x01,Set_dis);
}

  审核编辑:汤梓红

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分