摘要:本文介绍如何使用DS18B20和LCD1602实现一个数字温度计
LCD1602与单片机的连接通常有两种方式,一种是直接控制方式,另一种是所谓的间接控制方式。它们的区别只是所用的数据线的数量不同,其他都一样。在这里我们采用直接控制的方式,将LCD1602的8根数据线和3根控制线E,RS和R/W与单片机直接相连。VO引脚是液晶对比度调试端,连接一个10kΩ的电位器即可实现对比度的调整,实际项目中也可以采用将一个适当大小的电阻从该引脚接地的方法进行调整,不过该电阻的大小应通过事先的调试来决定。
DS18B20只有三个引脚,电源、数据线和接地。将数据线直接连接到单片机的引脚就可以了。整个仿真系统的原理图如下所示:
下面就来逐步的看一下仿真程序的实现方法。在程序的最开始还是引入头文件、宏定义和配置常量参数部分。具体内容如下所示:
#include <reg51.h> #include <intrins.h> #include <string.h> #include <stdio.h> #define uchar unsigned char #define uint unsigned int // LCD1602控制引脚定义 sbit RS = P2^0; sbit RW = P2^1; sbit E = P2^2; // 18B20引脚定义 sbit DQ = P2^3; |
这些引脚的定义需要按照实际的接线方法来进行,如果与上面的原理图不同,那么请进行修改。
接下来就是在C51中最经常使用到的延时函数的声明和实现了。在这里定义了2个延时函数,如下所示:
//*****************************// // 延时函数 //****************************// void delayUs2x(uchar t) { while(--t); } void delayMs(uchar t) { while(t--) { //大致延时1mS delayUs2x(245); delayUs2x(245); } } |
接下来就是对LCD1602进行控制的各个函数了。对LCD1602进行控制的关键点在于把握对三个控制信号RS、R/W和E的组合使用。在这里实现了以下几个函数:
busyCheck():检查LCD1602是否处于忙状态,用于确定是否可以接收新的指令。
lcdExec():通知LCD1602执行刚刚下发的命令。
lcdWCmd():写入命令函数。
lcdPos():设置光标位置,也就是下一个字符显示位置函数。
lcdWDat():写入数据指令,用于写入要显示的字符。
disChar():显示字符指令。
lcdDisplay():在指定位置显示字符串指令。
这些函数的实现方法如下:
// 检查1602是否处于忙碌状态 uchar busyCheck(void) { uchar lcdStatus; RS = 0; RW = 1; E = 1; _nop_(); _nop_(); _nop_(); _nop_(); lcdStatus = P0&0x80; E = 0; return lcdStatus; } // LCD执行命令 void lcdExec(void) { _nop_(); _nop_(); _nop_(); _nop_(); E = 1; _nop_(); _nop_(); _nop_(); _nop_(); E = 0; } // 写入1602命令 void lcdWCmd(uchar cmd) { RS = 0; RW = 0; E = 0; _nop_(); _nop_(); P0 = cmd; lcdExec(); } // 设置显示位置 void lcdPos(uchar pos) { lcdWCmd(pos|0x80); } // 写入字符 void lcdWDat(char dat) { while(busyCheck()); RS = 1; RW = 0; E = 0; P0 = dat; lcdExec(); } // 显示字符 void disChar(char a) { lcdWDat(a); while(busyCheck()); } // 显示字符串 void lcdDisplay(uchar p, uchar *s, uchar len) { uchar i; lcdPos(p); for(i=0; i<len; i++) { disChar(s[i]); } } |
最后还有一个LCD1602模块初始化的函数,在这里设置了其初始工作状态,并将屏幕显示内容清空。具体实现方法如下,有不清楚指令含义的可以参考前一篇文章中的介绍:
// 1602初始化 void lcdInit(void) { lcdWCmd(0x38); //设置显示格式 delayMs(1); lcdWCmd(0x0C); delayMs(1); lcdWCmd(0x06); delayMs(1); lcdWCmd(0x01); delayMs(1); } |
接下来就是读取DS18B20温度数值的函数了。使用DS18B20读取数据的过程在之前的文章中都介绍过了,下面就来看一下每一步都是如何实现的吧。
首先实现的是DS18B20初始化函数,如下所示:
/*------------------------------------------------ DS18B20函数声明 ------------------------------------------------*/ bit initDS18B20() //18b20初始化 { bit dat=0; DQ = 1; //DQ复位 delayUs2x(5); //稍做延时 DQ = 0; //单片机将DQ拉低 delayUs2x(200); //精确延时 大于 480us 小于960us delayUs2x(200); DQ = 1; //拉高总线 delayUs2x(50); //15~60us 后 接收60-240us的存在脉冲 dat=DQ; //如果x=0则初始化成功, x=1则初始化失败 delayUs2x(25); //稍作延时返回 return dat; } |
在这里需要特别注意的就是对脉冲宽度的要求,一定要满足DS18B20的要求,否则是无法完成正常的初始化工作的。
接下来是两个工具函数,分别实现了向DS18B20读取一个字节的数据和写入一个字节的数据,重点注意的也是对时序的规定和脉冲宽度的要求。具体实现方法如下:
//读取一个字节 unsigned char readOneByte() { unsigned char i=0; unsigned char dat = 0; for (i=8;i>0;i--) { DQ = 0; // 给脉冲信号 dat>>=1; DQ = 1; // 给脉冲信号 if(DQ) dat|=0x80; delayUs2x(25); } return(dat); } //写入一个字节 void writeOneByte(unsigned char dat) { unsigned char i=0; for (i=8; i>0; i--) { DQ = 0; DQ = dat&0x01; delayUs2x(25); DQ = 1; dat>>=1; } delayUs2x(25); } |
接下来的方法是读取2个字节的温度值函数,如下所示:
// 读取温度 unsigned int readTemperature() { unsigned char a=0; unsigned int b=0; unsigned int t=0; initDS18B20(); writeOneByte(0xCC); // 跳过读序号列号的操作 writeOneByte(0x44); // 启动温度转换 delayMs(10); initDS18B20(); writeOneByte(0xCC); //跳过读序号列号的操作 writeOneByte(0xBE); //读取温度寄存器等(共可读9个寄存器) 前两个就是温度 a=readOneByte(); //低位 b=readOneByte(); //高位 b<<=8; t=a+b; return(t); } |
这就是指令一级的与DS18B20的交互过程,关于交互的指令顺序和格式在之前的文章中都已经介绍过了。这个方法最终返回2个字节的测量结果。
接下来,就是将这2个字节的测量结果转换成我们所要的温度值,在转换的过程中所要注意的就是温度值是有正负的,要区别处理,具体的实现方法如下:
// 得到浮点数的温度值 float getTemperature() { unsigned int temp; float tempture; temp = readTemperature(); if( temp&0x8000 ) { temp = ~temp; temp++; tempture = -0.0625*temp; } else { tempture = 0.0625*temp; } return tempture; } |
好了,所有的准备工作都完成了,接下来该实现主程序了。这个主程序就比较简单了。首先需要完成初始化工作,然后就是循环读取DS18B20的温度值,并显示在LCD1602上就可以了。如下所示:
// 主程序 void main(void) { float temperature; char tempStr[8]; lcdInit(); delayMs(10); while(1) { temperature = getTemperature(); memset(tempStr, 0, sizeof(tempStr)); sprintf( tempStr, "Temp:%.2f", temperature ); lcdDisplay(0, tempStr, strlen(tempStr)); delayMs(500); } } |
好了,所有的程序就全部完成了,接下来看一下仿真的结果吧。
数字温度计