实验09 DS18B20及LCD 1602(温度检测与显示)

文末有源文件!

实验内容:单片机开机读取 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 
闫老师可以提问问题:负小数是怎么显示出来的:对读出的十一位数据位进行反码加一,转换成再十进制,代码中表示如下:

转换成十进制后,如果小数小于零,就对其进行十减,整数部分进行取反。

整个工程如下添加头文件操作如下: 

添加时,头文件和主函数在同一个目录,编译后就会变成下面。 

  • 12
    点赞
  • 83
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值