#include <reg51.h> //这个文件是 51寄存器定义的文件,诸如P1,P2等各个端口都有定义
#include <intrins.h> //这个头文件中 有51单片机的循环移位函数 各个函数的意义可以通过百度查到
#define uchar unsigned char //宏定义变量类型 unsigned char为 uchar。这样以后定义变量类型可以用uchar 来替代 unsigned char
#define uint unsigned int //同上
#define ulint unsigned long int
//===============================LCD1602接口定义=====================
/*-----------------------------------------------------
|DB0-----P2.7 | DB4-----P2.3 | RW-------P0.6 |
|DB1-----P2.6 | DB5-----P2.2 | RS-------P0.5 |
|DB2-----P2.5 | DB6-----P2.1 | E--------P0.7 |
|DB3-----P2.4 | DB7-----P2.0 | 注意,P0.5到P0.7需要接上拉电阻
---------------------------------------------------
需要说明的是,这里我为了作电路板的时候走线美观,将P2的端口与液晶
显示的数据端口,对应的顺序颠倒了,所以为了能够准确的输入输出数据
我编写ReverseOrder 这个函数,在输入输出数据前调用,这样就把顺序
调整过来了.
=============================================================*/
#define LCM_Data P2 //数据接口
#define Busy 0x80 //用于检测LCM状态字中的Busy标识
sbit LCM_RW = P0^6; //读写控制输入端,LCD1602的第五脚
//sbit 不是标准C语言的函数,是51单片机的C51语言特有的,是位定义
//这里定义P0.6 为LCM_RW,以后引用LCM_RW都是在引用P0.6。
sbit LCM_RS = P0^5; //寄存器选择输入端,LCD1602的第四脚
sbit LCM_E = P0^7; //使能信号输入端,LCD1602的第6脚
//===============================超声波模块定义========================
sbit RemPin = P3^2; // 接收端,超声波接收端用终端INT0来接收
sbit TxPin = P1^0; //发射端,用的是P1.0
//***********************************************************************
//ds18b20数字温度传感器控制引脚定义
sbit dq_ds18b20=P3^3;//定义控制DS18B20
//***********************************************************************
//LCD显示模块的函数声明
void WriteDataLCM (uchar WDLCM);//LCD模块写数据
void WriteCommandLCM (uchar WCLCM,BuysC); //LCD模块写指令
uchar ReadDataLCM (void);//LCD模块读数据
uchar ReadStatusLCM (void);//读LCD模块的忙标
void DisplayOneChar (uchar X,uchar Y,uchar ASCII);//在第X+1行的第Y+1位置显示一个字符
void DisplayListChar (uchar X,uchar Y,uchar delayms,uchar code *DData);
void DisplayCursorPos (uchar X, uchar Y);
void LCMInit (void);
void DisplayIntData (uchar X, uchar Y,int ZhengShu,uchar Digit,uchar XiaoShu);
uchar ReverseOrder( unsigned char X);
//**********************************************************************
//延时函数声明
void delay25us_40KHz(unsigned char us);
void DelayUs(uint us);
void DelayMs(uint Ms);
void delay_3us();//3US的延时程序
void delay_8us(unsigned int t);//8US延时基准程序
void delay_50us(unsigned int t);//延时50*T微妙函数的声明
//***********************************************************************
//DS18B20测温函数定义
void w_1byte_ds18b20(uchar value);//向DS18B20写一个字节
uchar r_1byte_ds18b20(void);//从DS18B20读取一个字节的数据
void rest_ds18b20(void);//DS18B20复位程序
void readtemp_ds18b20(void);//读取温度
void display_temp(void);//温度显示程序
//***********************************************************************
//参数定义
uint length = 0; // 测距的长度0.00M
//uchar flag = 0; // 测距的标志 有信号接收=1-->确实是个多余的标志位,去掉好了(flag)
uchar templ,temph;
uint speed;//根据温度计算出来的声音速度
uchar t_b,t_s,t_g,t_x;//从左到右分别存储温度百位,十位,个位,小数位
uchar flag1;//温度正负性暂存,1为正数,0为负数
uchar time_out=0;
const unsigned char tabl3[]={0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x04,0x05,0x06,0x06,0x07,0x08,0x08,0x09,0x09};
//因为51单片机处理浮点数很慢,顾常用查表的方式来代替浮点运算。这种方式是在用空间(代码的大小)来换时间(代码执行的时间)
//因此在readtemp_ds18b20这个函数中用t_x=tabl3[templ & 0x0f]; 计算温度的小数
//具体来说是这样:因为DS18B20 的小数部分在最后4位,顾首先用 templ & 0x0f来提取出第四位
//然后注意到这4位可以表示0-15个值(比如1110对应的十进制数是13),而看DS18B20的手册时(百度文库中就有很多中文的),
//可以看到DS1B820输出的最小单位是0.0625,所以网上有人直接将4位小数部分的值直接乘以0.0625,这样是对的
//但是我们只取了小数点后1位,而且因为考虑到系统的实时性,就希望用查表法来用空间换时间,这样
//0*0.0625=0,这是tab13表的第一位
//1*0.0625=1,这是tab13表的第一位,四舍五入约等于1了
//2*0.0625=1,这是tab13表的第一位,四舍五入约等于1了
//..........以此类推
/*===========================================================================
1主程序
=============================================================================*/
void main(void)
{ uchar i;
LCMInit(); //1602初始化
EX0 = 1; //允许总中断中断,使能 INT0 外部中断
ET0 = 1; //允许T0中断
TMOD=0x11; //设定T0为16位时器,设定T1为16位时器
DisplayOneChar( 0,15,'m'); //显示距离单位 ?m“ -->是的,单位为m
DisplayListChar(0,0,0, "Distance: ");
//显示字符串 参数4是延时吗?第一个参数应该是行数啊?应该是 0,0,4吧?
//是的,参看函数内部,第一条 X &= 0x1;如果是4,那么二级制代码是
//100,第一条语句的意思是将X按位与1做'&'运算,所以只要是偶数都被
//与运算成0了,这里我改回来了,改成0了,一样的
while(1) //可以算响应周期的,所谓超声波测距仪的响应周期就是这个while(1)循环了
//精确计算,还需知道每个函数的执行周期
{
readtemp_ds18b20(); //读取温度
display_temp(); //显示温度
for(i=0;i<20;i++) //这个i是控制什么的?-->主要是为了和DS18B20的检测周期分开
//因为温度不能瞬变,所以这样在while(1)的一次循环内,温度只测一次
//而距离测了20次
{
DisplayIntData(0, 14,length,5,3); //显示测量距离
time_out=1;
TH0=0x00;
TL0=0x00; //启动定时器0 的寄存器 TH0和TL0 清零
TR0=1; //启动定时器0
EA = 1; //允许所有中断
delay25us_40KHz(15); //发出脉冲信号,15?-->是本次发射,一共发射15个周期的声波
DelayMs(300);
}
}
}
/*====================================================================
2超声波模块测试子程序
注意:是用12MHz晶振
设定延时时间:x*25us 与 产生40KHZ的脉冲
====================================================================*/
void delay25us_40KHz(unsigned char us) //精确的计算要用keil的 debug,根据单片机的汇编来计算
{ //当然更常用的方法也是简便的方法是用示波器来试验
//实际上很少有人精确去算的,都是用示波器来看效果,然后
//酌情来增减_nop_();函数来调整,这是一般方法
//但是为了论文写的漂亮可以精确计算的
while(us--)
{
TxPin = 0; //此时,TX端,即 P1.0输出0V?发射超声波? 没错,TX周期性的发射1与0,每调用之前是1
//第一次调用编程0
_nop_();_nop_();
_nop_();_nop_();
_nop_();_nop_();
_nop_();_nop_();
_nop_();_nop_();
_nop_();
TxPin = 1; //然后又变成1
//1与0的交替进行2*US次,这里US表示的是声波的个数
_nop_();_nop_();
_nop_();_nop_();
}
TxPin = 1; //最后又重新置为高电平,等待下一次调用..
}
/*=============================================================================
3中断程序的入口即接收模块并计算距离
(注意:接收与发射的电平是相反的)
=============================================================================*/
void init0int() interrupt 0 // interrupt 0是中断关键字,意思是中断0触发后,就调用这个函数
{ //interrupt 是编译器特有的关键字,和char一样,都用黑体和普通代码区分开
uint timer_us = 0;
TR0=0; //关闭定时器0
timer_us = TH0*256+TL0; //修正测距的距离
if(timer_us>190)timer_us=timer_us-180; //修正测距的距离,
//180这个数来源于,定时器中断函