51单片机配合超声波测距以及用1602液晶进行显示

自己写的一个小程序,给51入门的朋友们用一下,实测还行,没什么问题,但几米以上就不怎么准,主要是模块比较廉价,当练手用。注释也都比较清楚。

/*超声波测距程序,利用51单片机的外部中断,如果有时候发现不好使,建议直接使用杜邦线连接引脚*/
#include<reg52.h>
#include <intrins.h>

typedef unsigned char uchar;
typedef unsigned int uint;
/*超声波模块为HC-SR04,触发信号给一个至少10us的高电平,之后模块会自动发送8个40kHz的方波信号,
一旦模块收到回响信号,Echo引脚便输出高电平,直到接收不到。这里有个问题是关于时间计算的问题,说明书上说的是
按Echo引脚的高电平持续时间计算,但是声波的路程,不应该是从声波发出的时候就已经开始了吗?这个程序使按照
第二种计算的,如果要改动 ,请改动定时器0的启动时间。*/
sbit Echo=P3^2;//超声波模块的回响信号输出,P3^2也是51单片机外部中断0的输入端,利用下降沿来触发中断
sbit Trig=P1^4;//触发信号脚


sbit lcdrs=P1^0;//数据命令选择端,为0时写指令,为1时写数据
sbit lcdrw=P1^1;//读、写命令选择端,为0时写数据,为1时读数据,读状态时置1,其他时间都是0;
sbit lcden=P2^5;//使能端
sbit dula=P2^6;//在这段程序里没有用处
sbit wela=P2^7;
sbit STA7=P0^7;//D0~D7数据口对应P0^0~P0^7,当读液晶的数据时,STA7对应P0^7,STA7为1时表示液晶忙,无法接收数据

float dis;//距离的缓存
uchar flag;//中断标志位
char code table[]="distance:";//在规定位置显示字符
char code table1[]="cm";

void delayms(uchar x)
{
	uint i,j;
	for(i=0;i<x;i++)
	{
		for(j=0;j<110;j++);
	}
}
void nops()//延时10.9us,每个机器周期约1.09us,在晶振为11.0592MHz时
{
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
}

void distance()//计算距离的函数
{
	Trig=0;//首先将触发拉低
	nops();
	Trig=1;//给至少10us的高电平,启动模块
	nops();
	nops();
	Trig=0;//此时已经触发了模块,接下来立刻开启定时器计数
	TR0=1;//打开定时器0
	EX0=1;//打开外部中断,外部中断输入为P3^2,下降沿有效,触发中断
	delayms(1);//等待一下,一个机器周期约为1.09us,如果不等待,可能就错过计算dis,陷入死循环,永远无法得出值
	if(flag==1)//如果标志位置1,表示Echo输出下降沿,即接收结束
	{

		flag=0;
	}


}

void wait()//等待液晶空闲
{
	P0=0xff;
	do
	{
		lcdrs=0;//循环里的这些语句是读状态时的操作时序
		lcdrw=1;
		lcden=0;
		lcden=1;
	}while(STA7==1);//等液晶空闲的时候再出循环
	lcden=0;//使使能为0
}

void write_com(uchar com)//写命令程序
{
	wait();
	lcdrs=0;//使RS为0,发出的数据会被液晶当做命令而不是现实数据,这也是与写数据程序的唯一不同
	P0=com;
	lcdrw=0;//总是写的,故为0
	delayms(5);//使数据平稳,让液晶有时间读取
	lcden=1;//根据操作时序,使能要输出高脉冲,这句与后面那句共同构成一个高脉冲
	delayms(5);
	lcden=0;
}

void write_date(uchar date)//写数据程序
{
	wait();
	lcdrs=1;//注意这是写数据
	P0=date;
	lcdrw=0;
	delayms(5);
	lcden=1;
	delayms(5);
	lcden=0;
}

/*初始化函数*/
void init()
{
	wait();
	dula=0;//没用
	wela=0;
	lcden=0;
	write_com(0x38);//设置16*2显示,5*7点阵,8位数据口。这句是不变的
	write_com(0x0c);//根据操作时序设置的状态字。下面一样
	write_com(0x06);
	TMOD=0x01;//16位计数器,这里不用开启定时器中断,因为定时器中断无事可做,只要定时器里的值就行,因此无需将TF0置1
	TH0=0;//全部设为0
	TL0=0;
	IT0=1;//设置外部中断下降沿有效
	EA=1;//开总中断
}
/*显示函数*/
void display(float dis)
{
	uint bai,shi,ge,p1,p2;//分别是百位一次向下,到小数点后两位
	bai=dis/100;
	shi=(dis-bai*100)/10;
	ge=dis-bai*100-shi*10;
	p1=(dis*10)-bai*1000-shi*100-ge*10;
	p2=(dis*100)-bai*10000-shi*1000-ge*100-p1*10;
	write_date(0x30+bai);//将数字转换为字符,必须+0x30,另外,1602液晶只能接收字符型的数据
	write_date(0x30+shi);
	write_date(0x30+ge);
	write_date('.');
	write_date(0x30+p1);
	write_date(0x30+p2);

}


void main()//主函数
{
	uchar i=0;
	init();
	write_com(0x80);//设置地址,在第一行第一列显示distance:,0x80是第一行的起始地址
	while(table[i]!='\0')
	{
		write_date(table[i]);
		i++;
		delayms(5);
	}
	i=0;
	write_com(0x80+0x40+6);//设置地址,在第二行第7位显示cm,0x80+0x40是第二行的起始地址
	while(table1[i]!='\0')
	{
		write_date(table1[i]);
		i++;
		delayms(5);
	}
	while(1)
	{
		distance();
		write_com(0x80+0x40);//设置数据的显示地址,为第二行的起始位,之后在进行循环时不断刷新在这个地方显示
		display(dis);
		delayms(60);
	}


}

void ex() interrupt 0//外部中断的中断函数
{
	TR0=0;//一旦受到下降沿,立马停止定时器计数
	dis=(TH0*256+TL0)*1.09/58;//先取出定时器里的时间值,之后再将定时器置0
	flag=1;//将标志位置0
	TH0=0;
	TL0=0;
}


  • 23
    点赞
  • 160
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值