(1)用单片机实现;
(2)用C语言编程;
(3)硬件电路板布局合理;
(4)用数码管显示器显示;
(5)距离测量范围是:10cm----100cm;
(6)误差小于8%;
(7)超过测距量程并报警;
(8)通过按键调节测距范围。
电烙铁、万用表、STC烧写器、5V电源、PC机、Keil uVision5、AltiumDesigner 13.3.4、stc-isp程序烧写软件(可到STC官方网站下载:http://www.stcmcu.com/index.htm)等。
如图1所示,是单片机最小系统电路图。学过51单片机的人都知道单片机最小系统包括:晶振电路、复位电路、单片机芯片和电源,这里作大概讲解即可,想了解详细电路原理请另查资料。晶振电路是为单片机提供时钟源;复位电路是为了让单片机从头到尾重新执行一遍程序,为了方便调试笔者采用按键复位方式;单片机芯片采用国产的STC单片机,型号为STC89C52RC(注:可以选择其他型号,譬如STC89、STC90、STC12等等),代码是完全兼容的,不必过多地担心,只是单片机内部资源不同而已;电源电路很简单,采用普通5V供电即可,为了方便调试建议加一个总电源开关,开关类型根据个人喜欢随便选。
图1 单片机最小系统电路图
图1中的J2是程序下载接口,接法很简单,单片机的串口P3.0和P3.1口引出即可;RP1是P0口的上拉排阻。
头文件common.h:
#ifndef __COMMON_H__
#define __COMMON_H__
//头文件包含
#include <reg52.h> //提供单片机寄存器地址
#include <intrins.h> //提供_nop_()函数
#endif
程序如下:
头文件dig.h
#ifndef __DIG_H__
#define __DIG_H__
//头文件包含
#include "common.h"
//管脚定义
#define digDuan P0 //数码管段控制端口,高电平有效
sbit digWei1 = P2^4; //第1位数码管控制端口,低电平有效
sbit digWei2 = P2^5; //第2位数码管控制端口,低电平有效
sbit digWei3 = P2^6; //第3位数码管控制端口,低电平有效
sbit digWei4 = P2^7; //第4位数码管控制端口,低电平有效
//函数声明
void dig_scan(unsigned int *dat); //数码管扫描
#endif
源文件dis.c
#include "dig.h"
/*
* 数码管扫描
* *dat:以数组的方式传值,使用的时候直接填入数组名即可
*/
void dig_scan(unsigned int *dat)
{
static unsigned char i = 0; //循环变量
/* 数码管扫描处理 */
digDuan = 0x00; //消隐
digWei1 = 1;
digWei2 = 1;
digWei3 = 1;
digWei4 = 1;
switch(i)
{
case 0:
{
digWei1 = 0; //选通第1位数码管
digDuan = dat[0]; //显示第1位数码管的内容
break;
}
case 1:
{
digWei2 = 0; //选通第2位数码管
digDuan = dat[1]; //显示第2位数码管的内容
break;
}
case 2:
{
digWei3 = 0; //选通第3位数码管
digDuan = dat[2] + 0x80; //显示第3位数码管的内容,顺便也显示小数点
break;
}
case 3:
{
digWei4 = 0; //选通第4位数码管
digDuan = dat[3]; //显示第4位数码管的内容
break;
}
default: break;
}
++i; //数码管位选变量循环
if(i >= 4)
i = 0;
}
程序如下:
头文件HC-SR04.h:
#ifndef __HC_SR04_H__
#define __HC_SR04_H__
#include "common.h"
//引脚定义
sbit Ttig = P1^3; //触发信号控制端口
sbit Echo = P1^4; //回响信号接收端口
//函数声明
void start_ranging(void);
#endif
源程序文件HC-SR04.c:
#include "HC-SR04.h"
/* 启动超声波程序 */
void start_ranging(void)
{
Ttig = 1; //启动一次模块
_nop_(); //延时一段时间,至少10us以上
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
Ttig = 0;
}
程序如下:
头文件key.c:
#ifndef __KEY_H__
#define __KEY_H__
//头文件包含
#include "common.h"
//管脚定义
sbit KEY1 = P1^0; //设置键
sbit KEY2 = P1^1; //加键
sbit KEY3 = P1^2; //减键
//函数声明
unsigned char key_scan(void); //按键扫描
#endif
源程序文件key.c:
#include "key.h"
/* 按键延时函数,单位:ms */
static void key_delayms(unsigned int ms)
{
unsigned char a,b,c;
while(--ms)
{
for(c=1;c>0;c--)
for(b=142;b>0;b--)
for(a=2;a>0;a--);
}
}
/* 按键扫描 */
unsigned char key_scan(void)
{
if(KEY1 == 0) //如果按键1按下
{
key_delayms(10); //延时10ms,去除按键抖动
if(KEY1 == 0) //再判断一次按键按下
{
return 1; //输出键值1
}
}
if(KEY2 == 0) //如果按键2按下
{
key_delayms(10); //延时10ms,去除按键抖动
if(KEY2 == 0) //再判断一次按键按下
{
return 2; //输出键值2
}
}
if(KEY3 == 0) //如果按键3按下
{
key_delayms(10); //延时10ms,去除按键抖动
if(KEY3 == 0) //再判断一次按键按下
{
return 3; //输出键值3
}
}
return 0; //如果没有按键按下,则输出0
}
程序如下:
头文件
在这里插入代码片
源程序文件
在这里插入代码片
程序如下:
头文件timer.h
#ifndef __TIMER_H__
#define __TIMER_H__
//头文件包含
#include "common.h"
#include "dig.h"
//函数声明
void InitTimer0(void); //定时器0初始化
#endif
源程序文件timer.c:
#include "timer.h"
extern unsigned int dis_buf[4];
/* 定时器0初始化,定时1ms */
void InitTimer0(void)
{
TMOD = 0x11; //配置定时器0为工作方式1,定时器1位工作方式1
TH0 = 0;
TL0 = 0;
TH1 = 0x0EC; //定时5ms
TL1 = 0x78;
EA = 1; //打开总中断
ET1 = 1; //打开定时器0中断
TR1 = 1; //打开定时器1
TR0 = 0; //打开定时器0
}
/* 定时器0中断服务函数,5ms进一次本中断,用于数码管扫描 */
void Timer1Interrupt(void) interrupt 3
{
TH1 = 0x0EC; //定时5ms,重新赋值
TL1 = 0x78;
dig_scan(dis_buf); //数码管显示温度
}
程序如下:
头文件tack.h:
#ifndef __TASK_H__
#define __TASK_H__
#include "dig.h" //添加数码管显示程序
#include "HC-SR04.h"
#include "timer.h"
#include "key.h"
//引脚定义
sbit BEEP = P3^7; //蜂鸣器控制端口,低电平有效,无源蜂鸣器
//函数声明
void delayms(unsigned int ms); //延时,单位:ms
void count(void); //计算距离
void dis_distance(void); //显示距离
void key_fuction(void);
#endif
源程序文件tack.c:
#include "task.h"
//全局变量定义
//段码表,适用于共阴数码管
unsigned int code duan_ma[16] =
{
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f, //0~9
0x77,0x7c,0x39,0x5e,0x79,0x71 //A~F
};
unsigned int dis_buf[4]; //显示缓存
unsigned int S = 0; //距离缓存变量
unsigned long time = 0; //时间缓存变量
unsigned int S_H = 400, S_L = 10; //S_H:报警上限值,默认400cm,S_L:报警下限值,默认10cm
/*
* 延时,单位:ms
*/
void delayms(unsigned int ms)
{
unsigned char a,b,c;
while(--ms)
{
for(c=1;c>0;c--)
for(b=142;b>0;b--)
for(a=2;a>0;a--);
}
}
/*
* 计算测距
*/
void count(void)
{
start_ranging(); //启动一次超声波
while(!Echo); //等待超声波模块输出IO拉高
TR0 = 1; //开启定时器计时
while(Echo); //等待超声波模块输出IO拉低
TR0 = 0; //关闭定时器计时
/* 计算距离 */
time = TH0 * 256 + TL0; //得到的时间,专业写法应该是这样:time = TH0 << 8 | TL0
TH0 = 0; //定时计数器清零
TL0 = 0;
S = (time * 1.7) / 100; //算出来是CM
}
/*
* 显示距离
*/
void dis_distance(void)
{
dis_buf[3] = 0x00;
dis_buf[2] = duan_ma[S / 100 %10]; //取出百位数,并在数码管的第3位显示,从右往左数
dis_buf[1] = duan_ma[S / 10 %10]; //取出十位数,并在数码管的第2位显示,从右往左数
dis_buf[0] = duan_ma[S % 10]; //取出个位数,并在数码管的第1位显示,从右往左数
}
/*
* 设置报警功能
*/
void key_fuction(void)
{
unsigned char mode_flag = 1; //模式切换标记位,1:设置上限值,2:设置下限值,3:退出
while(1)
{
if(key_scan() == 1) //功能切换键
{
BEEP = 0; //按键提示音
delayms(50);
BEEP = 1;
delayms(100);
++mode_flag;
if(mode_flag >= 3) //如果满3了,则退出
break;
}
if(key_scan() == 2) //如果加键按下
{
BEEP = 0; //按键提示音
delayms(50);
BEEP = 1;
delayms(100);
if(mode_flag == 1) //设置上限值
{
++S_H;
if(S_H > 400)
S_H = 2;
}
if(mode_flag == 2) //设置下限值
{
++S_L;
if(S_L > 400)
S_L = 2;
}
}
if(key_scan() == 3) //如果减键按下
{
BEEP = 0; //按键提示音
delayms(50);
BEEP = 1;
delayms(100);
if(mode_flag == 1) //设置上限值
{
--S_H;
if(S_H < 2)
S_H = 400;
}
if(mode_flag == 2) //设置下限值
{
--S_L;
if(S_L < 2)
S_L = 400;
}
}
/*------------- 报警设置显示 --------------*/
if(mode_flag == 1) //显示设置上限值内容
{
dis_buf[3] = 0x76; //第4位数码管显示字母 H
dis_buf[2] = duan_ma[S_H / 100 % 10]; //显示百位数
dis_buf[1] = duan_ma[S_H / 10 % 10]; //显示十位数
dis_buf[0] = duan_ma[S_H % 10]; //显示个位数
}
if(mode_flag == 2) //显示设置下限值内容
{
dis_buf[3] = 0x38; //第4位数码管显示字母 L
dis_buf[2] = duan_ma[S_L / 100 % 10]; //显示百位数
dis_buf[1] = duan_ma[S_L / 10 % 10]; //显示十位数
dis_buf[0] = duan_ma[S_L % 10]; //显示个位数
}
}
}
编写完功能程序之后,在主函数main()中测试功能程序是否成功,内容如下:
源程序文件main.c:
#include "task.h"
//全局变量声明
extern unsigned int S; //声明距离缓存变量,在文件"task.c"中定义
extern unsigned int dis_buf[4]; //声明显示缓存数组,在文件"task.c"中定义
extern unsigned int code duan_ma[16];
extern unsigned int S_H, S_L; //S_H:报警上限值,默认400cm,S_L:报警下限值,默认10cm
/* 主函数 */
void main(void)
{
digDuan = 0x40; //显示"----"
digWei1 = 0;
digWei2 = 0;
digWei3 = 0;
digWei4 = 0;
delayms(1000); //延时大约1s
InitTimer0(); //定时器0初始化
while(1)
{
count(); //计算距离
if(S>=S_H || S<=S_L) //如果超出设定的范围,则显示"---"
{
dis_buf[3] = 0x00;
dis_buf[2] = 0x40; //显示"---"
dis_buf[1] = 0x40;
dis_buf[0] = 0x40;
BEEP = 0; //蜂鸣器鸣叫
}
else
{
BEEP = 1; //关闭蜂鸣器
dis_distance(); //显示距离
}
if(key_scan() == 1) //如果按下设置键,则进入设置报警功能
{
BEEP = 0; //按键提示音
delayms(50);
BEEP = 1;
delayms(100);
key_fuction(); //进入设置报警功能
}
}
}