|
超声波测距导盲系统带原文仿真程序以及详细的介绍
由于超声波、语音无法仿真,只能仿真按键操作、液晶显示、光控灯功能。
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
盲人由于先天或后天的生理缺陷丧失了视觉功能,因而在日常生活和安全行走方面受到了很大的制约。所以,为了协助盲人安全行进,提高他们的生活能力与人身安全,世界各国一直在进行着电子导盲系统的研制。在此背景下,本文设计了一种基于超声波测距的导盲系统。大体上可分为两部分进行设计:测距系统与电源管理系统。
测距系统通过超声波的发送和接收来检测盲人周围各个方向上一段距离内的障碍物信息,并将其转化为声音信号,使盲人从中获知其周围的情况。本文是在基于STC89C52单片机控制的超声波测距原理的基础上,来确定目标范围内障碍物的存在,同时利用光敏电阻检测和处理电路对环境光线强度的采集,通过声音和蜂鸣器进行报警提示,系统的信息通过LCD1602进行显示。在软件设计中主要包括:串行口初始化函数、超声波发射函数、写字符函数、LCD1602显示函数、光照A/D处理函数和时钟函数。
电子导盲仪作为一种便携式电子设备,电源的地位在其研究中占有十分重要的位置。电源管理系统会对电源电压进行实时监控,为系统提供稳定的电源以及根据电量的状态作出相应的反应,这是导盲仪整个系统稳定可靠的重要一环。
制作出来的实物图如下:
Altium Designer画的原理图和PCB图如下:(51hei附件中可下载工程文件)
单片机源程序如下:
- /***********************************************************************************************************/
- #include <AT89x51.H> //器件配置文件
- #include <intrins.h>
- #define uchar unsigned char //宏定义
- #define uint unsigned int //宏定义
- sbit RX =P2^2; //接收端口
- sbit TX =P2^1; //发送端口
- sbit BEEP =P2^0; //蜂鸣器端口
- sbit LED =P3^5; //LED指示灯端口
- sbit LIGHT=P3^6; //光敏电阻端口
- sbit KEY_DIS =P1^3; //播放距离语音
- sbit KEY_TIME =P1^4; //播放时间语音
- sbit BUSY =P3^1; //语音芯片
- sbit DATA =P3^2; //语音芯片
- sbit RST =P3^3; //语音芯片
- unsigned int time=0; //定义发送到接收的时间
- unsigned int timer=0; //定义超声波扫描周期
- unsigned int count=0; //中断次数,闪烁用
- unsigned long S=0; //定义距离为0
- bit flag =0; //超出距离标志位
- bit Di_flag;
- bit Beep_flag;
- bit voice_flg; //语音播报标志位
- unsigned int counter;
- uchar time_data[7];
- uchar write_add[7]={0x8c,0x8a,0x88,0x86,0x84,0x82,0x80}; //数据的地址
- uchar read_add[7]={0x8d,0x8b,0x89,0x87,0x85,0x83,0x81};
- uchar code table1[]="Dis: |2000/00/00";
- uchar code table2[]="000cm|00:00:00 0";
- uchar code table3[]=" "; //清屏内容
- uchar code table4[]=" Set Real Time ";
- uchar code table9[]="Date: 2000/00/00";
- uchar code table0[]="Time: 00:00:00 0";
- bit Adjust; //调节标志位,=1表示进入调节模式,=0是正常模式
- sbit rs=P1^0; //LCD1602
- sbit rw=P1^1; //LCD1602
- sbit e=P1^2; //LCD1602
- sbit sck=P2^5; //时钟端口
- sbit io=P2^6; //时钟端口
- sbit rst=P2^7; //时钟端口
- sbit SELT =P1^5; //选择键
- sbit ENTER=P1^6; //确认键
- sbit UP =P1^7; //加键
- sbit DOWN =P3^0; //减键
- uchar Select_num; //选择按键按下次数
- uchar Enter_num; //确认按键按下次数
- char Year,Month,Day,Hour,Minute,Second,Week; //时间设置值
- void read_rtc();
- /****************************************LCD1602显示*****************************************************************************/
- void delay1(uint z) //延时函数
- {
- uint x,y;
- for(x=z;x>0;x--)
- for(y=10;y>0;y--);
- }
- void write_com(uchar com) //写指令函数
- {
- rw=0;
- delay1(5);
- rs=0;
- delay1(5);
- e=1;
- delay1(5);
- P0=com;
- delay1(5);
- e=0;
- delay1(5);
- }
- void write_date(uchar date) //写数据函数
- {
- rw=0;
- delay1(5);
- rs=1;
- delay1(5);
- e=1;
- delay1(5);
- P0=date;
- delay1(5);
- e=0;
- delay1(5);
- }
- void init() //初始化函数
- {
- uchar num;
- Year=16;
- Month=1;
- Day=1;
- Hour=15;
- Minute=30;
- Second=50;
- Week=6;
- e=0; // 时序表e初始为0
- write_com(0x38); //设置16*2显示,5*7点阵,8位数据接口
- write_com(0x0c); //设置光标
- write_com(0x06); //光标自动加1,光标输入方式
- write_com(0x01); //清屏
- write_com(0x80); //设置初始显示位置
- for(num=0;num<16;num++)
- {
- write_date(table1[num]);
- delay1(5);
- }
- write_com(0x80+0x40);
- for(num=0;num<16;num++)
- {
- write_date(table2[num]);
- delay1(5);
- }
- }
- void SetTime_dispaly(uchar add,uchar dat) //第一个:参数的地址,第二个:参数的内容
- {
- uchar shi,ge;
- shi=dat/10; //把温度的十位提取出来
- ge=dat%10; //把温度的个位提取出来
- write_com(add); //要写的地址
- write_date(0x30+shi); //十位的内容 1602字符库
- write_date(0x30+ge); //个位的内容 1602字符库
- }
- void Week_dispaly(uchar add,uchar dat) //第一个:参数的地址,第二个:参数的内容
- {
- write_com(add); //要写的地址
- write_date(0x30+dat); //十位的内容 1602字符库
- }
- void Date_dispaly(uchar add,uchar dat) //第一个:参数的地址,第二个:参数的内容
- {
- uchar shi,ge;
- shi=dat/16; //把十位提取出来
- ge=dat%16; //把个位提取出来
- write_com(add+3); //要写的地址
- write_date(0x30+shi); //十位的内容 1602字符库
- write_date(0x30+ge); //个位的内容 1602字符库
- }
- void LCD_Display_String(unsigned char line,unsigned char *string)
- { //液晶屏显示内容,把要显示的内容写到对应的位置
- unsigned char i;
- unsigned char address=0;
- if(line==1)
- {
- address=0x80; //0X80是第1行的第1个位置 ,0x81第2位
- }
- else if(line==2)
- {
- address=0x80+0x40; //0X80+0x40是第2行的第1个位置 ,0X80+0x40+1是第2行第2位
- }
-
- for(i=0;i<16;i++)
- {
- write_com(address);
- write_date(string[i]);
- address++;
- }
- }
- void Display(uchar add,uint dat) //显示避障距离
- {
- uchar bai,shi,ge;
- bai=dat%1000/100; //取出百位
- shi=dat%1000%100/10; //取出十位
- ge=dat%1000%10%10; //取出个位
- write_com(0x80+add);
- write_date(0x30+bai);
- write_date(0x30+shi);
- write_date(0x30+ge);
- }
- void Display_ASIIC(uchar add) //显示符号
- {
- write_com(0x40+0x80+add);
- write_date(0x2d);
- write_date(0x2d);
- write_date(0x2d);
- }
- void Time_Display(void)
- {
- read_rtc();
- Date_dispaly(0x80+0x40+9,time_data[6]); //显示秒
- Date_dispaly(0x80+0x40+6,time_data[5]); //显示分
- Date_dispaly(0x80+0x40+3,time_data[4]); //显示时
- Date_dispaly(0x80+11,time_data[3]); //显示日
- Date_dispaly(0x80+8,time_data[2]); //显示月
- Week_dispaly(0x80+0x40+15,time_data[1]); //显示周
- Date_dispaly(0x80+5,time_data[0]); //显示年
- // Year/10*16+Year%10
- Year=time_data[0]/16*10+time_data[0]%16;
- Week=time_data[1]/16*10+time_data[1]%16;
- Month=time_data[2]/16*10+time_data[2]%16;
- Day=time_data[3]/16*10+time_data[3]%16;
- Hour=time_data[4]/16*10+time_data[4]%16;
- Minute=time_data[5]/16*10+time_data[5]%16;
- Second=time_data[6]/16*10+time_data[6]%16;
- }
- /*********************************时间************************************************/
- void write_ds1302_byte(uchar dat)
- {
- uchar i;
- for(i=0;i<8;i++)
- {
- sck=0;
- io=dat&0x01; //准备数据,从最低位开始
- dat=dat>>1;
- sck=1;
- }
- }
- void write_ds1302(uchar add,uchar dat)
- {
- rst=0;
- _nop_(); //CPU原地踏步
- sck=0;
- _nop_();
- rst=1;
- _nop_();
- write_ds1302_byte(add); //传地址
- write_ds1302_byte(dat); //传数据
- rst=0; //不受其他影响
- _nop_();
- io=1; //释放
- sck=1;
- }
- uchar read_ds1302(uchar add)
- {
- uchar i,value;
- rst=0;
- _nop_(); //CPU原地踏步
- sck=0;
- _nop_();
- rst=1;
- _nop_();
- write_ds1302_byte(add);
- for(i=0;i<8;i++)
- {
- value=value>>1;
- sck=0;
- if(io)
- value=value|0x80;
- sck=1;
- }
- rst=0;
- _nop_();
- sck=0;
- _nop_();
- sck=1;
- io=1;
- return value;
- }
- void set_rtc() //设置时间
- {
- uchar i,j;
- for(i=0;i<7;i++) //转换BCD码
- {
- j=time_data[i]/10;
- time_data[i]=time_data[i]%10;
- time_data[i]=time_data[i]+j*16;
- }
- write_ds1302(0x8e,0x00); //去除写保护
- for(i=0;i<7;i++)
- {
- write_ds1302(write_add[i],time_data[i]);
- }
- write_ds1302(0x8e,0x80); //加写保护
- }
- void read_rtc()
- {
- uchar i;
- for(i=0;i<7;i++)
- {
- time_data[i]=read_ds1302(read_add[i]); //最终读出来的数 16进制
- }
- }
- /*************************************************按键****************************************************************/
- void Keyscan(void)
- {
- if(SELT==0)
- {
- delay1(2);
- if(SELT==0)
- {
- while(!SELT);
- Select_num++; //选择键按下一次
- Adjust=1; //进入调节模式
- BEEP=0; //关闭蜂鸣器再播报
- voice_flg=1; //语音播报标志位置一
- }
- if(Select_num==1)
- {
- LCD_Display_String(1,table4);
- LCD_Display_String(2,table3);
- write_com(0x80+0); //写 >>
- write_date(0x3e);
- write_com(0x80+1); //写 >>
- write_date(0x3e);
- Enter_num=0;
- }
- if(Select_num==2)
- {
- LCD_Display_String(1,table1);
- LCD_Display_String(2,table2);
- Select_num=0;
- Enter_num=0;
- Adjust=0;
- voice_flg=0; //语音播报标志清除
- }
- write_com(0x0c); //光标不再闪烁
- Enter_num=0;
- }
-
- if(ENTER==0)
- {
- delay1(2);
- if(ENTER==0)
- {
- while(!ENTER);
- Enter_num++;
- }
- if(Select_num==1) //设置实时时间
- {
- if(Enter_num==1)
- {
- LCD_Display_String(1,table9);
- LCD_Display_String(2,table0);
- SetTime_dispaly(0x80+8,Year);
- SetTime_dispaly(0x80+11,Month);
- SetTime_dispaly(0x80+14,Day);
- SetTime_dispaly(0x80+0x40+6,Hour);
- SetTime_dispaly(0x80+0x40+9,Minute);
- SetTime_dispaly(0x80+0x40+12,Second);
- Week_dispaly(0x80+0x40+15,Week);
- write_com(0x80+8); //光标闪烁地址,停留在年的位置上
- write_com(0x0f); //光标闪烁
- }
- if(Enter_num==2)
- {
- write_com(0x80+11); //光标闪烁地址,停留在月的位置上
- write_com(0x0f); //光标闪烁
- }
- /********************************************************/
- void Conut(void)
- {
- time=TH0*256+TL0; //计算时间,单位是us
- TH0=0; //时间清除
- TL0=0; //时间清除
- S=(time*1.7)/100; //超声波的速度按340m/s计算,由于路程往返,需要除以2,算出来是CM,
- if((S>=700)||(flag==1)) //超出测量范围或超时显示"-"
- {
- flag=0; //清除定时器T0中断标志位
- Display_ASIIC(0);
- Di_flag=1; //滴 语音
- Beep_flag=1; //蜂鸣器 标志位
- count=0;
- BEEP=0;
- }
- else
- {
- Display(0x40+0,S); //显示距离
- Di_flag=0;
- }
- }
- void StartModule() //启动模块
- {
- TX=1; //启动一次模块
- _nop_(); //启动模块,发送超声波
- _nop_(); //延时1us,至少需要发送10us
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- TX=0;
- }
- /********************************************************/
- void zd0() interrupt 1 //T0中断用来计数器溢出,超过测距范围
- {
- flag=1; //在TMOD=0x11定义下,定时器最多定时65535us,这段时间超声波走过的距离为:65535*1.7/100=1114095cm
- //显然距离明显超出范围,因此,当定时时间到仍未接收到超声波,中断溢出标志置位,则让其显示"-"
- count=0;
- BEEP=0;
- }
- /********************************************************/
- void zd3() interrupt 3 //T1中断用来扫描数码管和启动模块
- {
- TH1=(65536-2000)/256;
- TL1=(65536-2000)%256;
- timer++; //中断次数加一
- count++; //中断次数加一
- counter=S*2;
- if(voice_flg==0) //语音播报时,停止蜂鸣器
- {
- if(S<=300) //判断距离是否小于300cm
- {
- if(S>30)
- {
- if(count<S)
- BEEP=0; //启动蜂鸣器报警
- if(count>S)
- BEEP=1; //关闭蜂鸣器报警
- if(count>=counter)
- count=0;
- }
- else
- BEEP=1; //蜂鸣器报警
- }
- }
- if((S>300)||(Beep_flag==1))
- {
- BEEP=0; //关闭蜂鸣器报警
- Beep_flag=0;
- count=0;
- }
- if(count>=1000) //最大2s
- {
- count=0;
- BEEP=0;
- }
- if(timer>=300) //2ms*250=500ms,即每500ms发送一次超声波(测量周期一定要大于60ms)
- {
- timer=0; //把中断次数清除
- StartModule();
- }
- }
- /*********************************************************/
- void Timer_init(void)
- {
- TMOD=0x11; //设T0为方式1,GATE=1;
- TH0=0; //设置初值为0
- TL0=0;
- TH1=(65536-2000)/256; //2MS定时
- TL1=(65536-2000)%256;
- ET0=1; //允许T0中断
- ET1=1; //允许T1中断
- TR1=1; //开启定时器
- EA=1; //开启总中断
- }
- /*********************************************************/
- void Light_Check(void)
- {
- if(LIGHT==0) //检测到弱光
- LED=0; //开启LED
- else //检测到强光
- LED=1; //关闭LED
- }
- /***********************************************语音*****************************************************************/
- void delay(uint z) //延时函数约0.8ms
- {
- uint x,y;
- for(x=z;x>0;x--)
- for(y=80;y>0;y--);
- }
- void music(uchar k) //单独播放第K段语音
- {
- RST=1;
- delay(2);
- RST=0; //发送复位脉冲,即起始信号
- delay(2);
- while(k>0) //发送K个脉冲,播放第K段内容
- {
- DATA=1;
- delay(1);
- DATA=0;
- delay(1);
- k--;
- }
- }
- void Voice(void)
- {
- if(KEY_DIS==0)
- {
- delay(1);
- if(KEY_DIS==0)
- {
- if(Di_flag==0) //合理距离语音播报
- {
- BEEP=0; //关闭蜂鸣器再播报
- voice_flg=1; //语音播报标志位置一
-
- if(S/100==0) //百位为0
- {
- if(S%100/10==0) //十位为0
- {
- if(S%10==0) //个位为0
- {
- music(1); //播放0
- while(!BUSY); //等待播完
- }
- else
- {
- music(S%10+1); //播放个位
- while(!BUSY); //等待播完
- }
- }
- else
- {
- if(S%100/10==1) //十位为1
- {
- }
- else
- {
- music(S%100/10+1); //播放十位
- while(!BUSY); //等待播完
- }
- music(11); //播放 十
- while(!BUSY); //等待播完
- if(S%10==0) //个位为0 不播放
- {
- }
- else
- {
- music(S%10+1); //播放个位
- while(!BUSY); //等待播完
- }
- }
- }
- else
- {
- music(S/100+1); //播放百位
- while(!BUSY); //等待播完
- music(12); //播放 百
- while(!BUSY); //等待播完
- if(S%100/10==0) //十位为0
- {
- if(S%10==0) //个位为0
- {
-
- }
- else
- {
- music(1); //播放 0
- while(!BUSY); //等待播完
- }
- }
- else
- {
- music(S%100/10+1); //播放十位
- while(!BUSY); //等待播完
- music(11); //播放 十
- while(!BUSY); //等待播完
- if(S%10==0) //个位为0
- {
- }
- else
- {
- music(S%10+1); //播放个位
- while(!BUSY); //等待播完
- }
- }
- }
- voice_flg=0; //语音播报标志位清零
- }
- if(Di_flag==1) //超出测量播报“滴”提醒
- {
- music(32); //播放“滴”
- while(!BUSY); //等待播完
- }
- }
- }
- if(KEY_TIME==0) //按键按下,播放时间。
- {
- delay(5);
- if(KEY_TIME==0)
- {
- BEEP=0; //关闭蜂鸣器再播报
- voice_flg=1; //语音播报标志位置一
- music(22); //播放“现在时刻北京时间”
- while(!BUSY); //等待播完
- music(3); //2
- while(!BUSY);
- music(1); //0
- while(!BUSY);
- music(Year/10+1); //播放年“高位”
- while(!BUSY);
- music(Year%10+1); //播放年“低位”
- while(!BUSY);
- music(16); //年
- while(!BUSY);
- if((Month/10)!=0)
- {
- music(11); //播放“十”
- while(!BUSY); //等待播完
- }
- if((Month%10)!=0)
- {
- music(Month%10+1); //播放月“低位”
- while(!BUSY); //等待播完
- }
- music(17); //播放“月”
- while(!BUSY); //等待播完
- if((Day/10)==1) //日大于10,小于20
- {
- music(11); //播放“十”
- while(!BUSY); //等待播完
- }
- if((Day/10)>1) //日大于20
- {
- music(Day/10+1); //播放日“高位”
- while(!BUSY); //等待播完
- music(11); //播放“十”
- while(!BUSY); //等待播完
- }
- if((Day%10)!=0)
- {
- music(Day%10+1); //播放日“低位”
- }
- while(!BUSY); //等待播完
- music(18); //播放“日”
- while(!BUSY); //等待播完
- if((Hour/10)==1) //小时大于10,小于20
- {
- music(11); //播放“十”
- while(!BUSY); //等待播完
- }
- if((Hour/10)>1) //小时大于20
- {
- music(Hour/10+1); //播放小时“高位”
- while(!BUSY); //等待播完
- music(11); //播放“十”
- while(!BUSY); //等待播完
- }
- if(Hour==0)
- {
- music(1); //播放时“0”
- }
- while(!BUSY); //等待播完
- if((Hour%10)!=0)
- {
- music(Hour%10+1); //播放时“低位”
- }
- while(!BUSY); //等待播完
- music(13); //播放“点”
- while(!BUSY); //等待播完
- if((Minute/10)==1) //分钟大于10,小于20
- {
- music(11); //播放“十”
- while(!BUSY); //等待播完
- }
- if((Minute/10)>1) //分钟大于20
- {
- music(Minute/10+1); //播放分钟“高位”
- while(!BUSY); //等待播完
- music(11); //播放“十”
- while(!BUSY); //等待播完
- }
- if(Minute==0)
- {
- music(1); //播放分“0”
- }
- while(!BUSY); //等待播完
- if((Minute%10)!=0)
- {
- music(Minute%10+1); //播放分钟“低位”
- }
- while(!BUSY); //等待播完
- music(14); //播放“分”
- while(!BUSY); //等待播完
- voice_flg=0; //语音播报标志位清零
- }
- }
- if((Minute==0)&&(Second<3)) //分等于0表示整点,秒小于3主要是保证整点报一次就可以,否则一分钟内可能报多次
- {
- if((Hour>6)&&(Hour<22)) //早上7点~晚上7点 整点报时
- {
- BEEP=0; //关闭蜂鸣器再播报
- voice_flg=1; //语音播报标志位置一
- music(22); //播放“现在时刻北京时间”
- while(!BUSY); //等待播完
-
- if((Hour/10)==1) //小时大于10,小于20
- {
- music(11); //播放“十”
- while(!BUSY); //等待播完
- }
- if((Hour/10)>1) //小时大于20
- {
- music(Hour/10+1); //播放小时“高位”
- while(!BUSY); //等待播完
- music(11); //播放“十”
- while(!BUSY); //等待播完
- }
- if(Hour==0)
- {
- music(1); //播放时“0”
- }
- while(!BUSY); //等待播完
- if((Hour%10)!=0)
- {
- music(Hour%10+1); //播放时“低位”
- }
- while(!BUSY); //等待播完
- music(13); //播放“点”
- while(!BUSY); //等待播完
- music(26); //播放“整”
- while(!BUSY); //等待播完
- voice_flg=0; //语音播报标志位清零
- }
- }
- }
- /*********************************************************/
- void main(void)
- {
- BEEP=0; //关闭蜂鸣器
- LED=1; //关闭LED灯
- init(); //液晶初始化
- Timer_init(); //定时器初始化
- while(1)
- {
- if(Adjust==0) //非调节模式下显示时间
- {
- Time_Display(); //显示时间
- while(!RX); //当接收为0时,表示没接收到超声波,此时程序停在这里,直到开始有接收,while(1);表示停
- TR0=1; //开始有信号传入,开启定时器T0计数
- while(RX); //当接收为1时,表示接收到超声波,此时程序停在这里,直到开始接收结束,while(0);表示跳过
- TR0=0; //信号传入结束,关闭定时器T0计数
- Conut(); //计算并显示
- }
- Keyscan(); //扫描键盘
- Light_Check(); //光线检测
- Voice(); //语音播报
-
- delay1(2000);
- }
- }
复制代码
Keil5代码与Proteus8.8仿真下载:
仿真与代码.7z
(2.35 MB, 下载次数: 64)
|
-
1.jpg
(76.28 KB, 下载次数: 79)
-
2.jpg
(82.37 KB, 下载次数: 75)
-
3.jpg
(88.71 KB, 下载次数: 80)
-
4.jpg
(81.64 KB, 下载次数: 70)
-
5.jpg
(147.14 KB, 下载次数: 74)
-
11.jpg
(82.96 KB, 下载次数: 74)
-
14.jpg
(98.91 KB, 下载次数: 78)
-
15.jpg
(71.79 KB, 下载次数: 55)
评分
-
查看全部评分
|