【51单片机】DS18B02单总线温度采集

实验目标
1、学习单总线温度传感器DB18B20的原理和通信时序;
2、使用51单片机的一个 IO 口模拟单总线时序与温度传感器DS18B20通信,将检测的环境温度读取出来。

一、DS18B20温度传感器

(一)介绍

DS18B20是一款常用的高精度的单总线数字温度测量芯片。具有体积小,硬件开销低,抗干扰能力强,精度高的特点。
在这里插入图片描述
传感器参数

  • 测温范围为-55℃到+125℃,在-10℃到+85℃范围内误差为±0.4°。

  • 返回16位二进制温度数值

  • 主机和从机通信使用单总线,即使用单线进行数据的发送和接收

  • 在使用中不需要任何外围元件,独立芯片即可完成工作。

  • 掉电保护功能 DS18B20 内部含有 EEPROM ,通过配置寄存器可以设定数字转换精度和报警温度,在系统掉电以后,它仍可保存分辨率及报警温度的设定值。

  • 每个DS18B20都有独立唯一的64位-ID,此特性决定了它可以将任意多的DS18b20挂载到一根总线上,通过ROM搜索读取相应DS18B20的温度值

  • 宽电压供电,电压2.5V~5.5V

  • DS18B20返回的16位二进制数代表此刻探测的温度值,其高五位代表正负。如果高五位全部为1,则代表返回的温度值为负值。如果高五位全部为0,则代表返回的温度值为正值。后面的11位数据代表温度的绝对值,将其转换为十进制数值之后,再乘以0.0625即可获得此时的温度值。
    传感器引脚及原理图
    在这里插入图片描述
    在这里插入图片描述
    DS18B20一共有三个引脚

  • GND :电源地线

  • DQ:数字信号输入/输出端

  • VDD:外接供电电源输入端

(二)DS18B20内部构成

主要由以下3部分组成: 64 位ROM,高速暂存器,存储器。
其中高速暂存器包含:

  • 温度传感器
  • 一个字节的温度上限和温度下限报警触发器(TH和TL)
  • 配置寄存器允许用户设定9位,10位,11位和12位的温度分辨率,分别对应着温度的分辨率为:0.5°C,0.25°C,0.125°C,0.0625°C,默认为12位分辨率,
    存储器:由一个高速的RAM和一个可擦除的EEPROM组成,EEPROM存储高温和低温触发器(TH和TL)以及配置寄存器的值,(就是存储低温和高温报警值以及温度分辨率)
    在这里插入图片描述

1.DS18B20高速暂存器

由9个字节组成。

  • 字节0~1 是温度存储器,用来存储转换好的温度。第0个字节存储温度低8位,第一个字节存储温度高8位
  • 字节2~3 是用户用来设置最高报警和最低报警值(TH和TL)。
  • 字节4 是配置寄存器,用来配置转换精度,可以设置为9~12 位。
  • 字节5~7 保留位。芯片内部使用
  • 字节8 CRC校验位。是64位ROM中的前56位编码的校验码。由CRC发生器产生。
    在这里插入图片描述

2.DS18B20温度读取与计算

DS18B20采用16位补码的形式来存储温度数据,温度是摄氏度。当温度转换命令发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第0和第1个字节。

高字节的五个S为符号位,温度为正值时S=1,温度为负值时S=0

剩下的11位为温度数据位,对于12位分辨率,所有位全部有效,对于11位分辨率,位0(bit0)无定义,对于10位分辨率,位0和位1无定义,对于9位分辨率,位0,位1,和位2无定义。
在这里插入图片描述
对应的温度计算
当五个符号位S=0时,温度为正值,直接将后面的11位二进制转换为十进制,再乘以0.0625(12位分辨率),就可以得到温度值;

当五个符号位S=1时,温度为负值,先将后面的11位二进制补码变为原码(符号位不变,数值位取反后加1),再计算十进制值。再乘以0.0625(12位分辨率),就可以得到温度值;

例如:

+125℃的数字输出07D0(00000111 11010000)
 
转换成10进制是2000,对应摄氏度:0.0625x2000=125°C

-55℃的数字输出为 FC90。
  
首先取反,然后+1,转换成原码为:11111011 01101111
数值位转换成10进制是870,对应摄氏度:-0.0625x870=-55°C。
在这里插入图片描述

(三)单总线时序

  • 单总线(1-Wire BUS)是由Dallas公司开发的一种通用数据总线。
  • 一根通信线:DQ
  • 异步、半双工
  • 单总线只需要一根通信线即可实现数据的双向传输,当采用寄生供电时,还可以省去设备的VDD线路,此时,供电加通信只需要DQ和GND两根线。
    初始化:主机将总线拉低至少480微秒,然后释放总线,等待15-60微秒后,存在的从机会拉低总线60-240微秒以响应主机,之后从机将释放总线
    在这里插入图片描述
    在这里插入图片描述

发送一位:主机将总线拉低60-120us,然后释放总线,表示发送0;主机将总线拉低1~15us,然后释放总线,表示发送1。从机将在总线拉低30us后(典型值)读取电平,整个时间片应大于60us
在这里插入图片描述在这里插入图片描述

接收一位:主机将总线拉低1~15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us的末尾),读取为低电平则为接收0,读取为高电平则为接收1 ,整个时间片应大于60us
在这里插入图片描述
在这里插入图片描述

发送一个字节:连续调用8次发送一位的时序,依次发送一个字节的8位(低位在前)
在这里插入图片描述
在这里插入图片描述

接收一个字节:连续调用8次接收一位的时序,依次接收一个字节的8位(低位在前)
在这里插入图片描述
在这里插入图片描述

(四)DS18B20操作流程

DS18B20的工作步骤可以分为三步:

  • 初始化DS18B20
  • 执行ROM指令
  • 执行DS18B20功能指令
    ROM指令
    采用多个DS18B20时,需要写ROM指令来控制总线上的某个DS18B20。如果是单个Ds18B20,直接写跳过ROM指令0xCC即可。在这里插入图片描述
    功能指令
    温度转换 0x44:开启温度读取转换,读取好的温度会存储在高速暂存器的第0个和第一个字节中。
    读取温度 0xBE:读取高速暂存器存储的数据。
    在这里插入图片描述
    在这里插入图片描述

二、使用DS18B20采集环境温度

(一)LED数码管+DS18B20

要求:利用DS18B20和LED数码管实现单总线温度测量系统。

1.Proteus电路原理

在这里插入图片描述

2.代码实现

#include "reg51.h"
#include "intrins.h"
#define uchar unsigned char
#define uint unsigned int 
#define out P0
sbit smg1=out^4;
sbit smg2=out^5;
sbit DQ=P3^7;
void delay5(uchar);
void init_ds18b20(void);
uchar readbyte(void);
void writebyte(uchar);
uchar retemp(void);

void main(void)
{
uchar i,temp;
delay5(1000);
while(1)
	{
	 temp=retemp();	
	 for(i=0;i<10;i++)	//连续扫描数码管10次
	 {
	 out=(temp/10)&0x0f;
	 smg1=0;
	 smg2=1;
	 delay5(1000);	//延时5ms
	 out=(temp%10)&0x0f;
	 smg1=1;
	 smg2=0;
	 delay5(1000);	//延时5ms
	 }
	}

}

 /*--------------精确延时5us子程序---------*/	
void delay5(uchar n)
{
	 do
	 {
	 _nop_();
	 _nop_();
	 _nop_();
	 n--;
	 }
	 while(n);
}
/*--------------初始化函数--------------------*/
void init_ds18b20(void)
{
	 uchar x=0; 
	 DQ =0;    
	 delay5(120); 
	 DQ =1;    
	 delay5(16);
	 delay5(80);
}
/*--------------读取一字节函数----------------*/
uchar readbyte(void)
{
	uchar i=0;
	uchar date=0;
	for (i=8;i>0;i--)
	 {
		  DQ =0;
		  delay5(1);
		  DQ =1;	//15微秒内拉释放总线
		  date>>=1;
		  if(DQ)
		  date|=0x80;
		  delay5(11);
	 }
 	return(date);
}
/*--------------写一字节函数------------------*/
void writebyte(uchar dat)
{
 uchar i=0;
 for(i=8;i>0;i--)
	 {
	  DQ =0;
	  DQ =dat&0x01;//写"1" 在15微秒内拉低
	  delay5(12);	   //写"0" 拉低60微秒
	  DQ = 1;	   
	  dat>>=1;
	  delay5(5);
	  }
}
/*--------------读取温度函数------------------*/
uchar retemp(void)
{
	uchar a,b,tt;
	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;
	tt=t*0.0625;
	return(tt);
}

3.Proteus仿真结果

DS18b20仿真


时序测试
在这里插入图片描述
分析波形可知,此程序是符合技术要求的。而当我们修改延时函数,将时序改变后再重复实验,不能正确的采集温度。因此在此实验中理解时序尤其重要 。

(二)LCD1602+DS18B20

要求:在普中开发板上,利用DS18B20采集温度并用LCD1602显示,实现单总线温度测量系统。

1.代码实现

  • OneWire.c–单总线初始化及发送接收字节
#include <REGX52.H>

//引脚定义
sbit OneWire_DQ=P3^7;

/**
  * @brief  单总线初始化
  * @param  无
  * @retval 从机响应位,0为响应,1为未响应
  */
unsigned char OneWire_Init(void)
{
	unsigned char i;
	unsigned char AckBit;
	OneWire_DQ=1;
	OneWire_DQ=0;
	i = 247;while (--i);		//Delay 500us
	OneWire_DQ=1;
	i = 32;while (--i);			//Delay 70us
	AckBit=OneWire_DQ;
	i = 247;while (--i);		//Delay 500us
	return AckBit;
}

/**
  * @brief  单总线发送一位
  * @param  Bit 要发送的位
  * @retval 无
  */
void OneWire_SendBit(unsigned char Bit)
{
	unsigned char i;
	OneWire_DQ=0;
	i = 4;while (--i);			//Delay 10us
	OneWire_DQ=Bit;
	i = 24;while (--i);			//Delay 50us
	OneWire_DQ=1;
}

/**
  * @brief  单总线接收一位
  * @param  无
  * @retval 读取的位
  */
unsigned char OneWire_ReceiveBit(void)
{
	unsigned char i;
	unsigned char Bit;
	OneWire_DQ=0;
	i = 2;while (--i);			//Delay 5us
	OneWire_DQ=1;
	i = 2;while (--i);			//Delay 5us
	Bit=OneWire_DQ;
	i = 24;while (--i);			//Delay 50us
	return Bit;
}

/**
  * @brief  单总线发送一个字节
  * @param  Byte 要发送的字节
  * @retval 无
  */
void OneWire_SendByte(unsigned char Byte)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		OneWire_SendBit(Byte&(0x01<<i));
	}
}

/**
  * @brief  单总线接收一个字节
  * @param  无
  * @retval 接收的一个字节
  */
unsigned char OneWire_ReceiveByte(void)
{
	unsigned char i;
	unsigned char Byte=0x00;
	for(i=0;i<8;i++)
	{
		if(OneWire_ReceiveBit()){Byte|=(0x01<<i);}
	}
	return Byte;
}

  • DS18B20.c–温度变化及读取温度
#include <REGX52.H>
#include "OneWire.h"

//DS18B20指令
#define DS18B20_SKIP_ROM			0xCC
#define DS18B20_CONVERT_T			0x44
#define DS18B20_READ_SCRATCHPAD 	0xBE

/**
  * @brief  DS18B20开始温度变换
  * @param  无
  * @retval 无
  */
void DS18B20_ConvertT(void)
{
	OneWire_Init();
	OneWire_SendByte(DS18B20_SKIP_ROM);
	OneWire_SendByte(DS18B20_CONVERT_T);
}

/**
  * @brief  DS18B20读取温度
  * @param  无
  * @retval 温度数值
  */
float DS18B20_ReadT(void)
{
	unsigned char TLSB,TMSB;
	int Temp;
	float T;
	OneWire_Init();
	OneWire_SendByte(DS18B20_SKIP_ROM);
	OneWire_SendByte(DS18B20_READ_SCRATCHPAD);
	TLSB=OneWire_ReceiveByte();
	TMSB=OneWire_ReceiveByte();
	Temp=(TMSB<<8)|TLSB;
	T=Temp/16.0;
	return T;
}

  • LCD1602.c–液晶显示
#include <REGX52.H>

//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0

//函数定义:
/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()
{
	unsigned char i, j;

	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
}

/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command)
{
	LCD_RS=0;
	LCD_RW=0;
	LCD_DataPort=Command;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
	LCD_RS=1;
	LCD_RW=0;
	LCD_DataPort=Data;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
	if(Line==1)
	{
		LCD_WriteCommand(0x80|(Column-1));
	}
	else if(Line==2)
	{
		LCD_WriteCommand(0x80|(Column-1+0x40));
	}
}

/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init()
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}

/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
	LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);
}

/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteData(String[i]);
	}
}

/**
  * @brief  返回值=X的Y次方
  */
int LCD_Pow(int X,int Y)
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}

/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
	unsigned char i;
	unsigned int Number1;
	LCD_SetCursor(Line,Column);
	if(Number>=0)
	{
		LCD_WriteData('+');
		Number1=Number;
	}
	else
	{
		LCD_WriteData('-');
		Number1=-Number;
	}
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFF
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i,SingleNumber;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		SingleNumber=Number/LCD_Pow(16,i-1)%16;
		if(SingleNumber<10)
		{
			LCD_WriteData(SingleNumber+'0');
		}
		else
		{
			LCD_WriteData(SingleNumber-10+'A');
		}
	}
}

/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
	}
}
  • main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "DS18B20.h"
#include "Delay.h"

float T;
void main()
{
	DS18B20_ConvertT();		//上电先转换一次温度,防止第一次读数据错误
	Delay(1000);			//等待转换完成
	LCD_Init();
	LCD_ShowString(1,1,"Temperature:");
	while(1)
	{
		DS18B20_ConvertT();	//转换温度
		T=DS18B20_ReadT();	//读取温度
		if(T<0)				//如果温度小于0
		{
			LCD_ShowChar(2,1,'-');	//显示负号
			T=-T;			//将温度变为正数
		}
		else				//如果温度大于等于0
		{
			LCD_ShowChar(2,1,'+');	//显示正号
		}
		LCD_ShowNum(2,2,T,3);		//显示温度整数部分
		LCD_ShowChar(2,5,'.');		//显示小数点
		LCD_ShowNum(2,6,(unsigned long)(T*10000)%10000,4);//显示温度小数部分
	}
}

2.实验结果

DSB1802温度采集

三、总结

在此次学习DS18B20温度传感器的过程中,不仅学习了解到了单总线的工作原理,同时深刻的体会到了时序的重要性。通过本次实验,也让我掌握了DS18B20的使用方法,为进一步学习打下基础。

  • 25
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
51单片机可以使用DS18B20数字温度传感器进行单总线多点测温。以下是一个简单的设计思路: 1. 连接DS18B20传感器到51单片机的IO口。将传感器的VCC引脚连接到单片机的电源正极,将传感器的GND引脚连接到单片机的地线,将传感器的DQ引脚连接到单片机的IO口。 2. 在单片机上编写程序,使用1-Wire协议与DS18B20传感器进行通信。可以使用单总线库函数来简化通信过程。 3. 初始化单总线总线,搜索并识别连接的DS18B20传感器。可以使用搜索函数来自动识别传感器的存在。 4. 通过发送命令,读取DS18B20传感器的温度数据。可以使用读取函数来获取传感器的温度值。 5. 将获取的温度数据进行处理和显示。可以使用LED数码管模块来显示温度值,也可以使用串口或LCD显示模块进行显示。 6. 可以通过循环的方式,实现对多个DS18B20传感器的温度进行连续测量和显示。 下面是一个示例代码,演示了如何使用51单片机DS18B20传感器进行单总线多点测温: ```c #include <reg51.h> #include <ds18b20.h> sbit DQ = P1^0; // DS18B20传感器连接的IO口 void main() { unsigned char i; unsigned char count; unsigned char temp[9]; float temperature; while (1) { count = ds18b20_search(); // 搜索并识别连接的DS18B20传感器 for (i = 0; i < count; i++) { ds18b20_convert(); // 发送温度转换命令 ds18b20_read(temp); // 读取温度数据 temperature = ds18b20_temperature(temp); // 处理温度数据 // 在这里可以将温度进行显示或其他处理 } } } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值