超声波测距(数码管显示)

目录

  • 前言
  • 一、超声波测距的概念
  • 二、超声波测距模块
    • 1.引脚说明
    • 2.工作原理
  • 三、编程实现
    • 1.超声波测距函数
    • 2.数码管显示函数
    • 3.定时器0配置函数
    • 4.main.c
  • 总结


前言

  本次编程实验以IAP15F2K61S2为单片机主控芯片,其编程使用与STC15F2K60S2完全相同,头文件为STC15F2K60S2.H。若用于51系列单片机,以reg52.h为头文件,则读者需将程序中可能涉及的定时器初始化程序和LED亮灭程序和数码管显示程序,根据自身所用单片机原理图和手册进行修改。


一、超声波测距的概念

  超声波测距,即使用超声波测量物体间的距离。超声波从测量点发射,接触到被测物体后,产生回波,测量超声波从发出到接收到回波的时间,乘以声速,再除以2,就可以得到测量距离。由于超声波指向性强,能量消耗缓慢,在介质中传播的距离较远,因而超声波经常用于距离的测量,并且在测量精度方面能达到工业实用的要求,在移动机器人研制上得到了广泛的应用。

二、超声波测距模块

1.引脚说明

  常用的超声波模块是4针的HC-SR04,四个引脚分别为:+VCC,Trig(触发端),Echo(接收端),GND。如下图所示:
超声波测距(数码管显示)_第1张图片

2.工作原理

  将HC-SR04模块与单片机连接后,给予Trig端高电平触发测距,高电平信号至少保持10us;之后模块自动发送 8 个 40khz 的方波,自动检测是否有信号返回;方波发射后,Echo端自动置高电平,一旦检测到有回波返回,Echo端自动置低电平。从而,Echo端高电平持续的时间就是超声波从发射到返回的时间。测量距离=(高电平时间*声速(340M/S))/2。时序图如下:
超声波测距(数码管显示)_第2张图片
  为了防止发射信号对回波信号产生干扰,每次测量时间间隔应大于60ms。

三、编程实现

1.超声波测距函数

  使用定时器1来计算Echo端高电平时间,代码如下:
  ultra.h

#ifndef __ULTRA_H
#define __ULTRA_H

#include 
#include 

sbit trig=P1^0;
sbit echo=P1^1;

void Timer1_Init(void);		//定时器1初始化
void ultra_start();  			//发射信号
unsigned int ultra_measure();   //Echo端高电平时间计算函数

#endif

  ultra.c

#include "ultra.h"

void Delay10us(void)	//@11.0592MHz
{
	unsigned char data i;

	_nop_();
	i = 25;
	while (--i);
}

void Timer1_Init(void)		//@11.0592MHz
{
	AUXR &= 0xBF;			//定时器时钟12T模式
	TMOD &= 0x0F;			//设置定时器模式
	TMOD |= 0x10;			//设置定时器模式
	TL1 = 0x00;				//设置定时初始值
	TH1 = 0x00;				//设置定时初始值
	TF1 = 0;				//清除TF1标志
	
	TR1 = 0;				//定时器1停止计时
}

void ultra_start()   //发射超声波
{
	trig=0;
	trig=1;
	Delay10us();
	trig=0;	
}

/*
  *  @brief     Echo端高电平时间测量函数
  *  @param    
  *  @reval     ultra_time:Echo端高电平时间持续时间
  *  @note:    
*/

unsigned int ultra_measure()
{
	unsigned int ultra_time=0;
	TL1 = 0x00;				//设置定时初始值
	TH1 = 0x00;				//设置定时初始值
		
	ultra_start();   //发射超声波
	while(!echo);    //等待Echo端置高电平
	TR1=1;         //定时器1开始计时
	while(echo&&(!TF1));  //如果检测到回波或定时器溢出
	TR1=0;       //停止计时
	TF1=0;
	ultra_time=TH1*256+TL1;  //TH1为高8位,TL1为低8位,因此需将TH1左移8位,即乘以256

	return ultra_time;   //返回Echo端高电平时间持续时间
}

2.数码管显示函数

  注意到ultra_measure()里面存在while()循环,一旦循环时间过长,使用delay()函数来显示数码管的方式就容易出现闪烁,因此采取定时器扫描数码管的方式。详情见定时器扫描8位数码管(含滚动显示)
  smg.h

#ifndef __SMG_H
#define __SMG_H

#include 

#define outputp0(y,x)  P0=x,P2&=0x1f,P2|=y,P2&=0x1f;//P2高三位用于选择数据输出通道。P0用于输出数据

extern unsigned char Seg_sy[];  //声明外部变量,使main.c文件可以调用
void showbit(unsigned char pos,dat,dot);
void showloop();

#endif

  smg.c

#include "smg.h"

unsigned char Seg_sy[]={11,11,11,11,11,11,11,11}; //数码管段选索引数组
unsigned char code Seg_Table[]={   //共阳段码表
0xc0,
0xf9,
0xa4,
0xb0,
0x99,
0x92,
0x82,
0xf8,
0x80,
0x90,
0xbf,   //"-"
0xff    //8段全灭
};

/*
  *  @brief     1位数码管显示函数
  *  @param     pos:位选;dat:显示数字;dot:小数点选择位,1有0无
  *  @reval      
  *  @note:    
*/

void showbit(unsigned char pos,dat,dot)
{
	outputp0(0xc0,0x01<<pos);       //位选
	outputp0(0xe0,Seg_Table[11]);   //段选,这两行用于消影
	
	outputp0(0xc0,0x01<<pos);     //位选
	outputp0(0xe0,Seg_Table[dat]+0x80*dot);	  //段选
}

/*
  *  @brief     8位数码管显示函数
  *  @param     
  *  @reval      
  *  @note:    
*/

void showloop()
{
	static unsigned char sy=0;  //sy用于选择位选和位选对应的段选
	if(sy==2)
		showbit(sy,Seg_sy[sy],1);  //数码管显示1位小数,第二位数码管显示测量距离个位数,第三位开始为小数,因此第二位数码管显示小数点
	else
		showbit(sy,Seg_sy[sy],0);
    sy++;   //每执行一次,sy加一,从而下次执行时刷新下一位数码管
	sy%=8;  //8位数码管,因此sy不超过8
}

3.定时器0配置函数

  定时器0用于扫描数码管。
  timer0.h

#ifndef __TIMER0_H
#define __TIMER0_H

#include 

void Timer0_Init(void);		//2毫秒@11.0592MHz

#endif

  timer0.c

#include "timer0.h"

void Timer0_Init(void)		//2毫秒@11.0592MHz,用于扫描数码管
{
	AUXR &= 0x7F;			//定时器时钟12T模式
	TMOD &= 0xF0;			//设置定时器模式
	TMOD |= 0x01;			//设置定时器模式
	TL0 = 0xCD;				//设置定时初始值
	TH0 = 0xF8;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
	
	ET0 = 1;				//使能定时器0中断
	EA=1;
}

4.main.c

  main.c

#include 
#include "math.h"
#include "smg.h"
#include "ultra.h"
#include "timer0.h"
typedef unsigned char u8;

long int distance=0;  //测量距离
int tultra=0;  //测量周期

void test()     //测试程序                                                                                                                  
{		
	if(tultra>=50)  //每100ms测量一次
	{
		u8 i=0;
		tultra=0;   //清零
		distance=ultra_measure()*0.17;	  //ultra_measure()*0.017为初始距离(CM),为了便于显示1位小数,将其乘以10
		if(distance>=40&&distance<=3500)   //如果在测量范围之内(4cm-350cm)
		{
			for(i=0;i<4;i++)
				Seg_sy[i]=(distance/(long int)pow(10,3-i))%10;  //显示测量距离,1位小数
		}
		else if(distance<40||distance>3500)  //如果超出测量范围
		{
			for(i=0;i<4;i++)
				Seg_sy[i]=10;  //显示"---.-"
		}
	}
}
void main()
{
	outputp0(0x80,0xff);  //关闭8个LED
	outputp0(0xa0,0x00);  //关闭蜂鸣器继电器等
	
	Timer0_Init();
	Timer1_Init();

	while(1)
	{
		test();		
	}
}

void Timer0_Isr(void) interrupt 1
{
	TL0 = 0xCD;				//设置定时初始值
	TH0 = 0xF8;				//设置定时初始值
	
	showloop();	//每隔2ms扫描一次数码管
	tultra++; //测量周期+1
}

总结

  实际测量距离时,由于硬件性能和测试场地限制等,测量距离容易出现波动,需要稳定测量一段时间。当然,也可以通过短时间内多次测量取平均值的方法来减小误差,请读者自行思考。

  有任何问题和补充,欢迎评论区交流。

你可能感兴趣的:(单片机,单片机,蓝桥杯,c语言,mcu)