DS18B20温度传感器

1:DS18B20的介绍

e0ed56993ddf476f977590e51edb401d.png

2:引脚及其应用电路

37b1a9de0ace4317bc2051196a2b38c3.png

 VDD接电源上,DQ接在单片机的引脚上(注意在单片机里面,所有的IO口都是弱上拉模式),所以都需要接一个上拉电阻

那么为什么要接上拉电阻呢?

首先在DS18B20通信协议里面为了避免重复的脉冲信号对通信设备造成干扰,一般会配置为开漏输出模式,在这种模式下,只可以输出1,但是不可以输出0,那么为了让设备具有输出1的功能,一般都要接上拉电阻

 DS18B20 是单线通信,即接收和发送都是这个通信脚进行的。 其接收数据时为高电阻输入,其发送数据时是开漏输出,本身不具有输出高电平的能力,即输出0时通过MOS下拉为低电平,而输出1时,则为高阻态,是一种不稳定的状态,需要外接上拉电阻将其拉为高电平。 因此,需要外接上拉电阻,否则无法输出1。

3:DS18B20的内部结构框图

01188c42bc0948ab885659ce7050bd25.png

其中这个暂存器(对应上图的SCRATCHPAD)里面有温度传感器,还有一个字节上限和一个字节下限温度报警触发器(TL和TH),以及配置寄存器(允许用户设置分辨率,最后四位对应着0.5,0.25,0.125,0.0625)

存储器:里面有RAM寄存器和EEPROM寄存器

be70bf5423a2441e89d642629caf3929.png

寄存器是由九个字节构成,字节0和1就是温度存储器,用来存储转化好的温度,字节0代表第八位,字节1代表高八位 ,后面的那个h代表十六进制;字节2和字节3是用来设置最高温度上限和最低温度下限;字节4是配置寄存器,用来配置精度的;字节5~7是保留位,字节内部使用的;字节8CRC校验位,是64位ROM中的前56位编码的校验码。由CRC发生器产生的

1:对于配置寄存器里面的配置如下

6b45c99ae29d4dc39bb952a09e39a300.jpeg

d8a69063562e4888832d02576251145d.png

4:温度的读取和计算

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

低字节的后四个存储的是温度的小数部分,中间的七位存储的是温度的整数部分

当温度是正数的时候直接将后面的十一位二进制转化为十进制,再乘以0.0625(12位分辨率),就可以得到温度值

当温度是负的时候就要先将补码符号位不变然后取反加1,得到原码,再进行温度的计算

那么温度传感器是只能实现通信的呢,我们接下来就要讲单总线(也就是通信的媒介)

5:通信的大致流程

16ca8fd87b53428e8eadcc6a0eabe69b.png

 这里介绍一下异步和半双工,异步就是主机和从机各自的通信速率进行通信;而半双工就是通过一根线进行数据的交互

什么是寄生供电:在DS18B20内部具有一个电容,当总线为高电平的时候,会对这个电容进行充电,当总线是低电平的时候,就有内部电容进行供电,从而实现内部供电,这个就是寄生供电。

注意这个寄生供电是对从机的寄生供电,也就是DS18B20这个从机的寄生供电,而不是单片机的寄生供电

 2de049d616c84d1087899081cd032fba.png

这里的开漏输出模式和上拉电阻在引脚那一部分就已经讲了,这里就不过多的说了 

那么这个强上拉输出电路是什么:我们看上图的下面那个,中间的那个就是强上拉输出电路,中间是MOS管,如果左边接低电平,那么这个MOS管就会导通,那么DQ就接到了电源的正极,这样DS18B20就有了正负极;如果左边接高电平,MOS管没有导通,那么就采用弱上拉模式进行供电,使具有输出高电平的功能

1:初始化

40b938bc57bb4a4ea2a86f57d3b8256f.png

任何器件想要使用,首先就是需要初始化,对于DS18B20单总线设备,首先初始化单总线为高电平,然后总线开始也需要检测这条总线上是否存在DS18B20这个器件。如果这条总线上存在DS18B20,总线会根据时序要求返回一个低电平脉冲,如果不存在的话,也就不会返回脉冲,即总线保持为高电平。

初始化具体时序步骤如下:

1.单片机拉低总线至少480us,产生复位脉冲,然后释放总线(拉高电平)。
2.这时DS8B20检测到请求之后,会拉低信号,大约60~240us表示应答。
3.DS8B20拉低电平的60~240us之间,单片机读取总线的电平,如果是低电平,那么表示初始化成功
4.DS18B20拉低电平60~240us之后,会释放总线。

 2:发送一位,循环八次就发送一个字节

1ae6bc833a544f0081d52bdc01999204.png  

从机在主机将总线拉低的30us时进行读取主机发来的信号 

注意两次写入的周期之间间隔至少1us

3:接受一位,循环八次就接受一个字节 


ecb952f03f8b472db5ce718f8f735831.png

 而对于主机的读取时间就要在15us之内读取,主机拉低1~15us之后总线还是位于低电平,因为从机要进行发送0,当主机读取从机发来的数据之后,从机会释放总线,;当主机拉低1~15us之后释放总线,发现总线位于高电平,就说明从机要给主机发送高电平1

 94e5c0f3dd35490ca2cf2c2f07ada8ee.png

 发送和接受进行循环八次就可以发送和接受1个字节了

ea3bbce55ec249588f7cad5c5d866c3c.png

当采用多个DS18B20的时候,就需要通过ROM操作来控制命令总线上了某个DS18B20,如果是当DS18B20,就直接跳过ROM操作过程   

 4:数据帧

db4f270bfcbe4200a3909c1cf704fb3d.png

 通过数据帧我们就可以知道了整个过程是怎么实现的

第一个代码:DS18B20温度读取

OneWire.c

#include <REGX52.H>

sbit OneWire_DQ=P3^7;


//初始化
unsigned char OneWire_Init(void)
{
	unsigned char i;
	unsigned char AckBit;
	OneWire_DQ=1;
	OneWire_DQ=0;
	i=247;while(--i);
	OneWire_DQ=1;
	i=32;while(--i);
	AckBit=OneWire_DQ;
	i=247;while(--i);
	return AckBit;
}

//发送一位
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;
	//在上面这个代码里面,不需要用if判断是发送1还是0,如果发送0,那么Delay50us就是在低电平里面;
	//如果发送1,那么Delay50us就是在高电平里面
}
	
//接受一位
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;
}

//发送一个字节
void OneWire_SendByte(unsigned char Byte)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		OneWire_SendBit(Byte&(0x01<<i));
	}
}

//接受一个字节
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"


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

void DS18B20_ConvertT(void)
{
	OneWire_Init();
	OneWire_SendByte(DS18B20_SKIP_ROM);
	OneWire_SendByte(DS18B20_CONVERT_T);
}

float DS18B20_ReadT(void)
{
	unsigned char TLSB,TMSB;//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;
	//因为最低一位不是2的0次方,就相当于比实际温度扩大2的4次方倍数,所以要除以16来表示实际温度
	return T;
}

 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)
		{
			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);
	}

}

第二个代码:DS18B20温度报警器

Onewire.c

#include <REGX52.H>

sbit OneWire_DQ=P3^7;


//初始化
unsigned char OneWire_Init(void)
{
	unsigned char i;
	unsigned char AckBit;
	EA=0;
	OneWire_DQ=1;
	OneWire_DQ=0;
	i=247;while(--i);
	OneWire_DQ=1;
	i=32;while(--i);
	AckBit=OneWire_DQ;
	i=247;while(--i);
	EA=1;
	return AckBit;
}

//发送一位
void OneWire_SendBit(unsigned char Bit)
{
	unsigned char i;
	EA=0;
	OneWire_DQ=0;
	i=4;while(--i);  //Delay 10us
	OneWire_DQ=Bit;
	i=24;while(--i); //Delay 50us
    OneWire_DQ=1;
	EA=1;
	//在上面这个代码里面,不需要用if判断是发送1还是0,如果发送0,那么Delay50us就是在低电平里面;
	//如果发送1,那么Delay50us就是在高电平里面
}
	
//接受一位
unsigned char OneWire_ReceiveBit(void)
{
	unsigned char i;
	unsigned char Bit;
	EA=0;
	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
	EA=1;
    return Bit;
}

//发送一个字节
void OneWire_SendByte(unsigned char Byte)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		OneWire_SendBit(Byte&(0x01<<i));
	}
}

//接受一个字节
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"


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

void DS18B20_ConvertT(void)
{
	OneWire_Init();
	OneWire_SendByte(DS18B20_SKIP_ROM);
	OneWire_SendByte(DS18B20_CONVERT_T);
}

float DS18B20_ReadT(void)
{
	unsigned char TLSB,TMSB;//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;
	//因为最低一位不是2的0次方,就相当于比实际温度扩大2的4次方倍数,所以要除以16来表示实际温度
	return T;
}

main.c

#include <REGX52.H>
#include "DS18B20.h"
#include "Delay.h"
#include "LCD1602.h"
#include "AT24C02.h"
#include "key.h"
#include "Timer0_Init.h"
float T,TShow;
char TLow,THigh;
unsigned char KeyNum;

void main()
{
	DS18B20_ConvertT();
	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_ConvertT();
		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();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值