C51学习笔记:基于STC89C52的超声波测距与LCD1602显示

最近需要拿超声波写一个避障的部分,打算拿arduino来写但是板子还在路上,用51单片机来写就需要拿液晶来显示,于是就在网上找了一大堆资料来学,也参考了很多前人的代码,最后把在这过程中查阅读一部分资料整理出来,作为记录。
图片来自网络,侵删。

HC-SR04模块

HC-SR04

超声波模块使用原理

  • IO口和TRIG两个接口触发测距,给10μs高电平信号
  • 模块自动发送8个4kHz方波,自动检测有无信号返回
  • 有信号返回,通过IO口给ECHO口输出一个高电平,高电平持续时间为超声波发出到返回时间,测距距离=(高电平持续时间*声速(340M/S))/2

超声波模块时序图

超声波模块时序图

HC-SR04模块引脚

超声波模块共有四个引脚:

  • VCC 提供5v电源
  • GND 地线
  • TRIG 触发控制,信号输入
  • ECHO 回响信号输入

HC-SR04模块参数

  • 额定电压:5V
  • 静态工作电流:2mA
  • 感应角度:小于15°
  • 探测距离:2cm-400cm
  • 精度:0.3cm
  • 盲区:<2cm

误差分析

  1. 盲区:当被测物体与超声波模块探头距离足够近的时候,发射出去的声波会在超声波探头与被测物体之间来回反射,此时串口监视器返回负值.
  2. 被测物体的材质:当被测物体表面积小于0.5m²或者是表面过于粗糙,都会影响到超声波测距的返回值.
  3. 反射:(1)三角误差:大部分时候超声波和被测物体之间都存在一个夹角,所测得的距离不是一个直线距离而是带有三角误差(2)多次反射:存在于墙角,所测得距离可能是被多次反射之后才被接收到.
  4. 噪音:轮子与地面摩擦产生的高频噪音,不同超声波之间的干扰.
  5. 余震:也可以理解为抖动现象,与探头,外壳甚至电路干扰都有关系.表现为在串口监视器返回的数据中突然出现不是与其他数据产生极大偏差的数据.

解决方案

查阅资料之后网络上提供的大部分方案是算术平均滤波或者是求方差之后取方差较小区域作为返回值。
我在此使用的是算术平均滤波,通俗地讲就是多次测量取平均值。返回N个测量值之后平均,优点是能够在一定程度上让测量结果更准确,但是缺点是占用大量RAM值。

LCD1602模块

在这里插入图片描述

LCD1602引脚

编号符号说明编号符号说明
1VSS电源地9D2数据
2VDD电源正极10D3数据
3VL液晶显示偏压11D4数据
4RS数据/命令选择12D5数据
5RW读/写选择13D6数据
6E使能信号14D7数据
7D0数据15BLA背光源正极
8D1数据16BLK背光源负极
  • VDD接5V正电源
  • VL是对比度调整端,接正电源对比度最弱,接地对比度最高,对比度过高会产生“白影”,在链接一个10K电位器来调节对比度
  • RS寄存器选择,高电平选择数据寄存器,低电平选择指令寄存器
  • RW读写选择;高电平进行读操作,低电平进行写操作。(在这里我们只写不读,所以RW=0即可)
  • E为使能端,当E端从高电平变成低电平,液晶模块执行命令
  • D0-D7为双向数据线。
  • BLA/BLK为背光源正负极

LCD1602参数

显示容量:162个字符(1602)
芯片工作电压:4.5-5.5V
模块最佳工作电压:5.0V
工作电流:2.0mA
字符尺寸:2.95
4.35(W*H)mm

LCD1602指令集

编号指令RSRWD7D6D5D4D3D2D1D0
1清屏0000000001
2光标返回000000001*
3置输入模式00000001I/DS
4显示开关控制0000001DCB
5光标或字符位移000001S/CR/L**
6置功能00001DLNF*
7置字符发生存储地址0001
8置数据存储器地址001
9读忙标志或地址01BF
10写数据到CGRAM10
  1. 清显示屏,指令码01H,光标复位到00H位置
  2. 光标复位,光标返回地址00H
  3. 光标和显示模式设置:I/D:高电平右移,低电平左移;S:高电平屏幕上所有文字左移或右移,低电平无效
  4. 显示开关控制:D:高电平整体显示开,低电平整体显示关;C:高电平显示光标,低电平关闭光标;B:高电平光标闪烁,低电平光标不闪烁
  5. 光标显示与位移:S/C:高电平移动显示文字,低电平移动光标
  6. 功能设置命令 DL:高电平时为 4 位总线,低电平时为 8 位总线 N:低电平时为单行显示,高电平时双行显示 F: 低电平时显示 5x7 的点阵字符,高电平时显示 5x10 的点阵字符
  7. 设置字符发生器RAM地址
  8. DDRAM地址设置
  9. 读忙信号与光标地址:BF:高电平忙,不能接受命令和数据,低电平为不忙
  10. 写数据

LCD1602时序图

LCD1602读时序图
LCD1602写时序图

  • 左侧字母对应IO口
  • 线交叉表示电平变化,在上为高电平,在下为低电平
  • Valid Date 有效数据

LCD1602映射关系

LCD1602的映射关系

80H为第一行第一位的地址,那么第一行第二位就是0x80+1;第二行第一位就是0x80+40.

LCD1602字库

LCD1602字库

代码部分

#include"reg52.h"
#include <intrins.h>

#define uint unsigned int  //偷懒
#define uchar unsigned char

sbit rs=P2^6;     //数据指令控制口 
sbit rw=P2^5;     //读写控制口    
sbit en=P2^7;     //使能端
sbit trig=P1^0;   //超声波触发引脚  
sbit echo=P1^1;   //超声波接收引脚  

bit flag1;
uchar count;       
long int distance;      
unsigned char code table[]={"0123456789"};

void delay(uint n)  //延时函数
	
{
	uint x,y;
		for(x=n;x>0;x--)
			for(y=110;y>0;y--);
}

void delayt(uint x)  //延时函数
{
    uchar j;
    while(x-- > 0)
    {
              for(j = 0;j < 125;j++)
        {
          ;
        }
    }
}

void T0_init()    //T0中断初始化
	
{
	TMOD=0x01;    //定义计时器0工作方式为1
	TL0=0x66;     //装入初始值
	TH0=0xfc;
	ET0=1;   //开定时器0中断
	EA=1;    //开总中断
}

void comwrite(uchar com)   //写指令函数
	
{
	rs=0;   //选择指令寄存器
	rw=0;   //选择写
	P0=com; //输入指令
	delay(5);
	en=1;  //使能端打开
	en=0;  //使能端关闭
}

void datwrite(uchar dat)   //写数据函数
	
{
	rs=1;    //选择数据寄存器
	rw=0;    //选择写
	P0=dat;  //输入数据
	delay(5);
	en=1;    //使能端打开
	en=0;    //使能端关闭
}

void lcd_init()   //LCD1602初始化
	
{
	comwrite(0x38);   //设置16*2显示,8位数据端口
	comwrite(0x0c);   //打开显示屏不打开光标
	comwrite(0x06);   //显示地址递增,写入一个数据之后,显示位置右移
	comwrite(0x01);   //清屏
}

void dis()   //显示固定字符
	
{
	comwrite(0x80+0x40);   //设定输入位置
	datwrite('d');         //输入用'',不可以用""
	datwrite('i');
	datwrite('s');
	datwrite('t');
	datwrite('a');
	datwrite('n');
	datwrite('c');
	datwrite('e');
	datwrite(':');
	comwrite(0x80+0x4c);    //设定输入位置
	datwrite('.');
	comwrite(0x80+0x4e);    //设定输入位置
	datwrite('c');
	datwrite('m');
}

void trigger()   //超声波模块触发函数

{
	trig=1;      //打开超声波触发端口
	delay(1);
	trig=0;      //关闭超声波触发端口
}

void measure_init()  //测量函数初始化
	
{
	trig=0;   //触发端口低电平
	echo=0;   //接收端口低电平
	count=0;  
}

void measure()    //测距函数
	
{
	uchar a;
	uint b,c;
	TR0=1;   //打开定时器
	while(echo)   //当超声波接收打开时计时
	{
		;
	}
	TR0=0;   //关闭定时器
	a=TL0;   //a,b分别赋予中断函数高八位和第八位计时后的数值
	b=TH0;
	c=(b<<8)+a;  //高八位左移八位之后与低八位做加法运算
	c=c-0xfc66;  //减去初始值高八位和低八位
	distance=c+1000*count;  //总的时间,单位是μs
	TL0=0x66;    //重新装入初始值
	TH0=0xfc;
	delayt(30);
	distance=3453*distance/20000; //计算距离,单位是cm
}

/*
距离计算原理:所求t μs,distance=t*0.34(声速340m/s=0.34cm/μs)/2  
*/

void echodisplay(uint x)   //显示距离函数

{
	uchar q,b,s,g;
	q=x/1000;            //将测量函数测得的distance分为个位,十位,百位,千位
	b=(x/100)%10;
	s=(x/10)%10;
	g=x%10;
	comwrite(0x80+0x49);    //设定输出位置
	datwrite(table[q]);     //写入数据
	datwrite(table[b]);
	datwrite(table[s]);
	comwrite(0x80+0x4d);    //设定输入位置
	datwrite(table[g]);     //写入数据
}

void main()    //主函数
	
{
	lcd_init();    //初始化LCD
	T0_init();     //初始化T0计时器
	measure_init();//初始化超声波测量模块 
	
	while(1)       //循环
	{ 
		dis();     //显示固定字符
		trigger(); //触发超声波测距
		while(echo==0)//当接收端没有打开的时候在这里循环
		{
			;
		}
		measure();//测量距离distance
		echodisplay(distance);//显示距离distance
		measure_init();//再次初始化测距模块
		delayt(500);//测量间隔500ms
	}
}

void T_0() interrupt 1   //计时器中断函数T0
	
{
	TF0=0;      //计时器溢出中断
	TL0=0x66;   //填入初始数据
	TH0=0xfc;
	count++;    //产生一次中断就加一
    if(count==18)  //当计数到达18的时候,约1s
	{
		TR0=0;      //关闭计时器中断0
		TL0=0x66;   //重新装入初始值
		TH0=0xfc;
		count=0;    //把count清零
	}
}
  • 27
    点赞
  • 190
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值