最近由于事情比较杂,加上自己懒得动手码字,一直没有更简书。刚好最近帮学弟做了一个超声波测距(含温度补偿)的小项目,觉得再看以前的示例程序不够通俗明了,站在过来人的视点,写一段简单的示例程序,给大家看看。
首先想说的是,单片机程序应该从时序图入手。了解外围器件的工作时序,并按照时序,对控制时序的引脚上拉下拉各种跳变,就可以对外围器件进行读写。是不是觉得有点抽象。没有关系,接下来不多说,实际例子来教新手使用HC-SR04这个超声波模块。四个引脚分别是电源正负和收发,探头带有T的是传送(Transmission),带有R的是接收(Receiva)实物如下图1所示。
硬件连接图如图2。VCC&GND常规的电源正负,ECHO&TRIG两个脚分别为读写引脚。
介绍了硬件,接下来就是关键的软件部分了,在讲软件之前,一定要说下最核心的时序部分(理解时序是十分重要的工程师素养)。下图3就是这个模块的时序图。
触发信号就是Trig引脚的信号,TTL的10us就是出发信号,由图知道,10us的高电平(TTL规格,不懂TTL得话自己百度哟,主要区别于CMOS电平。)。即可启动模块内部的工作时序,模块内部的发出信号我们不用管,只需要了解接口程序怎么按照时序编写就好了,模块内部的工作,只需要配置好工作时序。直接看第三根线(输出回响信号),其实在模块收到trig=1以后,模块就很快(可以忽略的时间)开始发出声波,同时Echo引脚产生高电平,直到声波接触物体并且返回的时候,Echo才变为低电平。所以第三根时序线的高电平时长除以2就是距离所耗时间。(错误的理解:探头发出的声波被物体挡住以后,声波返回,R探头接收到声波,Echo引脚产生高电平,直到物体消失,Echo引脚电平变低。声波从T探头发出,物体挡住声波,沈波返回到R探头接收。)这段时间就是声波走的总路程。看图4了解测距原理吧。
有没有发现,输出回响信号(时序图第三根线)的高电平保持时间就是声波从发出然后碰到到障碍物返回,所消耗的总时间。把这段时间所走的距离除以2.就是模块与障碍物的距离拉~~~
那么来看程序吧骚年们,主程序如下。
```
void main()
{
uint L; //定义距离变量为无符号integer
TMOD = 0x01; // 初始化单片机的计时器0,方式1
L=GetDistance(); //调用距离计算的子函数
while(1); //死循环
}
```
主函数写的应该不难看懂,就是定义要得到的距离是L,然后开启定时器(89C51定时器有T0&T1两个,工作模式有4种,后面我会写文章详细讲定时器/计数器,尽情期待哟),然后调用GetDistance()这个函数获得距离的值,需要注意的是子函数的类型一定要和主函数定义的一致,都是无符号int,避免数据不准确。
```
uint GetDistance(void)
{
uint ss; // 用于记录测得的距离
TH0=0; //初始化定时器的高位为0
TL0=0; //初始化定时器的低位为0
Trig_P=1; // 给超声波模块一个开始脉冲
DelayMs(1); //延时的意义就在于时序要求启动信号要有10um的高电平
Trig_P=0; //恢复低电平
//前面的程序都是配置时序,使模块工作起来
//模块一旦开始工作,发出声波的同时,会使得ECHO引脚变高
while(!Echo_P); // 等待超声波模块的Echo变高 直到Echo引脚不再是0
TR0=1; // 启动定时器,开始计时
while(Echo_P); // 等待超声波模块接收到返回声波
TR0=0; // 停止定时器,停止计时
ss=((TH0*256+TL0)*0.034)/2; // 距离cm=(时间us * 速度cm/us)/2
return ss;
}
```
上面这段程序就是严格按照时序写的。如果说有难懂的语句就是定时器的使用,定时器最开始初始化为0。从Echo脚变为1开始计时,收到声波返回停止计时。低位最大只能是256,满了256就要向前进位。举例说明吧,26(十进制数)低位最大是10,挡计数到10的时候,就要向前进位,高位(十位)就会变为3,低位清零继续从0开始计数到10,又进一位。之后十进制数的结果就是 十位X10+个位。同理,计数器也是这样。所以有(TH0*256+TL0)。注意换算单位以后进行运算,并且注意是来回的距离,除以2后就是实际距离哟。讲到这里,简单的超声波测距就完全讲完了,有人跟我吐槽,说我写的例子没有完整的程序,不方便查阅,现附上完整的程序。谢谢大家。
```
/**************Copyrigfht:DENGWEI
TIME:2016.12.12
FUNCTION:超声波测距
VISION: v1.0**********************/
#include<reg52.h>
#include<intrins.h> //自带的库
#define uchar unsigned char // 以后unsigned char就可以用uchar代替
#define uint unsigned int // 宏定义
sbit Trig_P = P2^2; // 超声波模块的Trig管脚
sbit Echo_P = P2^3; // 超声波模块的Echo管脚
/*********************************************************/
// 毫秒级的延时函数,time是要延时的毫秒数
/*********************************************************/
void DelayMs(uint time)
{
uint i,j;
for(i=time;i>0;i--)
for(j=110;j>0;j--);
}
/*********************************************************/
// 计算测到的距离
/*********************************************************/
uint GetDistance(void)
{
uint ss; // 用于记录测得的距离
TH0=0;
TL0=0;
Trig_P=1; // 给超声波模块一个开始脉冲
DelayMs(1); //延时的意义就在于时序要求启动信号要有10um的高电平
Trig_P=0; //恢复低电平
//前面的程序都是配置时序,使模块工作起来
//模块一旦开始工作,发出声波的同时,会使得ECHO引脚变高
while(!Echo_P); // 等待超声波模块的Echo变高 直到Echo引脚不再是0
TR0=1; // 启动定时器,开始计时
while(Echo_P); // 等待超声波模块接收到返回声波
TR0=0; // 停止定时器,停止计时
ss=((TH0*256+TL0)*0.034)/2; // 距离cm=(时间us * 速度cm/us)/2
return ss;
}
/*********************************************************/
//主函数
/*********************************************************/
void main()
{
uint L; //定义距离变量为无符号integer
TMOD = 0x01; // 初始化单片机的计时器0,方式1
L=GetDistance(); //调用距离计算的子函数
while(1); //死循环
}
//end
```