制作出来的实物图如下:
51单片机超声波避障小车
电路原理图如下:
单片机源程序如下:
#include <REGX51.H>
#include "lcd1602.h"
#include "ds18b20.h"
#include "eeprom52.h"
#include "car_pwm.h"
#include<intrins.h>
#define uint unsigned int
#define uchar unsigned char
sbit trig=P2^1; //超声波测距模块Trig
sbit echo=P3^2; //超声波测距模块Echo
sbit key1=P2^0;
sbit key2=P2^2;
sbit key3=P2^3;
sbit key4=P2^4; //按键IO口定义
bit key1_flag=1;
bit key2_flag=1;
bit key3_flag=1;
bit key4_flag=1; //按键标志位
bit start_flag=0; //开始测量标志位
bit start_flag1=0; //开始测量标志位
sbit key5=P1^6;//红外遥控
sbit key6=P2^5;
sbit key7=P3^3;
sbit key8=P3^4;
sbit LeftIRBZ=P2^6;//左循迹传感器
sbit RightIRBZ=P2^7;//右循迹传感器
bit beep1; //蜂鸣器标志位
int temp; //温度变量
uchar count; //中断累加变量
long int distance; //测量所得距离
uint distance_h=50; //阈值
uchar system_ms,ms,t1ms1,ms1; //累加时间
bit read_flag=1; //读数据标志位
bit memory_flag=0; //存储标志位
uint C;
void delay_nus(unsigned int i) //延时:i>=12 ,i的最小延时单12 us
{
i=i/10;
while(--i);
}
void delay_nms(unsigned int n) //延时n ms
{
n=n+1;
while(--n)
delay_nus(900); //延时 1ms,同时进行补偿
}
//对于掉电存储,实际就是用的单片机自带的EEPROM,对于这个EEPROM,使用起来要知道如下几点
//“ EEPROM存储区域分了好几个扇区”
//“ 存储数据时,先擦除要存储的扇区,然后写入数据”
//“ 读数据的时候,直接读取相应的扇区
void memory() //存储子函数
{
SectorErase(0x2000);
byte_write(0x2000,distance_h/256);
byte_write(0x2001,distance_h%256);
}
void read_memory() //读存储子函数
{
distance_h=byte_read(0x2000)*256+byte_read(0x2001);
if(distance_h>450) distance_h=50;
}
void delayt(uint x) //延时x ms函数
{
uchar j;
while(x-- > 0)
{
for(j = 0;j < 125;j++)
{
;
}
}
}
/*
对于显示函数,本次设计用到的是LCD1602液晶,程序里用到的显示函数主要有两个, LCD1602_write LCD1602_writebyte
两个都是有参函数,其中 LCD1602_write ,要填写两个元素,第一个只能填写0 1,当填写0,就说明你第二个元素是命令,当填写1,就
说明第二个元素是数据,例如 LCD1602_write(0,0x80); ,说明0x80这个数据对于LCD1602来说是命令,其功能是 在第一行第0个位置
进行显示。
LCD1602_writebyte 这个函数是显示一串字符串
液晶是字符屏,我们要显示的也只能是字符, temp_h/10%10 得到的是数据,是不能直接显示的,而0x30是字符0,0x31是字符1,所以我们
直接用 数据加上0x30就得到对应的字符数据;
*/
void dis_play() //显示函数
{
LCD1602_write(0,0x80); //0是发命令,0x80是液晶第一行第一个位置
LCD1602_writebyte(" ");
LCD1602_write(1,'0'+temp/100%10);
LCD1602_write(1,'0'+temp/10%10);
LCD1602_writebyte(".");
LCD1602_write(1,'0'+temp%10); //1是发送数据,显示温度数据,液晶只能显示字符,所以在显示的时候加一个‘0’,将数字转换成字符进行显示
LCD1602_write(1,0xdf);
LCD1602_writebyte(" D: ");
LCD1602_write(1,'0'+distance/100%10);
LCD1602_write(1,'0'+distance/10%10);
LCD1602_write(1,'0'+distance%10);
LCD1602_writebyte("cm ");
LCD1602_write(0,0xc0);
LCD1602_writebyte(" Alarm_Di:");
LCD1602_write(1,'0'+distance_h/100%10);
LCD1602_write(1,'0'+distance_h/10%10);
LCD1602_write(1,'0'+distance_h%10);
LCD1602_writebyte("cm ");
}
void Robot_Traction() //机器人循迹子程序
{
if(key5==1)
{
run();
delay_nms (1000);
stop();
}
if(key6==1)
{
leftrun();
delay_nms (1000);
stop();
}
if(key7==1)
{
rightrun();
delay_nms (1000);
stop();
}
if(key8==1)
{
back();
delay_nms (1000);
stop();
}
else {
if(LeftIRBZ == 0 && RightIRBZ == 0) //两个红外检测到黑线,就前进 Left_1_led Right_1_led
{
run_1();
delay_nms (100);
run_1();
}
if(LeftIRBZ == 1 && RightIRBZ == 0)
{
leftrun_1();
delay_nms (10);
}
if(LeftIRBZ == 0 && RightIRBZ == 1)
{
rightrun_1();
delay_nms (10);
}
if(LeftIRBZ == 1 && RightIRBZ == 1)
{
stop();
delay_nms (10);
}
}
}
void Robot_A()
{
if(key5==1)
{
run();
delay_nms (1000);
stop();
}
if(key6==1)
{
leftrun();
delay_nms (1000);
stop();
}
if(key7==1)
{
rightrun();
delay_nms (1000);
stop();
}
if(key8==1)
{
back();
delay_nms (1000);
stop();
}
}
//按键处理函数,常规的处理方式是,判断按键按下-短延时消抖-再次判断按键按下-执行函数体-按键死循环等待
//常规的有一个弊端,就是我按键一直按下,程序就一直等待按键死循环释放,程序不往下执行。所以这里采用另一种方式
//标志位判断的方式,按键送开的时候,将一个标志位置一,然后按键按下,判断标志位只要是1,就执行函数体,同时将标志位置零
//这时候,如果我按键还是按下,由于标志位已经置零,是不能再执行函数体的,只有按键松开,才会将标志位再次置一
//这个处理方式,可以放到定时器里面,效果最好,定时器本身就是定时一段时间执行一次,放到定时器里,变相的实现了定时扫描按键的作用
//并且可以消除抖动
void key_scan() //
{
if(!key1) //判断按键按下
{
if(key1_flag)
{
key1_flag=0;
if(distance_h<450) distance_h++; //阈值自加
}
}
else //松开按键
{
if(key1_flag==0)
{
memory_flag=1; //存储标志位置1一次
key1_flag=1;
}
}
if(!key2)
{
if(key2_flag)
{
key2_flag=0;
if(distance_h>0) distance_h--; //阈值自减
}
}
else
{
if(key2_flag==0)
{
memory_flag=1;
key2_flag=1;
}
}
if(!key3)
{
if(key3_flag)
{
key3_flag=0;
start_flag=1; //开始测量
start_flag1=0;
}
}
else key3_flag=1;
if(!key4)
{
if(key4_flag)
{
key4_flag=0;
start_flag=0; //停止测量
start_flag1=1;
}
}
else key4_flag=1;
}
void Time_init() //配置定时器
{
TMOD=0x11; //定时器0和1都是方式1
// TH1=0X4C;
// TL1=0X00; //50ms初值
TH1=0XFc; //1Ms定时
TL1=0X18;
ET1=1;
TR1=1; //打开定时器1
TL0=0x66;
TH0=0xfc; //1ms
ET0=1; //设置定时器0
EA=1; //打开总中断
}
void trigger() //启动测量
{
trig=0;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
trig=1;
}
void init_measuring() //初始化超声波
{
trig=1;
echo=1;
count=0;
}
void measuring() //测量函数
{
uchar l;
uint h,y;
TR0 = 1;
while(echo==1)
{
;
}
TR0 = 0;
l = TL0;
h = TH0;
y = (h << 8) + l;
y = y - 0xfc66;//us部分
distance = y + 1000 * count;//计算总时间,单位是微秒
TL0 = 0x66;
TH0 = 0xfc;
delayt(30);
C=331.45+0.061*temp;
distance = C* distance / 20000;//原始为:(0.34毫米/us)*时间/2//
}
void police() //报警函数
{
if(distance<distance_h)
{
back_1();
delay_nms (1000);
stop();
delay_nms (1000);
rightrun_1();
delay_nms (500);
stop();
}
else
{
// beep=1; //否则停止报警
run();
//beep1=0;
}
}
void main()
{
do
{
temp=Temper();
}while(temp==850); //温度传感器上电会读一个850,这里用do{}while()语句将850过滤掉
Time_init(); //调用定时器初始化函数
LCD1602_cls(); //调用液晶初始化函数
init_measuring(); //超声波相应端口初始化
read_memory(); //上电把存储的数据读出来
while(1)
{
// Robot_Traction();
//Robot_A();
if(memory_flag) //如果存储标志位是1
{
memory_flag=0; //清0
memory(); //存储一次数据
}
ms1++;
if(distance>=3) //如果距离大于3cm
{
if(ms1>=distance) //根据距离的远近,报警频率不同
{
ms1=0;
// if(beep1) beep=!beep;
// else beep=1;
}
}
else //否则小于3cm,用最快的报警频率
{
if(ms1>=3)
{
ms1=0;
// if(beep1) beep=!beep;
// else beep=1;
}
}
dis_play(); //液晶显示函数
if(start_flag) police(); //如果开始测量,调用报警函数
//if(start_flag1) Robot_Traction();
if(read_flag) //如果读数据标志位是1
{
read_flag=0; //置0
temp=Temper(); //读温度数据
if(start_flag) //如果开始测量
{
trigger(); //触发超声波启动
while(echo==0) //等待回声
{
;
}
measuring(); //进行距离测量
init_measuring(); //超声波相应端口初始化
}
else //否则
{
//beep1=0; //停止报警,测量距离是0
stop();
distance=0;
}
}
}
}
//……………………………………………中断服务函数…………………………………………………//
void T_0()interrupt 1
{
TF0 = 0;
TL0 = 0x66;
TH0 = 0xfc;
count++;
if(count==18)
{
TR0 =0;
TL0 = 0x66;
TH0 = 0xfc;
count = 0;
}
}
void time1() interrupt 3
{
t1ms1++;
TH1=0XFc; //1Ms定时
TL1=0X18;
if(t1ms1>=50)
{
t1ms1=0;
key_scan(); //调用按键处理函数
ms++;
}
if(ms%10==0)
{
read_flag=1; //每10*50ms=500ms将读数据标志位置1一次
}
if(ms>=40)
{
ms=0;
}
time3++;
pwm_val_left++;
pwm_val_right++;
pwm_out_left_moto();
pwm_out_right_moto();
}
全部资料51hei下载地址:
B65037资料20210322.zip
(242.92 KB, 下载次数: 66)
|