1:DS18B20的介绍
2:引脚及其应用电路
VDD接电源上,DQ接在单片机的引脚上(注意在单片机里面,所有的IO口都是弱上拉模式),所以都需要接一个上拉电阻
那么为什么要接上拉电阻呢?
首先在DS18B20通信协议里面为了避免重复的脉冲信号对通信设备造成干扰,一般会配置为开漏输出模式,在这种模式下,只可以输出1,但是不可以输出0,那么为了让设备具有输出1的功能,一般都要接上拉电阻
DS18B20 是单线通信,即接收和发送都是这个通信脚进行的。 其接收数据时为高电阻输入,其发送数据时是开漏输出,本身不具有输出高电平的能力,即输出0时通过MOS下拉为低电平,而输出1时,则为高阻态,是一种不稳定的状态,需要外接上拉电阻将其拉为高电平。 因此,需要外接上拉电阻,否则无法输出1。
3:DS18B20的内部结构框图
其中这个暂存器(对应上图的SCRATCHPAD)里面有温度传感器,还有一个字节上限和一个字节下限温度报警触发器(TL和TH),以及配置寄存器(允许用户设置分辨率,最后四位对应着0.5,0.25,0.125,0.0625)
存储器:里面有RAM寄存器和EEPROM寄存器
寄存器是由九个字节构成,字节0和1就是温度存储器,用来存储转化好的温度,字节0代表第八位,字节1代表高八位 ,后面的那个h代表十六进制;字节2和字节3是用来设置最高温度上限和最低温度下限;字节4是配置寄存器,用来配置精度的;字节5~7是保留位,字节内部使用的;字节8CRC校验位,是64位ROM中的前56位编码的校验码。由CRC发生器产生的
1:对于配置寄存器里面的配置如下
4:温度的读取和计算
高字节的五个S为符号位,温度为正的时候S=0;温度为负的时候S=1;
低字节的后四个存储的是温度的小数部分,中间的七位存储的是温度的整数部分
当温度是正数的时候直接将后面的十一位二进制转化为十进制,再乘以0.0625(12位分辨率),就可以得到温度值
当温度是负的时候就要先将补码符号位不变然后取反加1,得到原码,再进行温度的计算
那么温度传感器是只能实现通信的呢,我们接下来就要讲单总线(也就是通信的媒介)
5:通信的大致流程
这里介绍一下异步和半双工,异步就是主机和从机各自的通信速率进行通信;而半双工就是通过一根线进行数据的交互
什么是寄生供电:在DS18B20内部具有一个电容,当总线为高电平的时候,会对这个电容进行充电,当总线是低电平的时候,就有内部电容进行供电,从而实现内部供电,这个就是寄生供电。
注意这个寄生供电是对从机的寄生供电,也就是DS18B20这个从机的寄生供电,而不是单片机的寄生供电
这里的开漏输出模式和上拉电阻在引脚那一部分就已经讲了,这里就不过多的说了
那么这个强上拉输出电路是什么:我们看上图的下面那个,中间的那个就是强上拉输出电路,中间是MOS管,如果左边接低电平,那么这个MOS管就会导通,那么DQ就接到了电源的正极,这样DS18B20就有了正负极;如果左边接高电平,MOS管没有导通,那么就采用弱上拉模式进行供电,使具有输出高电平的功能
1:初始化
任何器件想要使用,首先就是需要初始化,对于DS18B20单总线设备,首先初始化单总线为高电平,然后总线开始也需要检测这条总线上是否存在DS18B20这个器件。如果这条总线上存在DS18B20,总线会根据时序要求返回一个低电平脉冲,如果不存在的话,也就不会返回脉冲,即总线保持为高电平。
初始化具体时序步骤如下:
1.单片机拉低总线至少480us,产生复位脉冲,然后释放总线(拉高电平)。
2.这时DS8B20检测到请求之后,会拉低信号,大约60~240us表示应答。
3.DS8B20拉低电平的60~240us之间,单片机读取总线的电平,如果是低电平,那么表示初始化成功
4.DS18B20拉低电平60~240us之后,会释放总线。
2:发送一位,循环八次就发送一个字节
从机在主机将总线拉低的30us时进行读取主机发来的信号
注意两次写入的周期之间间隔至少1us
3:接受一位,循环八次就接受一个字节
而对于主机的读取时间就要在15us之内读取,主机拉低1~15us之后总线还是位于低电平,因为从机要进行发送0,当主机读取从机发来的数据之后,从机会释放总线,;当主机拉低1~15us之后释放总线,发现总线位于高电平,就说明从机要给主机发送高电平1
发送和接受进行循环八次就可以发送和接受1个字节了
当采用多个DS18B20的时候,就需要通过ROM操作来控制命令总线上了某个DS18B20,如果是当DS18B20,就直接跳过ROM操作过程
4:数据帧
通过数据帧我们就可以知道了整个过程是怎么实现的
第一个代码: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();
}
}