文末有源文件!
实验内容:单片机开机读取 ds18b20 检测到的温度,并显示在 1602 上,精确到0.1度;能显示超过100的温度和零下温度。温度值后面要加上符号:℃
提示:在lcd 1602上总共需要6位显示温度值: ???. ?℃
其中的.和℃都是固定位置的固定符号,可在程序里写好。
另外4个?号处的温度值要实时的从18b20读取。
当温度为零下时,最高位?号处显示为-号;
当温度为0及以上时,最高位不显示+号,且不为0;其中当温度值>=100,该处显示1。
1.首先是LCD1602模块,用于把读出的温度进行展示,LcdStar()用于对显示器的初始化,可以按照所需进行定制初始化。LcdWriteDat()表示向LCD写入字节类型的数据,LcdSetCursor(unsigned char x, unsigned char y)表示写入字节的位置,即屏幕坐标。LcdShowStr(unsigned char x, unsigned char y, unsigned char *str)就是对两个函数的综合。代码如下。
#include <reg52.h>
#define LCD1602_DB P0
sbit LCD1602_RS = P2^0;
sbit LCD1602_RW = P2^1;
sbit LCD1602_E = P2^2;
void InitLcd1602();
int n=0;
int i=0;
int j=0;
int l=0;
int ii=0;
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
void LcdWriteCmd(unsigned char cmd);
void delay(unsigned int j);
void LcdStar()
{
unsigned char str1[] = "Welcome to aau";
unsigned char str2[] = "No .5 ";
unsigned char str3[] = " ";
unsigned char str4[] = " ";
// unsigned char tab[]="Temp=";
InitLcd1602();/* 初始化 1602 液晶 */
// LcdShowStr(2, 0, str);
// LcdShowStr(1, 1, tab);
LcdShowStr(9, 1, "...");//默认初始化温度00
// LcdShowStr(12, 1,0XDF);
LcdShowStr(14, 1,"C");//添加C温度
// LcdShowStr(1, 1, str2);
for(i=0;i<14;i++){
// for(j=i,l=0;j<14;j++,l++){
// str3[l]=str1[j];
// str4[l]=str2[j];
// }
// LcdShowStr(2, 0, str3);
// for(ii=0;ii<14;ii++){
// str3[ii]=0x00;
// }
// delay(100);
// LcdWriteCmd(0x01);
// LcdShowStr(1, 1, str2);
}
LcdWriteCmd(0x01); //清屏
}
void delay(unsigned int jj){
unsigned char i=250;
for(;jj>0;jj--){
while(--i);
i=249;
while(--i);
i=250;
}
}
/* 等待液晶准备好 */
void LcdWaitReady()
{
unsigned char sta;
LCD1602_DB = 0xFF;
LCD1602_RS = 0;
LCD1602_RW = 1;
do {
LCD1602_E = 1;
sta = LCD1602_DB; //读取状态字
LCD1602_E = 0;
} while (sta & 0x80); //bit7 等于 1 表示液晶正忙,重复检测直到其等于 0 为止
}
/* 向 LCD1602 液晶写入一字节命令, cmd-待写入命令值 */
void LcdWriteCmd(unsigned char cmd)
{
LcdWaitReady();
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
LCD1602_E = 1;
LCD1602_E = 0;
}
/* 向 LCD1602 液晶写入一字节数据, dat-待写入数据值 */
void LcdWriteDat(unsigned char dat)
{
LcdWaitReady();
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat;
LCD1602_E = 1;
LCD1602_E = 0;
}
/* 设置显示 RAM 起始地址,亦即光标位置, (x,y)-对应屏幕上的字符坐标 */
void LcdSetCursor(unsigned char x, unsigned char y)
{
unsigned char addr;
if (y == 0) //由输入的屏幕坐标计算显示 RAM 的地址
addr = 0x00 + x; //第一行字符地址从 0x00 起始
else
addr = 0x40 + x; //第二行字符地址从 0x40 起始
LcdWriteCmd(addr | 0x80); //设置 RAM 地址
}
/* 在液晶上显示字符串, (x,y)-对应屏幕上的起始坐标, str-字符串指针 */
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str)
{
LcdSetCursor(x, y); //设置起始地址
while (*str != '\0') //连续写入字符串数据,直到检测到结束符
{
LcdWriteDat(*str++); //先取 str 指向的数据,然后 str 自加 1
}
}
/* 初始化 1602 液晶 */
void InitLcd1602()
{
LcdWriteCmd(0x38); //16*2 显示, 5*7 点阵, 8 位数据接口
LcdWriteCmd(0x0C); //显示器开,光标关闭
LcdWriteCmd(0x06); //文字不动,地址自动+1
LcdWriteCmd(0x01); //清屏
}
将代码保存为LCD1602.h
2.DS18B20模块,工作原理为读出16位二进制数据,前五位代表符号位,后十一位表示数据位。并且为补码表示,如果是负数就要对后十一位数据进行取反加一进行运算,符号位不变,也不参加运算。由于分辨率默认为0.0625,所以需要将所得的数×0.0625。或者÷16,此代码中是在主函数中进行4位移位运算。得到我们需要的温度。具体代码如下:
#include <reg52.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
void delay5(uchar);
void init_ds18b20(void);
uchar readbyte(void);
void writebyte(uchar);
void retemp(int);
sbit DQ=P3^2;
void delay5(uchar n) //函数功能:延时5μs
{
do
{
_nop_();
_nop_();
_nop_();
n--;
}
while(n);
}
void init_ds18b20(void) //函数功能:18B20初始化
{
uchar x=0;
DQ =0;
delay5(120);
DQ =1;
delay5(16);
delay5(80);
}
uchar readbyte(void) { //函数功能:读取1字节数据
uchar dat;
uchar mask;
EA = 0; //禁止总中断
for (mask=0x01; mask!=0; mask<<=1) //低位在先,依次采集 8 个 bit
{
DQ = 0; //产生 2us 低电平脉冲
_nop_();
_nop_();
DQ = 1; //结束低电平脉冲,等待 18B20 输出数据
_nop_(); //延时 2us
_nop_();
if (!DQ) //读取通信引脚上的值
dat &= ~mask;
else
dat |= mask;
delay5(60); //再延时 60us
}
EA = 1; //重新使能总中断
return dat;
}
void writebyte(uchar dat) { //函数功能:写1字节
uchar i=0;
for(i=8;i>0;i--) {
DQ =0;
DQ =dat&0x01; //写"1" 在15μs内拉低
delay5(12); //写"0" 拉低60μs
DQ = 1;
dat>>=1;
delay5(5);
}
}
void retemp(int *temp) {
uchar a,b;
uint t;
init_ds18b20();
writebyte(0xCC);
writebyte(0x44);
init_ds18b20();
writebyte(0xCC);
writebyte(0xBE);
a=readbyte();
b=readbyte();
t=b;
t<<=8;
t=t|a;
// *temp = t*0.0625;
*temp = (((int)b << 8) + a);
}
将代码保存为DS18B20_1.h(别问为什么加了_1因为头文件写的太多了,为了方便调试,又加了一个,为了标准,可以直接去掉,但是主函数中记得改引入的头文件)。
3.主函数模块:主函数就是将两块代码进行整合,其中有一个将数字转化成字符串函数,是因为LCD1602不能输出数字。具体代码如下:
#include <reg52.h>
#include<DS18B20.h>
#include<LCD1602.h>
#include<DS18B20_1.h>
bit flag1s = 0; //1s 定时标志
unsigned char T0RH = 0; //T0 重载值的高字节
unsigned char T0RL = 0; //T0 重载值的低字节
void ConfigTimer0(unsigned int ms);
unsigned char IntToString(unsigned char *str, int dat);
extern void init_ds18b20();
extern void retemp(int *temp);
extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
void main()
{
int temp; //读取到的当前温度值
int intT, decT; //温度值的整数和小数部分
unsigned char len;
unsigned char str[12];
unsigned char tab[]="Temp=";
EA = 1; //开总中断
ConfigTimer0(10); //T0 定时 10ms
// Start18B20(); //启动 DS18B20
InitLcd1602(); //初始化液晶
LcdStar();
while (1)
{
if (flag1s) //每秒更新一次温度
{
flag1s = 0;
retemp(&temp); //读取当前温度
intT = temp >> 4; //分离出温度值整数部分
decT = temp & 0xF; //分离出温度值小数部分
len = IntToString(str, intT); //整数部分转换为字符串
str[len++] = '.'; //添加小数点
decT = (decT*10) / 16; //二进制的小数部分转换为 1 位十进制位
if( intT<0){
decT=10-decT;
}
str[len++] = decT + '0'; //十进制小数位再转换为 ASCII 字符
while (len < 6) //用空格补齐到 6 个字符长度
{
str[len++] = ' ';
}
str[len] = '\0'; //添加字符串结束符
LcdShowStr(1, 1, tab);
LcdShowStr(7, 1, str); //显示到液晶屏上
LcdShowStr(12, 1,0xdf);
LcdShowStr(14, 1,"C");//添加C温度
}
}
}
/* 整型数转换为字符串, str-字符串指针, dat-待转换数,返回值-字符串长度 */
unsigned char IntToString(unsigned char *str, int dat)
{
signed char i = 0;
unsigned char len = 0;
unsigned char buf[6];
if (dat < 0) //如果为负数,首先取绝对值,并在指针上添加负号
{
dat = -dat-1;
*str++ = '-';
len++;
}
do { //先转换为低位在前的十进制数组
buf[i++] = dat % 10;
dat /= 10;
} while (dat > 0);
len += i; //i 最后的值就是有效字符的个数
while (i-- > 0) //将数组值转换为 ASCII 码反向拷贝到接收指针上
{
*str++ = buf[i] + '0';
}
*str = '\0'; //添加字符串结束符
return len; //返回字符串长度
}
/* 配置并启动 T0, ms-T0 定时时间 */
void ConfigTimer0(unsigned int ms)
{
unsigned long tmp; //临时变量
tmp = 11059200 / 12; //定时器计数频率
tmp = (tmp * ms) / 1000; //计算所需的计数值
tmp = 65536 - tmp; //计算定时器重载值
tmp = tmp + 12; //补偿中断响应延时造成的误差
T0RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节
T0RL = (unsigned char)tmp;
TMOD &= 0xF0; //清零 T0 的控制位
TMOD |= 0x01; //配置 T0 为模式 1
TH0 = T0RH; //加载 T0 重载值
TL0 = T0RL;
ET0 = 1; //使能 T0 中断
TR0 = 1; //启动 T0
}
/* T0 中断服务函数,完成 1 秒定时 */
void InterruptTimer0() interrupt 1
{
static unsigned char tmr1s = 0;
TH0 = T0RH; //重新加载重载值
TL0 = T0RL;
tmr1s++;
if (tmr1s >= 100) //定时 1s
{
tmr1s = 0;
flag1s = 1;
}
}
原理图以及hex文件连接如下:
链接:https://pan.baidu.com/s/1WR04LHYcTlr2i-M_9fbwnA?pwd=h4e0
提取码:h4e0
闫老师可以提问问题:负小数是怎么显示出来的:对读出的十一位数据位进行反码加一,转换成再十进制,代码中表示如下:
转换成十进制后,如果小数小于零,就对其进行十减,整数部分进行取反。
整个工程如下添加头文件操作如下: 文章来源:https://www.toymoban.com/news/detail-482026.html
添加时,头文件和主函数在同一个目录,编译后就会变成下面。 文章来源地址https://www.toymoban.com/news/detail-482026.html
到了这里,关于实验09 DS18B20及LCD 1602(温度检测与显示)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!