找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1201|回复: 0
打印 上一主题 下一主题
收起左侧

超声波测距

[复制链接]
跳转到指定楼层
楼主
ID:313883 发表于 2018-8-4 10:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
3方向的超声波测距程序,基于stc89c5
但是只有上电的第一次能测距
后面就显示超出测距范围了,一直报警
/*
* 程序:基于HC-SR04 的超声波测距系统
* 单片机型号: STC90C516 12MHz
* 说明:按下K1 键后,指示灯点亮,开始连续进行7 次超声波测距,每次测距间隔80ms ,
* 完成后对7 次结果排序并将最大的2 个数值和最小的2 个数值去除,对剩余的
* 3 个数值取平均值。完成后指示灯灭,输出结果到LCD1602 上。测量超出范围则发出报警
声。
* 使用两个IO 端口控制HC-SR04 触发信号输入和回响信号输出,
* 以及一个T0 定时器用于时间计数。
* 使用DS18B20 测量环境温度,声速公式: V=334.1m/s+Temperature*0.61 ,
* 单片机晶振为12Mhz(11.953M) ,计数时为T=1us
* 计算公式: S=(334.1m/s+Temperature*0.61)*N*T/2 ,N 为计数值=TH0*256+TL0
*/
/* 包含头文件*/
#include <reg51.h>
#include <intrins.h>
#define Delay4us(){_nop_();_nop_();_nop_();_nop_();}
/* 宏定义*/
#define uchar unsigned char // 无符号8 位
#define uint unsigned int // 无符号16 位
#define ulong unsigned long // 无符号32 位
/* 全局变量定义*/
sbit K1=P1^0; // 按下K1 后,开始测距
sbit LEDRed=P1^1; // 测距指示灯,亮表示正在测距,灭表示测距完成
sbit BEEP=P1^5; // 报警测量超出范围
sbit Trig=P1^7; //HC-SR04 触发信号输入
sbit Echo=P1^6; //HC-SR04 回响信号输出
float xdata DistanceValue=0.0; // 测量的距离值
float xdata SPEEDSOUND; // 声速
float xdata XTALTIME; // 单片机计数周期
uchar xdata stringBuf[6]; // 数值转字符串缓冲
//LCD1602 提示信息
uchar code Prompts[][16]=
{
{"Measure Distance"}, // 测量距离
{"- Out of Range -"}, // 超出测量范围
{"MAX range 400cm "}, // 测距最大值400cm
{"MIN range 2cm "}, // 测距最小值2cm
{" "}, // 清屏
{" Press K1 Start "} // 按键开始测量
};
uchar xdata DistanceText[]="Range: ";// 测量结果字符串
uchar xdata TemperatureText[]="Temperature: ";// 测量温度值
/* 外部函数声明*/
extern void LCD_Initialize();
extern void LCD_Display_String(uchar *, uchar);
extern void ReadTemperatureFromDS18B20();
extern int xdata CurTempInteger;
// 毫秒延时函数
void DelayMS(uint ms);
//20 微秒延时函数
void Delay20us();
//HCSR04 初始化
void HCSR04_Initialize();
// 测量距离
float MeasuringDistance();
// 测距的数值排序求平均
float DistanceStatistics();
// 输出距离值到LCD1602 上
void DisplayDistanceValue(float dat);
// 将无符号的整数转成字符串,返回字符串长度,不包括'\0' 结束符
uchar UnsigedIntToString(uint value);
// 蜂鸣器
void Beep(uchar time);
// 显示温度值
void DisplayTemperatureValue();
// 测量距离
float MeasuringDistance()
{
// 最大定时时间约65ms
TH0=0;
TL0=0;
// 生成20us 的脉冲宽度的触发信号
Trig=1;
Delay20us();
Trig=0;
// 等待回响信号变高电平
while(!Echo);
TR0=1; // 启动定时器0
// 等待回响信号变低电平
while(Echo);
TR0=0; // 关闭定时器0
// 返回距离值(mm)
return (SPEEDSOUND*XTALTIME*((float)TH0*256+(float)TL0))/2000;
}
//HCSR04 初始化
void HCSR04_Initialize()
{
// 计算单片机计数周期晶振=11.953M 单位us
XTALTIME=12/11.953;
// 温度25 度时声速的值
SPEEDSOUND=334.1+25*0.61;
Trig=0;
Echo=0;
TMOD=0x01;
}
// 输出距离值到LCD1602 上
void DisplayDistanceValue(float dat)
{
uchar i=0,j=0,len;
uint value;
value=(uint)dat;
// 范围检查大于4000mm 和小于20mm 都为超出测量范围
if(value>4000)
{
LCD_Display_String(Prompts[1],0x00);
LCD_Display_String(Prompts[2],0x40);
Beep(2);
}
else if(value<20)
{
LCD_Display_String(Prompts[1],0x00);
LCD_Display_String(Prompts[3],0x40);
Beep(2);
}
else
{
// 将数值转换成字符串
len=UnsigedIntToString(value);
// 保留1 位小数
while(stringBuf[i]!='\0')
{
if(len-j==1)
{
DistanceText[6+j]='.';
j++;
}else
{
DistanceText[6+j]=stringBuf[i];
i++;
j++;
}
}
DistanceText[6+j]='c';
j++;
DistanceText[6+j]='m';
i=7+j;
// 剩余位置补空格
while(i<16)
{
DistanceText[i]=' ';
i++;
}
//LCD_Display_String(Prompts[0],0x00);
LCD_Display_String(DistanceText,0x40);
}
}
// 显示温度值
void DisplayTemperatureValue()
{
TemperatureText[13]=CurTempInteger/10+'0';
TemperatureText[14]=CurTempInteger%10+'0';
TemperatureText[15]='C';
LCD_Display_String(TemperatureText,0x00);
}
// 将无符号的整数转成字符串,返回字符串长度
uchar UnsigedIntToString(uint value)
{
uchar i=0,t,length;
// 从个位开始转换
do
{
stringBuf[i]='0'+value%10;
value=value/10;
i++;
}while(value!=0);
length=i;
// 将字符串颠倒顺序
for(i=0;i<(length/2);i++)
{
t=stringBuf[i];
stringBuf[i]=stringBuf[length-i-1];
stringBuf[length-i-1]=t;
}
stringBuf[length]='\0';
return length;
}
// 蜂鸣器
void Beep(uchar time)
{
uchar i;
for(i=0;i<100;i++)
{
BEEP=!BEEP;
DelayMS(time);
}
BEEP=0;
DelayMS(100);
}
// 延时函数毫秒 @12.000MHz
void DelayMS(uint ms)
{
uchar i, j;
while(ms--)
{
_nop_();
i = 2;
j = 239;
do
{
while (--j);
}while (--i);
}
}
// 延时函数 20 微秒 @12.000MHz
void Delay20us()
{
uchar i;
_nop_();
i = 7;
while (--i);
}
// 定时器0 中断
void Timer0() interrupt 1
{
}
//DS18B20 代码:
/*----------------------------------------------
* 程序功能: DS18B20 温度检测程序
* 单片机型号: STC89C52 12MHz
* 晶振: 12Mhz
------------------------------------------------*/
/* 包含头文件*/
#include <reg51.h>
#include <intrins.h>
/* 宏定义*/
#define uchar unsigned char // 无符号8 位
#define uint unsigned int // 无符号16 位
// 定义DS18B20 端口DS18B20_DQ
sbit DS18B20_DQ = P3^7;
// 当前采集的温度值整数部分
int xdata CurTempInteger;
// 当前采集的温度值小数部分
int xdata CurTempDecimal;
/*----------------------------------------------------------------
* 功能:延时函数 STC89C52 @12MHz 12T 模式
* 参数:无
* 返回:无
----------------------------------------------------------------*/
void Delayus(uint count)
{
while (--count);
}
/*----------------------------------------------------------------
* 功能: DS18B20 复位及状态检测
* 参数:无
* 返回: 0 或1,1 表示未准备好, 0 表示准备好
----------------------------------------------------------------*/
uchar Reset_DS18B20()
{
uchar status;
DS18B20_DQ=1;
Delayus(1);
// 开始复位过程
DS18B20_DQ=0; // 数据线拉低
Delayus(100); // 延时480us-960us
DS18B20_DQ=1; // 数据线拉高
Delayus(10); // 延时15us-60us
status=DS18B20_DQ; // 读取数据线上的状态
Delayus(120);
return status;
}
/*----------------------------------------------------------------
* 功能:写一字节到DS18B20 中
* 参数: dat= 数据
* 返回:无
----------------------------------------------------------------*/
void WriteByteToDS18B20(uchar dat)
{
uchar i;
for(i=0;i<8;i++)
{
DS18B20_DQ=0;
DS18B20_DQ=dat&0x01; // 发送1 位数据
Delayus(15); // 延时60us 以上
DS18B20_DQ=1; // 释放总线,等待总线恢复
dat>>=1; // 准备下一位数据
}
}
/*----------------------------------------------------------------
* 功能:从DS18B20 中读一字节
* 参数:无
* 返回:读取的数据
----------------------------------------------------------------*/
uchar ReadByteFromDS18B20()
{
uchar i,dat=0;
for(i=0;i<8;i++)
{
DS18B20_DQ=0; // 拉低总线,产生读信号
dat>>=1;
DS18B20_DQ=1; // 释放总线,准备读1 位数据
Delayus(2); // 延时4us
if(DS18B20_DQ) dat|=0x80; // 合并每位数据
Delayus(15); // 延时60us
DS18B20_DQ=1; // 拉高总线,准备读下1 位数据
}
return dat;
}
/*----------------------------------------------------------------
* 功能:读取温度值并转换成有符号的数值形式
* 参数:无
* 返回:无
----------------------------------------------------------------*/
void ReadTemperatureFromDS18B20()
{
uchar flag=0;// 正负符号标志
// 存储当前采集的温度值
uchar TempValue[]={0,0};
if(Reset_DS18B20())
{
CurTempInteger=255;
CurTempDecimal=0;
}
else
{
WriteByteToDS18B20(0xCC);// 跳过ROM命令
WriteByteToDS18B20(0x44);// 温度转换命令
Reset_DS18B20();// 复位
WriteByteToDS18B20(0xCC);// 跳过ROM命令
WriteByteToDS18B20(0xBE);// 读取温度暂存器命令
TempValue[0]=ReadByteFromDS18B20();// 先读低字节温度值
TempValue[1]=ReadByteFromDS18B20();// 后读高字节温度值
Reset_DS18B20();// 复位
// 计算温度值
// 先进行正温度与负温度判断,高5 位全为1 (0xF8 )则为负数
if((TempValue[1]&0xF8)==0xF8)
{
// 负温度计算:取反加1,低字节为0 时,高字节取反加1 ,否则不需要。
TempValue[1]=~TempValue[1];
TempValue[0]=~TempValue[0]+1;
if(TempValue[0]==0x00) TempValue[1]++;
flag=1;// 负数标志
}
// 将温度值分为整数和小数两部分存储( 默认为12 位精度)
CurTempInteger=((TempValue[1]&0x07)<<4)|((TempValue[0]&0xF0)>>4);
if(flag) CurTempInteger=-CurTempInteger;
CurTempDecimal=(TempValue[0]&0x0F)*625;
}
}
// LCD1602 程序代码:
/*----------------------------------------------
* 程序功能: 1602 液晶显示程序
* 单片机型号: STC90C160 12MHz
------------------------------------------------*/
/*1602 液晶显示器控制端口分配,数据使用P0 端口*/
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
/*--------------------------------------------
* 功能:毫秒级延时函数
* 参数: ms=毫秒数值
* 返回:无
--------------------------------------------*/
void LCDDelay(uint ms)
{
uchar i, j;
while(ms--)
{
_nop_();
i = 2;
j = 239;
do
{
while (--j);
}while (--i);
}
}
/*--------------------------------------------
* 功能: 1602 液晶忙状态检测
* 参数:无
* 返回: 0 或1,1 表示状态忙, 0 表示状态闲
--------------------------------------------*/
bit LCD_Busy_Check()
{
bit result;
LCD_RS=0; LCD_RW=1; LCD_EN=1;
Delay4us();
result=(bit)(P0&0x80);
LCD_EN=0;
return result;
}
/*--------------------------------------------
* 功能: 1602 液晶写指令
* 参数: cmd=1602LCD 指令
* 返回:无
--------------------------------------------*/
void Write_LCD_Command(uchar cmd)
{
while(LCD_Busy_Check());
LCD_RS=0; LCD_RW=0; LCD_EN=0; _nop_(); _nop_();
P0=cmd; Delay4us();
LCD_EN=1; Delay4us(); LCD_EN=0;
}
/*--------------------------------------------
* 功能: 1602 液晶写数据
* 参数: dat= 一个字节数据
* 返回:无
--------------------------------------------*/
void Write_LCD_Data(uchar dat)
{
while(LCD_Busy_Check());
LCD_RS=1;LCD_RW=0;LCD_EN=0;
P0=dat;Delay4us();
LCD_EN=1;Delay4us();LCD_EN=0;
}
/*--------------------------------------------
* 功能:设置1602 液晶显示位置
* 参数: pos= 位置地址值
* 返回:无
--------------------------------------------*/
void LCD_Set_POS(uchar pos)
{
Write_LCD_Command(pos|0x80);
}
/*--------------------------------------------
* 功能: 1602 液晶初始化
* 参数:无
* 返回:无
--------------------------------------------*/
void LCD_Initialize()
{
Write_LCD_Command(0x01); LCDDelay(5);
Write_LCD_Command(0x38); LCDDelay(5);
Write_LCD_Command(0x0C); LCDDelay(5);
Write_LCD_Command(0x06); LCDDelay(5);
}
/*--------------------------------------------
* 功能:在1602 液晶指定的行上显示字符串
( 共两行,一行16 个字符)
* 参数: *str= 字符串指针,
LineNo= 行首地址( 第一行0x00 ,第二行0x40)
* 返回:无
--------------------------------------------*/
void LCD_Display_String(uchar *str, uchar LineNo)
{
uchar k;
LCD_Set_POS(LineNo);
for(k=0;k<16;k++)
{
Write_LCD_Data(str[k]);
}
}
/*--------------------------------------------
* 功能:在1602 液晶指定位置显示一个字符
( 共两行,一行16 个字符)
* 参数: Dat= 一个字符,
X= 列位置(0-15)
Y=行位置(0 ,1)
* 返回:无
--------------------------------------------*/
void LCD_Display_OneChar(uchar Dat, uchar X, uchar Y)
{
Y &= 0x01; // 限制Y 不能大于1(2 行, 0-1)
X &= 0x0F; // 限制X 不能大于15(16 个字符, 0-15)
if(Y) {X |= 0x40;} // 当要在第二行显示时地址码+0x40;
X |= 0x80; // 算出指令码
Write_LCD_Command(X);
Write_LCD_Data(Dat);
}
/* 主函数*/
void main()
{
LCD_Initialize();//1602 初始化
LCD_Display_String(Prompts[0],0x00);
LCD_Display_String(Prompts[5],0x40);
ReadTemperatureFromDS18B20();// 测温度
HCSR04_Initialize();//HC-SR04 初始化
while(1)
{
if(K1==0)
{
DelayMS(5);
if(K1==0)
{
Beep(1);
while(K1==0);
LEDRed=0;
ReadTemperatureFromDS18B20();// 测温度
DisplayTemperatureValue();
if(CurTempInteger<14)
CurTempInteger=14;
else if(CurTempInteger>26)
CurTempInteger=26;
SPEEDSOUND=334.1+CurTempInteger*0.61;// 计算声速
DistanceValue=DistanceStatistics();// 测距并返回距离值
DisplayDistanceValue(DistanceValue);// 显示距离值
LEDRed=1;
}
}
}
}
// 测距的数值排序求平均
float DistanceStatistics()
{
uchar i,j;
float disData[7],t;
// 连续测距
for(i=0;i<7;i++)
{
disData[i]=MeasuringDistance();
DelayMS(80);
}
// 排序
for(j=0;j<=6;j++)
{
for(i=0;i<7-j;i++)
{
if(disData[i]>disData[i+1])
{
t=disData[i];
disData[i]=disData[i+1];
disData[i+1]=t;
}
}
}
return (disData[2]+disData[3]+disData[4])/3;
}

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表