制作出來的實物圖如下:
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)
2021-3-28 22:43 上傳
點擊文件名下載附件
|