精准测温,51单片机搭载DS18B20温度传感器

DS18B20

介绍

DS18B20是一种常见的数字温度传感器,其控制命令和数据都是以数字信号的方式输入输出,相比较于模拟温度传感器,具有功能强大、硬件简单、易扩展、抗干扰性强等特点,其测温范围大约在-55°C 到 +125°C之间,它的通信接口是1-Wire(单总线),其它特征有可形成总线结构、内置温度报警功能、可寄生供电(可寄生供电即无需接VCC,只需要接数据线与GND即可完成数据通信,节省线路)

关于温度的测量,我们可以用温度传感器或者数字温度传感器,但是数字温度传感器总体会比温度传感器的精度要高一些。

本单片机的DS18B20无需设计电路,只需要通过引脚与单总线通信协议,将RAM里面的温度转化读取出来即可,因此我就不在说明里面的电路构造了

存储器

TEMPERATURE SENSOR(温度传感器)相当于内部的模拟传感器集成部分。

当发出指令让其开始温度转换时,该部分就开始工作,并将数据放入RAM中。

ALARM HIGH TRIGGER REGISTER(TH,报警高触发器)用于存储温度上限阈值,实现温度报警。

存储介质为EEPROM,掉电不丢失数据。

ALARM LOW TRIGGER REGISTER(TL,报警低触发器)用于存储温度下限阈值,实现温度报警。

存储介质为EEPROM,掉电不丢失数据。

CONFIGURATION REGISTER(配置寄存器)用于设置分辨率(精度)

——这里出厂默认最高精度为0.0625℃。

可以通过配置寄存器中两位(其他位无效)来降低精度,最低为0.5℃。

当精度降低时,温度转换速率会迅速提升。(相当于处理的数据少了,流程简化,速度就上去了)

存储介质为EEPROM,掉电不丢失数据。

8-BIT CRC GENERATOR(8位CRC生成器)中CRC为校验码,该部分将RAM之前的数据进行校验,并生成一个校验码放于后端,通讯时检测即可判断数据是否正确。(CRC为校正率较高的一种校验码)

温度存储部分:

Byte 0存储的是温度的最低有效字节,括号内为默认值。

Byte1存储的是温度的最高有效字节,括号内为默认值。

两个字节共同组成温度值,在未变动情况下,默认值为85℃。(不同单片机默认值不一样)

配置温度数据部分:

Byte 2存储为温度上限阈值,Byte 3存储为温度下限阈值,Byte 4存储为精度(分辨率)。

右边的EEPROM负责存储数据,需要读取与写入数据要先经过RAM部分,然后通过指令将数据存储进EEPROM中(当上电时,EEPROM的数据会自动放入RAM中)

Byte 5~Byte 7为保留部分,说白了,就是没有任何用处,只不过为了以后的功能添加而预留的位置。

CRC校验部分:

将前面八个字节进行运算,并生成CRC校验码。

可以读取CRC校验码,并对前面八个字节数据进行运算,如果运算结果与CRC相同,那么校验正确,反之数据出错。

单总线时序结构

初始化

由于这是单总线,所以数据传送的时间一定要严格

图中黑粗线Bus master pulling low(主机拉低)是将总线拉低;而图中黑细线Resistor pullup为上拉电阻将总线上拉(因为是弱上拉,所以利用曲线来表示弱的状态,而不是强上拉一下拉上去)

当从机存在时,从机会将总线拉低(即图中棕线DS18B20 pulling low)。最终上拉电阻拉高,总线恢复空闲状态。由于中间的操作间隔时间是范围值,没有确切的参考数据,于是取中间值来作为等待间隔时间,对于以上的操作,可以将过程分为复位与响应两个部分

发送一位

我们可以将上面的部分分为发送0与发送1两部分的时序。换句话说,就是通过时间的不同来区分数据是输入1还是0.

发送0时,拉低超过60us且不能超过120us,否则如果超过120us时,可能会变为初始化操作(至少480us),两个数据发送中间存在总线恢复时间,因此连续发送两位不能低于这个恢复时间。

图中阴影部分,表示可以在1~15us时释放总线即可,这时电阻都会将总线拉高。

从机在主线拉低30us后进行操作(如果发送0,就一直保持总线拉低;如果发送1,就在主线拉低后,将总线释放),这样就可以很清楚的确认要发送的数据了。若在主线拉低15us后进行操作,就有可能会出现一些数据传输的偏差,因为发送1的时序在15us时,正处于主线拉高的状态,我们无法准确的判断此时是否已经完成拉高的操作,因此在30us后进行最合适。

接收一位

当主机拉低总线后,从机如果需要发0,那么也拉低总线,当主机在1~15us后释放总线时,因为从机拉低着,因此总线状态为拉低状态。

当主机拉低总线后,从机如果需要发1,那么就不动总线。当主机经过1~15us后释放总线时,因为没有被拉低,因此总线状态为拉高状态。在总机拉低后15us内读取总线电平(I/O口),即可知道从机需要发送的数据。

 发送与接收一个字节

DS18B20操作

首先,我们需要将DS18B20进行初始化,让从机复位,主机判断从机是否响应

然后,就是ROM的操作和功能指令的操作

ROM

对于ROM的操作,就是ROM指令+本指令需要的读写操作

SEARCH ROM就是表面意思,搜索ROM。

READ ROM就是读取ROM:先调用读取指令,再调用读取时序(接收部分),就可以将ROM读取出来。

MATCH ROM就是匹配ROM:先发送匹配指令,然后发送ROM地址,实现利用该指令匹配与哪个设备进行通信的效果。

SKIP ROM就是跳过ROM:调用跳过指令,直接将ROM部分跳过进入下一部分。但是,要注意的是,当总线上只有一个设备时使用。多个设备挂载时,使用这个指令会产生混乱,无法确定是哪个设备

ALARM SEARCH就是报警搜索:当挂载多个设备的情况下,某个设备温度超过设定温度阈值时,会产生报警,利用这个指令即可快速搜索出超过温度阈值的设备。

 功能指令

CONVERT T(温度变换):调用该指令时,温度传感器部分会将温度数据的模拟信号转化为数字信号,并将数据放在RAM中,从而达到RAM里面的温度数据与外界温度几乎同步的状态。

WRITE SCRATCHPAD(写暂存器):调用该指令后,再接着写入时序(发送一个字节部分),那么该字节就会被写入RAM中的配置温度数据部分。

READ SCRATCHPAD(读暂存器):调用该指令后,再接着接收时序(接收一个字节部分部分),那么DS18B20就会依次将RAM里面内容读取出来(直到CRC)。至于代码的实现只需要读取前两个字节(温度数据)即可。

COPY SCRATCHPAD(复制暂存器):调用该指令后,DS18B20就会把RAM中配置温度数据部分写入EEPROM中(实现掉电不丢失)。

RECALL E2(重调E2暂存器):调用该指令后,DS18B20就会把EEPROM中数据放入RAM中(覆盖之前的数据)。

READ POWER SUPPLY(读取设备供电模式):调用该指令后,紧跟着读取一位时序(接收一位字节部分),就会返回当前供电状态(独立供电或寄生供电),因为进行操作(如数据变换)时,需要高电能补充;因此在寄生供电状态下,需要调强上拉模式;如果在独立供电时,就不需要调;

DS18B20数据帧

以上是温度变换与读取的相关操作。

 温度存储的原理

这里的小数部分依靠二分法的机制进行补位,最小分度为0.0625,即每次数据加一,即温度增加0.0625℃。这里数据的存储方式以二进制补码的形式存储。即正数保持不变,负数则将对应的正数每个位取反,最后加一。

代码

温度读取代码

主函数代码

#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "OneWire.h"
#include "DS18B20.h"


float T;

void main()
{
	LCD_Init();
	LCD_ShowString(1,1,"Temperature:");
	while(1)
	{
		DS18B20_ConverT();
		T=DS18B20_ReadT();
		if(T<0)
		{
			LCD_ShowChar(2,1,'-');
			T=-T;
		}
		else
		{
			LCD_ShowChar(2,1,'+');
		}
		LCD_ShowNum(2,2,T,3);
		LCD_ShowChar(2,5,'.');
		LCD_ShowNum(2,6,(unsigned long)(T*10000)%10000,4);//这样便可以将小数部分进行显示了
	}
}

 部分模块代码

#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;
}
#include <REGX52.H>
#include "OneWire.h"
#include "LCD1602.h"

#define DS18B20_SKIP_ROM			0xCC
#define DS18B20_CONVERT_T			0x44
#define DS18B20_READ_SCARTCHPAD		0xBE



/**
  * @brief  DS18B20开始温度变换
  * @param  无
  * @retval 无
  */
void DS18B20_ConverT(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_SCARTCHPAD);
	TLSB=OneWire_ReceiveByte();
	TMSB=OneWire_ReceiveByte();
	Temp=(TMSB<<8)|TLSB;
	T=Temp/16.0;
	return T;
}

温度警报器代码

主函数代码

#include <REGX52.H>
#include "AT24C02.h"
#include "Delay.h"
#include "I2C.h"
#include "LCD1602.h"
#include "Key.h"
#include "DS18B20.h"
#include "OneWire.h"
#include "Timer0.h"


float T,TShow;
char TLow,THigh;		//定义温度上限与下限阈值
unsigned char KeyNum;

void main()
{
	DS18B20_ConverT();		//使得温度数据不是默认值
	Delay(1000);
	THigh=AT24C02_ReadByte(0);		//读取掉电前的数据
	TLow=AT24C02_ReadByte(1);
	if(THigh>125||TLow<-55||THigh<=TLow)
	{
		THigh=20;
		TLow=15;
	}
	LCD_Init();
	LCD_ShowString(1,1,"T:");
	LCD_ShowString(2,1,"TH:");
	LCD_ShowString(2,9,"TL:");
	LCD_ShowSignedNum(2,4,THigh,3);		//上限阈值
	LCD_ShowSignedNum(2,12,TLow,3);		//下限阈值
	Timer0_Init();
	while(1)
	{
		
		KeyNum=Key();
		/*温度读取及显示*/
		DS18B20_ConverT();
		T=DS18B20_ReadT();
		if(T<0)
		{
			LCD_ShowChar(1,3,'-');
			TShow=-T;
		}
		else
		{
			LCD_ShowChar(1,3,'+');
			TShow=T;
		}
		LCD_ShowNum(1,4,TShow,3);
		LCD_ShowChar(1,7,'.');
		LCD_ShowNum(1,8,(unsigned long)(TShow*100)%100,2);
		/*阈值判断及显示*/
		if(KeyNum)		//在按键按下时才进行检查,减少扫描时间
		{
			if(KeyNum==1)
			{
				THigh++;
				if(THigh>125){THigh=125;}
			}
			if(KeyNum==2)
			{
				THigh--;
				if(THigh<=TLow){THigh++;}
			}
			if(KeyNum==3)
			{
				TLow++;
				if(TLow>=THigh){TLow--;}
			}
			if(KeyNum==4)
			{
				TLow--;
				if(TLow<-55){TLow=-55;}
			}
			LCD_ShowSignedNum(2,4,THigh,3);
			LCD_ShowSignedNum(2,12,TLow,3);	
			AT24C02_WriteByte(0,THigh);		//保存上限下限的阈值数据
			Delay(5);
			AT24C02_WriteByte(1,TLow);
			Delay(5);
			
		}
		if(T>THigh)
		{
			LCD_ShowString(1,13,"OV:H");
		}
		else if(T<TLow)
		{
			LCD_ShowString(1,13,"OV:L");
		}
		else
		{
			LCD_ShowString(1,13,"    ");
		}
		
		
	}
}


void Timer0_Routine()  interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count++;
	if(T0Count>=20)
	{
		T0Count=0;
		Key_Loop();		//在较短的时间内不断判断按键的状态
	}
	
}

部分模块代码

#include <REGX52.H>
#include "Delay.h"


unsigned char Key_KeyNumber;

unsigned char Key(void)
{
	unsigned char Temp=0;
	Temp=Key_KeyNumber;
	Key_KeyNumber=0;
	return Temp;
}

unsigned char Key_GetState()
{
	unsigned char KeyNumber=0;
	if(P3_1==0){KeyNumber=1;}
	if(P3_0==0){KeyNumber=2;}
	if(P3_2==0){KeyNumber=3;}
	if(P3_3==0){KeyNumber=4;}
	
	return KeyNumber;
}


void Key_Loop(void)
{
	static unsigned char NowState,LastState;
	LastState=NowState;			//由于按键按下时的前后状态会不同,就可以用判断的方法去确定是否按下按键
	NowState=Key_GetState();		//这样做就可以避免调入死循环中
	if(LastState==1&&NowState==0)
	{
		Key_KeyNumber=1;
	}
	if(LastState==2&&NowState==0)
	{
		Key_KeyNumber=2;
	}
	if(LastState==3&&NowState==0)
	{
		Key_KeyNumber=3;
	}
	if(LastState==4&&NowState==0)
	{
		Key_KeyNumber=4;
	}
}
unsigned char OneWire_Init(void)
{
	unsigned char i;
	unsigned char AckBit;
	EA=0;		//关闭定时器0
	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
	EA=1;		//打开定时器0
	return AckBit;
}


void OneWire_SendBit(unsigned char Bit)
{
	
	unsigned char i;
	EA=0;		//关闭定时器0
	OneWire_DQ=0;
	i = 4;while (--i);		//Delay  10us
	OneWire_DQ=Bit;
	i = 24;while (--i);		//Delay  50us
	OneWire_DQ=1;
	EA=1;		//打开定时器0
}
物联沃分享整理
物联沃-IOTWORD物联网 » 精准测温,51单片机搭载DS18B20温度传感器

发表评论