单片机练习 - 使用1602LCD显示DS18B20的温度值

关于DS18B20电子温度器件可查看 单片机练习 - DS18B20温度转换与显示 .
本文主要讲述如何使用LCD. 具体过程解释可查看代码注释.

程序代码:
1602LCD
  1//1602液晶显示程序, TX-1B实验板上的1602LCD的R/W读写端接地, 即始终只写, 不读数据
  2//使用1602LCD显示DS18B20转换的温度值
  3#include <reg52.H>
  4#include <intrins.H>
  5#include <math.H>
  6
  7#define uchar unsigned char
  8#define uint unsigned int
  9sbit dula = P2^6;
 10sbit wela = P2^7;
 11
 12sbit RS = P3^5;    //数据/命令选择端(H/L)
 13sbit LCDEN = P3^4//使能端
 14
 15void delayUs()
 16{
 17    _nop_();
 18}

 19
 20void delayMs(uint a)
 21{
 22    uint i, j;
 23    for(i = a; i > 0; i--)
 24        for(j = 100; j > 0; j--);
 25}

 26
 27//第一行开始地址为0x80, 第二行开始地址为0xc0;
 28//写命令:RS=0, RW=0;
 29void writeComm(uchar comm)
 30{
 31    RS = 0;    
 32    P0 = comm;
 33    LCDEN = 1;
 34    delayUs();
 35    LCDEN = 0;
 36    delayMs(1);
 37}

 38
 39//写数据:RS=1, RW=0;
 40void writeData(uchar dat)
 41{
 42    RS = 1;
 43    P0 = dat;
 44    LCDEN = 1;
 45    delayUs();
 46    LCDEN = 0;
 47    delayMs(1);
 48}

 49
 50//初始化
 51//显示模式, 固定指令为00111000=0x38, 16*2显示, 5*7点阵,8位数据接口
 52//显示开/关及光标设置
 53//指令1: 00001DCB : D:开显示/关显示(H/L); C:显示光标/不显示(H/L), B:光标闪烁/不闪烁(H/L)
 54//指令2: 000001NS :
 55//N=1, 当读/写一个字符后地址指针加1, 且光标也加1; N=0则相反
 56//S=1, 当写一个字符, 整屏显示左移(N=1)或右移(N=0), 但光标不移动; S=0, 整屏不移动
 57void init()
 58{
 59    //先关闭数码管, 使两个锁存器锁存, 因开了数码管, USB电压驱动不够, 将没法使LCD正常工做
 60    dula = wela = 0;
 61    writeComm(0x38); //显示模式
 62    writeComm(0x0c); //开显示, 关光标
 63    writeComm(0x06); //写字符后地址加1, 光标加1
 64    writeComm(0x01); //清屏
 65}

 66
 67void writeString(uchar * str, uchar length)
 68{
 69    uchar i;
 70    for(i = 0; i < length; i++)
 71    {
 72        writeData(str[i]);
 73    }

 74}

 75
 76/**//*****************************DS18B20*******************************/
 77sbit ds = P2^2;
 78//初始化DS18B20
 79//让DS18B20一段相对长时间低电平, 而后一段相对很是短期高电平, 便可启动
 80void dsInit()
 81{
 82    //对于11.0592MHz时钟, unsigned int型的i, 做一个i++操做的时间大于为8us
 83    unsigned int i;  
 84    ds = 0;
 85    i = 100;   //拉低约800us, 符合协议要求的480us以上
 86    while(i>0) i--;
 87    ds = 1;    //产生一个上升沿, 进入等待应答状态
 88    i = 4;
 89    while(i>0) i--;
 90}

 91
 92void dsWait()
 93{
 94     unsigned int i;
 95     while(ds);  
 96     while(~ds);  //检测到应答脉冲
 97     i = 4;
 98     while(i > 0) i--;
 99}

100
101//向DS18B20读取一位数据
102//读一位, 让DS18B20一小周期低电平, 而后两小周期高电平, 
103//以后DS18B20则会输出持续一段时间的一位数据
104bit readBit()
105{
106    unsigned int i;
107    bit b;
108    ds = 0;
109    i++;   //延时约8us, 符合协议要求至少保持1us
110    ds = 1
111    i++; i++;  //延时约16us, 符合协议要求的至少延时15us以上
112    b = ds;
113    i = 8
114    while(i>0) i--;  //延时约64us, 符合读时隙不低于60us要求
115    return b;
116}

117
118//读取一字节数据, 经过调用readBit()来实现
119unsigned char readByte()
120{
121    unsigned int i;
122    unsigned char j, dat;
123    dat = 0;
124    for(i=0; i<8; i++)
125    {
126        j = readBit();
127        //最早读出的是最低位数据
128        dat = (j << 7| (dat >> 1);
129    }

130    return dat;
131}

132
133//向DS18B20写入一字节数据
134void writeByte(unsigned char dat)
135{
136    unsigned int i;
137    unsigned char j;
138    bit b;
139    for(j = 0; j < 8; j++)
140    {
141        b = dat & 0x01;
142        dat >>= 1;
143        //写"1", 将DQ拉低15us后, 在15us~60us内将DQ拉高, 即完成写1
144        if(b)   
145        {
146            ds = 0;
147            i++; i++;  //拉低约16us, 符号要求15~60us内
148            ds = 1;    
149            i = 8while(i>0) i--;  //延时约64us, 符合写时隙不低于60us要求
150        }

151        else  //写"0", 将DQ拉低60us~120us
152        {
153            ds = 0;
154            i = 8while(i>0) i--;  //拉低约64us, 符号要求
155            ds = 1;
156            i++; i++;  //整个写0时隙过程已经超过60us, 这里就不用像写1那样, 再延时64us了
157        }

158    }

159}

160
161//向DS18B20发送温度转换命令
162void sendChangeCmd()
163{
164    dsInit();    //初始化DS18B20, 不管什么命令, 首先都要发起初始化
165    dsWait();   //等待DS18B20应答
166    delayMs(1);    //延时1ms, 由于DS18B20会拉低DQ 60~240us做为应答信号
167    writeByte(0xcc); //写入跳过序列号命令字 Skip Rom
168    writeByte(0x44); //写入温度转换命令字 Convert T
169}

170
171//向DS18B20发送读取数据命令
172void sendReadCmd()
173{
174    dsInit();
175    dsWait();
176    delayMs(1);
177    writeByte(0xcc); //写入跳过序列号命令字 Skip Rom
178    writeByte(0xbe); //写入读取数据令字 Read Scratchpad
179}

180
181//获取当前温度值
182int getTmpValue()
183{
184    unsigned int tmpvalue;
185    int value; //存放温度数值
186    float t;
187    unsigned char low, high;
188    sendReadCmd();
189    //连续读取两个字节数据
190    low = readByte(); 
191    high = readByte();
192    //将高低两个字节合成一个整形变量
193    //计算机中对于负数是利用补码来表示的
194    //如果负值, 读取出来的数值是用补码表示的, 可直接赋值给int型的value
195    tmpvalue = high;
196    tmpvalue <<= 8;
197    tmpvalue |= low;
198    value = tmpvalue;
199    
200    //使用DS18B20的默认分辨率12位, 精确度为0.0625度, 即读回数据的最低位表明0.0625度
201    t = value * 0.0625;
202    //将它放大100倍, 使显示时可显示小数点后两位, 并对小数点后第三进行4舍5入
203    //如t=11.0625, 进行计数后, 获得value = 1106, 即11.06 度
204    //如t=-11.0625, 进行计数后, 获得value = -1106, 即-11.06 度
205    value = t * 100 + (value > 0 ? 0.5 : -0.5); //大于0加0.5, 小于0减0.5
206    return value;
207}

208
209void display(int v) 
210{
211    unsigned char count;
212    unsigned char datas[] = {00000};
213    unsigned int tmp = abs(v);
214    datas[0= tmp / 10000;
215    datas[1= tmp % 10000 / 1000;
216    datas[2= tmp % 1000 / 100;
217    datas[3= tmp % 100 / 10;
218    datas[4= tmp % 10;
219    writeComm(0xc0+3);
220    if(v < 0)
221    {
222        writeString(""2);
223    }

224    else
225    {
226        writeString(""2);
227    }

228    if(datas[0!= 0)
229    {
230        writeData('0'+datas[0]);
231    }

232    for(count = 1; count != 5; count++)
233    {
234        writeData('0'+datas[count]);
235        if(count == 2)
236        {
237            writeData('.');
238        }

239    }

240}

241/**//*****************************DS18B20*******************************/
242
243void main()
244{
245    uchar table[] = "Now Temperature:";
246    sendChangeCmd();
247    init();
248    writeComm(0x80);
249    writeString(table, 16);
250    while(1)
251    {
252        delayMs(1000); //温度转换时间须要750ms以上
253        writeComm(0xc0);
254        display(getTmpValue());
255        sendChangeCmd();
256    }

257}

效果图1:

效果图2: