51单片机HC-SR04超声波模块距离报警LCD1602显示距离例程

超声波模块就是那最常见的HC-SR04
为了实用,不用每次断电后再设置距离,我后来又加了eeprom存储最新距离设定功能,自己做了个小东西又怕lcd焊接麻烦,所以把lcd代码都注释掉了,基本上应该取消注释就是带lcd显示了,保证能用,目前在用没出现问题



//更新保存上次设置距离到eeprom 保存间隔为每5分钟保存一次  下次开启自动启用上次设置数据


/********************************************************************************************************************
HC-SR04 超声波测距模块可提供 2cm-400cm 的非接触式距离感测功能, 测距精度可达高到 3mm;模块包括超声波发射器、接收器与控制电路。
 基本工作原理:
(1)采用 IO 口 TRIG 触发测距,给至少 10us 的高电平信号; 
(2)模块自动发送 8个40khz 的方波,自动检测是否有信号返回; 
(3)有信号返回,通过IO口ECHO 输出一个高电平,高电平持续的时间就是超声波从发射到返回的时间。测试距离=(高电平时间*声速(340M/S))/2;
*********************************************************************************************************************/
#include<reg52.h> 
#include<intrins.h> 
///晶振12M///
#define uchar unsigned char
#define uint unsigned int
//sbit lcdrs=P3^5;          //lcd数据命令选择端  rs为L(ledrs=0)则写命令 H(ledrs=1)则写数据
//sbit lcdrw=P3^6;
//sbit lcden=P3^7;          //lcd使能   lcden=1 则写入 
sbit TRIG = P2^7; //超声波的 TRIG端 //TRIG为控制端
sbit ECHO = P2^6; //超声波的 ECHO端 //ECHO 为接收端 
sbit key1 = P2^2;  //增加距离
sbit key2 = P2^1;  //减少距离
sbit sbeep = P2^3;  //蜂鸣器

#define ReadRomCom 	0x01          //内部E2PROM读 写 擦除命令代码
#define PrgRomCom		0x02
#define EraseRomCom 0x03
#define SAVED 1                   //内部eprom是否已经保存过的标记
#define NOTSAVED 0
#define WaitTime 0x01
sfr ISP_DATA=0xe2;           //ISP寄存器地址
sfr ISP_ADDRH=0xe3;
sfr ISP_ADDRL=0xe4;
sfr ISP_CMD=0xe5;
sfr ISP_TRIG=0xe6;
sfr ISP_CONTR=0xe7;


//uchar DisplayData[3];
uchar alarm_dis=30;  //警报距离
//unsigned char code ASCII[15] = {'0','1','2','3','4','5','6','7','8','9','.','-','M'};
uchar flag = 0; //标志定时器是否溢出
uint count=0;  //计数来存档

///eeprom//
void ISP_IAP_enable()      //打开 ISP IAP功能
{
	EA=0;                 //关闭中断功能
	ISP_CONTR=ISP_CONTR & 0X18;          //0001 1000  ISP/IAP 控制寄存器复位 相当于寄存器指令清零?
	ISP_CONTR=ISP_CONTR | WaitTime;          //写入硬件延时 WaitTime 0-3(最后两位分别00 01 10 11) 0等待时间最长 3等待时间最短,过短容易出问题 10M系统时钟建议contr|10  20M系统时钟建议  |01
	ISP_CONTR=ISP_CONTR | 0x80;	              //1000 0000 最高位  ISPEN=1   ISPEN:ISP/IAP功能允许位 1:允许编程改变Flash     ISP_CONTR=0x60 01(1)(软件复位)0 0000 则可以实现从用户应用程序区软件复位到ISP程序区开始运行程序。
}			//ISP_CONTR=0x20 00(1)(软件复位)0 0000 则可以实现从ISP程序区软件复位到用户应用程序区开始运行程序。
void ISP_IAP_disable()         //关闭功能
{
	ISP_CONTR=ISP_CONTR & 0x7f;  //01111111   通过&操作符最高位清零  ISPEN=0
	ISP_TRIG=0x00;
	EA=1;                      //结束后继续开启中断功能
}
void ISP_trig()             //ISP触发执行程序
{
	ISP_IAP_enable();
	ISP_TRIG=0x46;            //ISP_IAP触发就是连续写入  0x46 0xb9
	ISP_TRIG=0xb9;
	_nop_();                //一个_nop_();函数延时一个机器周期的时间
}
uchar read_data_byte(uint byte_addr)         //读取一个字节已保存数据
{
	ISP_ADDRH=(uchar)(byte_addr>>8);           //两个字节的地址分成高低两个部分
	ISP_ADDRL=(uchar)(byte_addr&0x00ff);
	ISP_CMD = ISP_CMD & 0xf8;               // 11111000  清空低3位
	ISP_CMD = ISP_CMD | ReadRomCom;					//写入读命令
	ISP_trig();
	ISP_IAP_disable();
	return(ISP_DATA);                         //返回读到的数据
}
char check_data(uint byte_addr,uchar value)  //测试某个地址是否为自己预设的值来确定是否已经写入过数据
{
	if(read_data_byte(byte_addr)==value)
		return SAVED;
	else
		return NOTSAVED;		
}

void save_data_byte(uint byte_addr,uchar b_data)                                     //保存一个字节的数据在地址中
{
	ISP_ADDRH=(uchar)(byte_addr>>8);
	ISP_ADDRL=(uchar)(byte_addr&0x00ff);
	ISP_CMD = ISP_CMD & 0xf8;
	ISP_CMD = ISP_CMD | PrgRomCom;
	ISP_DATA=b_data;             //准备写入的数据
	ISP_trig();
	ISP_IAP_disable();
}

void erase_data(uint sector_addr)                                 //擦除某个地址所属的整个扇区
{
	uint iSectorAddr;
	iSectorAddr=(sector_addr & 0xfe00);       //1111 1110 0000 0000 &2000-2e00  0010 0000 0000 0000-0010 1110 0000 0000 扇区起始地址为左侧7位 通过&操作符清空右边9位得到扇区起始地址
	ISP_ADDRH=(uchar)(iSectorAddr>>8);        //右移8位得到高8位
	ISP_ADDRL=0x00;														//低8位为0
	ISP_CMD = ISP_CMD & 0xf8;
	ISP_CMD = ISP_CMD | EraseRomCom;
	ISP_trig();
	ISP_IAP_disable();
}
//

/*******************************************************************************
* 函 数 名         : delay
* 函数功能         : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay(uint i)
{
    while(i--); 
}
void delayms(uint z)
{
 uint x,y;
  for(x=z;x>0;x--)
  for(y=125;y>0;y--);
}
void beep()                            //蜂鸣器发声
{
	sbeep=1;
	delayms(100);
	sbeep=0;
}
void init_time()
{
    TMOD = 0x01; //选择定时器0工作 工作方式为方式1
    TH0 = 0; //装初值0
    TL0 = 0;
    TF0 = 0; //中断溢出标志位
    ET0 = 1; //开定时器中断
    EA = 1; // 开总中断
}
//void write_com(unsigned char com)
//{
//	lcdrs=0;
//	P0=com;
//	delayms(5);
//	lcden=1;
//	delayms(5);
//	lcden=0;
//}
//void lcd_init()
//{
//	lcden=0;
//	lcdrw=0;
//	write_com(0x38);//设置16X2显示,5X7点阵,8位数据接口
//	write_com(0x0c);//设置开显示,不显示光标
//	write_com(0x06);//写一个字符后地址指针加1
//	write_com(0x01);//显示清零,数据指针清零
//}
//void write_data(unsigned char date)
//{
//	lcdrs=1;
//	P0=date;
//	delayms(5);
//	lcden=1;
//	delayms(5);
//	lcden=0;
//}
//void lcd_display_char(unsigned char line,unsigned char addr, unsigned char data1)   //写数据 第几行 第几个开始  数据  data1如果是数字0-9  必须用'0'+数字计算ascii值
//{
//	write_com(0x80+(line-1)*0x40+addr);						//根据行数和add参数定位写数据位置
//	write_data(data1); 		输出字符****写完后光标自动下移一个,无需再定位地址****
//}
//void display(unsigned int num) //显示函数
//{   
//    if(num == -1|| num>999) //当超出范围 显示999
//    {
//        DisplayData[0] = ASCII[9];
//        DisplayData[1] = ASCII[9];
//        DisplayData[2] = ASCII[9];
//    }
//    else  //显示  因为测距范围为2-400cm 故3位即可 
//    {
//        DisplayData[0] = ASCII[num / 100];  //取百位        
//        DisplayData[1] = ASCII[num/10%10];//取十位        
//        DisplayData[2] = ASCII[num %10];//取个位
//    }
//    lcd_display_char(1, 1, DisplayData[0]);
//    lcd_display_char(1, 2, ASCII[10]);    //显示点
//    lcd_display_char(1, 3, DisplayData[1]);
//    lcd_display_char(1, 4, DisplayData[2]);
//    lcd_display_char(1, 5, ASCII[12]);    //显示M
//}

//void dis_alarm(uchar dis)
//{
//	lcd_display_char(2,4,'0'+dis/100);
//	lcd_display_char(2,5,ASCII[10]);
//	lcd_display_char(2,6,'0'+dis%100/10);
//	lcd_display_char(2,7,'0'+dis%10);
//	lcd_display_char(2,8,ASCII[12]);
//}
void keyscan()
{
	if(key1==0)            //第一个按键
	{
		delayms(10);
		if(key1==0)
		{
			alarm_dis++; 
			if(alarm_dis>100)     //报警距离不得超过1米
				alarm_dis=100;
		}
		while(!key1);     //等待按键弹起
	}
	if(key2==0)             //第二个按键在闪烁时增加 小时、分钟等数字  各数字不联动
	{
		delayms(10);
		if(key2==0)           //确认按下按键
		{
			if(--alarm_dis<5)     //报警距离不得小于5cm
				alarm_dis=5;
		}
		while(!key2);     //等待按键弹起
	}
}
void main()
{
    uint distance,out_TH0,out_TL0;
		uchar temp_dis; 
		sbeep=0;
		if(check_data(0x2000,66)==SAVED) //已经保存过了
		{
			alarm_dis=read_data_byte(0x2010); //读取已经保存的数据
		}
		else                      //没保存过
		{
			erase_data(0x2000);        //先擦除
			save_data_byte(0x2000,66);  //再写数据 0x2000位置写标记值66 如果有这个标记说明保存过了
			save_data_byte(0x2010,alarm_dis);
			beep();
		}
//		lcd_init();
//		lcd_display_char(2,1,'A');
//		lcd_display_char(2,2,'L');
//		lcd_display_char(2,3,':');
//		dis_alarm(alarm_dis);
    TRIG = 0; // 先给控制端初始化为0
    while(1)
    {    /*超声波传感器的使用方法:    控制口发一个10US 以上的高电平,就可以在接收口等待高电平输出。	一有输出就可以开定时器计时,当此口变为低电平时就可以读定时器的,
			此时就为此次测距的时间,方可算出距离。如此不断的周期测, 就可以达到移动测量的值了*/				
        init_time(); //初始化定时器
        flag = 0;    //置溢出标志位为0      
        TRIG = 1;      //控制口发一个10US 以上的高电平
				delay(2);
        TRIG = 0;   
        while(!ECHO);     //等待接收端出现高电平
        TR0 = 1; //启动计时器 开始计时
        while(ECHO); //等待高电平结束
        TR0 = 0; //关闭低电平
        out_TH0 = TH0; //取定时器的值
        out_TL0 = TL0;
        out_TH0 <<= 8;  //右移8位 
        distance = out_TH0 | out_TL0; //合并为16位的值  
        distance /= 54; //11.0592M   12M为/58
        if(flag == 1) //如果定时器溢出 则超出超声波测量范围
        {
//            display(-1);
            flag = 0;
						distance=100;
        }
//        else
//            display(distance);  
				if(distance>100)
					distance=100;
				keyscan();
//				dis_alarm(alarm_dis);
				if(distance<=alarm_dis&&distance<100&&distance>5)  //小于报警距离并且1米以内 报警					beep();
				delayms(500);//500ms的周期,这里不是6ms				
				count++;         //读取一次大概500ms 10次5s 120次1m 1200次10m
				if(count==120)                     //1分钟确认一次有没有修改报警距离
				{
					count=0;
					temp_dis=read_data_byte(0x2010); //读取已经保存的数据
					if(temp_dis!=alarm_dis)  //修改了数据才需要保存
					{
						erase_data(0x2000);        //先擦除
						save_data_byte(0x2000,66);  //再写数据 0x2000位置写标记值66 如果有这个标记说明保存过了
						save_data_byte(0x2010,alarm_dis);
					}								
				}
			}
}
void timer0() interrupt 1 //中断函数
{
    flag=1; //溢出标志位置1
}
···
  • 7
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值