一、单总线温度传感器DB18B20
(一)DB18B20介绍
1.工作原理:
a.DS18B20基于热敏电阻原理工作,它的内部包含一个温度传感器和一个数字转换器,能够将温度转换为数字信号输出。
b.DS18B20通过单总线接口与微控制器通信,可以在单一总线上同时连接多个传感器,并且只需要一个引脚进行数据传输。
2.传感器特点:
a.高精度:DS18B20具有较高的温度测量精度,通常为±0.5°C。
b.数字输出:输出为数字信号,易于与数字系统集成。
c.单总线接口:只需一个引脚即可完成数据和电源的传输,节省了连接线路和引脚资源。
d.范围广:可测量的温度范围通常在-55°C至+125°C之间。
3.使用方法:
a.通过微控制器的GPIO引脚连接DS18B20的数据引脚。
b.DS18B20通常由3个引脚组成:数据引脚(DQ)、电源引脚(VCC)和地引脚(GND)。
c.通过单总线接口发送命令给DS18B20,以启动温度转换,并从DS18B20读取温度数据。
4.应用领域:
a.家用电器:温度计、温度控制器等。
b.工业领域:工业自动化、环境监测等。
c.科学研究:实验室温度监测等。
DB18B20温度传感器实物图:
DB18B20引脚以及封装原理图:
DB18B20电路图:
单个DS18B20接线方式: VDD接到电源,DQ接单片机引脚,同时外加上拉电阻,GND接地
DS18B20数字温度传感器通常具有三个引脚,它们分别是数据引脚(DQ)、电源引脚(VCC)和地引脚(GND)。这三个引脚组成了DS18B20传感器的基本连接,其中数据引脚用于通信,电源引脚用于供电,地引脚用于电路的接地。通过正确连接这些引脚,可以与DS18B20传感器进行通信,并读取其输出的温度数据。
1.数据引脚(DQ):
(1)数据引脚是与微控制器通信的引脚,用于发送和接收数据。
(2)在单总线总线上,所有DS18B20传感器都连接到相同的数据总线上,并且每个传感器都有一个唯一的64位ROM代码,用于在总线上标识和寻址传感器。
2.电源引脚(VCC):
(1)电源引脚用于提供传感器所需的电源供应。
(2)DS18B20通常在3V至5.5V的电压范围内工作,可以通过连接电源引脚来为传感器供电。
3.地引脚(GND):
(1)地引脚用于连接传感器的地(GND)或负极,以完成电路的闭合。
(2)通过连接地引脚,传感器和微控制器共享相同的电源地。
(二)DB18B20传感器参数
1.测温范围为-55℃到+125℃,在-10℃到+85℃范围内误差为±0.4°。
2.返回16位二进制温度数值
3.主机和从机通信使用单总线,即使用单线进行数据的发送和接收
4.在使用中不需要任何外围元件,独立芯片即可完成工作。
5.掉电保护功能 DS18B20 内部含有 EEPROM ,通过配置寄存器可以设定数字转换精度和报警温度,在系统掉电以后,它仍可保存分辨率及报警温度的设定值。
6.每个DS18B20都有独立唯一的64位-ID,此特性决定了它可以将任意多的DS18b20挂载到一根总线上,通过ROM搜索读取相应DS18B20的温度值
7.宽电压供电,电压2.5V~5.5V
8.DS18B20返回的16位二进制数代表此刻探测的温度值,其高五位代表正负。如果高五位全部为1,则代表返回的温度值为负值。如果高五位全部为0,则代表返回的温度值为正值。后面的11位数据代表温度的绝对值,将其转换为十进制数值之后,再乘以0.0625即可获得此时的温度值。
(三)DB18B20内部结构
1.DB18B20寄生电源
DS18B20寄生电源是指DS18B20数字温度传感器在工作时只利用单总线上的数据引脚来获取所需的电源,而不需要额外的电源引脚。这种模式称为寄生电源模式,它使得DS18B20可以在仅连接一个引脚的情况下实现温度测量和通信。通过寄生电源模式,DS18B20传感器可以仅使用单个引脚就能完成电源供应和通信,这使得其在嵌入式系统中的应用更加方便,并且可以减少系统中的引脚数量和连接复杂度。
寄生电源模式的工作原理:
(1)电源供应:
a.在寄生电源模式下,DS18B20传感器在需要电源时会从单总线上的数据引脚获取所需的电源。
b.当微控制器向传感器发送电源提供命令时,传感器会利用单总线上的数据引脚来提取电源。
(2)温度测量:
a.一旦传感器获得电源,它将开始进行温度测量。
b.DS18B20具有内部的温度转换器,可将温度转换为数字信号。
(3)通信:
a.DS18B20通过单总线与微控制器进行通信。
b.微控制器可以向传感器发送命令,如启动温度转换、读取温度数据等。
2.DB18B20内部组成
DB18B20内部主要由以下3部分组成: 64 位ROM,高速暂存器,存储器
(1)64 位ROM
- 每个DS18B20传感器都具有一个唯一的64位ROM代码。这个代码是由出厂时编程的,每个传感器都不同,因此可以用于对不同传感器进行区分。也实现了一根总线上可以挂接多个DS18B20。
- ROM代码的一部分被用作传感器的地址,通常是最后的8位。这个地址可以在系统中用于区分多个DS18B20传感器,以便同时管理多个传感器的数据和通讯。
(2)高速暂存器
- 温度传感器
- 一个字节的温度上限和温度下限报警触发器(TH和TL)
- 配置寄存器允许用户设定9位,10位,11位和12位的温度分辨率,分别对应着温度的分辨率为:0.5°C,0.25°C,0.125°C,0.0625°C,默认为12位分辨率
高速暂存器组成:
(3)存储器
- DS18B20可以通过单总线协议将温度测量结果发送给外部系统。传感器内部会有一个存储器用于暂时存储最近的温度测量结果,以便在需要时能够读取和传输给主控制器。
- DS18B20具有一些配置寄存器,用于存储传感器的工作模式、分辨率设置、温度阈值等配置信息。这些配置存储器中的数据可以通过单总线协议进行读写,以调整传感器的工作参数。
- 一些DS18B20型号可能还包含EEPROM,用于存储用户自定义的配置或校准参数。EEPROM具有可擦除和可编程的特性,用户可以通过特定的命令来写入和擦除其中的数据。
3.DS18B20温度读取与计算
(1)启动温度转换
通过单线总线发送命令,启动DS18B20进行温度测量。具体命令是Convert T(0x44)。
(2)等待测量完成
DS18B20在启动温度测量后需要一定的时间来完成测量。在测量期间,控制器可以通过轮询或等待一段时间来确保测量完成。
(3)读取温度数据
一旦温度测量完成,可以通过单线总线发送Read Scratchpad(0xBE)命令读取DS18B20的暂存器中的数据。温度数据通常存储在Scratchpad的前两个字节中(字节0和字节1)。
(4)计算实际温度值
从Scratchpad读取的前两个字节(16位数据)包含了原始的温度测量结果。这16位数据是一个有符号整数,表示的单位是0.0625摄氏度。将两个字节组合成一个16位有符号整数,然后乘以0.0625,即可得到实际的温度值。
计算公式为:
temperature = raw_temp * 0.0625
其中,raw_temp是从Scratchpad读取的16位原始温度值。
(5)返回温度值
根据上述计算得到实际的温度值后,可以将其返回给主控制器或进行其他操作。
温度读取:
高字节的五个S为符号位,温度为正值时S=1,温度为负值时S=0
剩下的11位为温度数据位,对于12位分辨率,所有位全部有效,对于11位分辨率,位0(bit0)无定义,对于10位分辨率,位0和位1无定义,对于9位分辨率,位0,位1,和位2无定义
温度计算方法:
a.当五个符号位S=1时,温度为正,直接将后面11位二进制转换为十进制,再乘以0.0625(12位分辨率),就可以得到温度值;
举例:+10.125℃输出数字是00A2(0000 0000 1010 0010)
二进制数0000 0000 1010 0010转换为十进制数为162,温度为:162*0.0625=10.125℃
b.当五个符号位S=0时,温度为负,先将后面的11位二进制补码变为原码(符号位不变,数值位取反后加1),再计算十进制值。再乘以0.0625(12位分辨率),就可以得到温度值;
举例:-55℃的输出数字是FC90(1111 1100 1001 0000)。
首先取反,然后+1,转换成原码为:11111011 01101111
数值位转换成10进制是870,温度为:-0.0625x870=-55°C
(四)1-Wire单总线
在每个DS18B20内部有唯一的64位长的序列号,这个序列号值就存在DS18B20内部的ROM中。开始的8位是产品类型编码(DS18B20是28H),接着的48位是每个器件唯一的序号,最后的8位是CRC校验码。
DS18B20的工作步骤大致分为三步:
1.初始化DS18B20
2.执行ROM指令
3.执行DS18B20功能指令
具体执行操作:
- 系统启动时,需要对DS18B20进行初始化设置。这可能涉及配置1-Wire通信协议、设置分辨率(通常为9位、10位、11位或12位)等。
- 主控制器通过总线发送命令,要求DS18B20开始温度转换。DS18B20接收到命令后开始采集环境温度。
- DS18B20内部的温度传感器开始测量环境温度,并将结果转换为数字信号。
- 一段时间后,主控制器通过总线发送读取命令,要求DS18B20将转换后的温度数据发送回来。
- DS18B20将存储的温度数据发送回主控制器。这通常是通过单线数字总线进行通信,主控制器读取总线上传输的数据。
1.初始化DS18B20:
(1)单片机拉低总线至少480us,产生复位脉冲,然后释放总线(拉高电平)
(2)这时DS8B20检测到请求之后,会拉低信号,大约60~240us表示应答
(3)DS8B20拉低电平的60~240us之间,单片机读取总线的电平,如果是低电平,那么表示初始化成功
(4)DS18B20拉低电平60~240us之后,会释放总线
代码如下:
unsigned int Init_DS18B20(void)
{
unsigned int x=0;
DQ = 1; //DQ复位
delay(4); //稍做延时
DQ = 0; //单片机将DQ拉低
delay(60); //精确延时,大于480us
DQ = 1; //拉高总线
delay(8);
x = DQ; //稍做延时后,如果x=0则初始化成功,x=1则初始化失败
delay(4);
return x;
}
2.控制器写时序
a.单片机想要给DS18B20写入一个0时,需要将单片机引脚拉低,保持低电平时间要在60~120us之间,然后释放总线
b.单片机想要给DS18B20写入一个1时,需要将单片机引脚拉低,拉低时间需要大于1us,然后在15us内拉高总线
代码如下:
void WriteOneChar(unsigned char dat)
{
unsigned char i=0;
for (i=8; i>0; i--)
{
DQ = 0;
DQ = dat&0x01; //与1按位与运算,dat最低位为1时DQ总线为1,dat最低位为0时DQ总线为0
delay(4);
DQ = 1;
dat>>=1;
}
delay(4);
}
3.控制器读时序
DS18B20在检测到总线被拉低1微秒后,便开始送出数据,若是要送出0就把总线拉为低电平直到读周期结束。若要送出1则释放总线为高电平。
所有读时隙必须至少需要60us,且在两次独立的时隙之间至少需要1ps的恢复时间。读时序时是先读低字节,在读高字节,也就是先读取高速暂存器的第0个字节(温度的低8位),在读取高速暂存器的第1个字节(温度的高8位) 我们正常使用DS18B20读取温度读取两个温度字节即可。
总体代码如下(STM32):
#include "ds18b20.h"
#include "delay.h"
//复位DS18B20
void DS18B20_Rst(void)
{
DS18B20_IO_OUT(); //SET PG11 OUTPUT
DS18B20_DQ_OUT=0; //拉低DQ
delay_us(750); //拉低750us
DS18B20_DQ_OUT=1; //DQ=1
delay_us(15); //15US
}
//等待DS18B20的回应
//返回1:未检测到DS18B20的存在
//返回0:存在
u8 DS18B20_Check(void)
{
u8 retry=0;
DS18B20_IO_IN(); //SET PG11 INPUT
while (DS18B20_DQ_IN&&retry<200)
{
retry++;
delay_us(1);
};
if(retry>=200)return 1;
else retry=0;
while (!DS18B20_DQ_IN&&retry<240)
{
retry++;
delay_us(1);
};
if(retry>=240)return 1;
return 0;
}
//从DS18B20读取一个位
//返回值:1/0
u8 DS18B20_Read_Bit(void)
{
u8 data;
DS18B20_IO_OUT(); //SET PG11 OUTPUT
DS18B20_DQ_OUT=0;
delay_us(2);
DS18B20_DQ_OUT=1;
DS18B20_IO_IN(); //SET PG11 INPUT
delay_us(12);
if(DS18B20_DQ_IN)data=1;
else data=0;
delay_us(50);
return data;
}
//从DS18B20读取一个字节
//返回值:读到的数据
u8 DS18B20_Read_Byte(void)
{
u8 i,j,dat;
dat=0;
for (i=1;i<=8;i++)
{
j=DS18B20_Read_Bit();
dat=(j<<7)|(dat>>1);
}
return dat;
}
//写一个字节到DS18B20
//dat:要写入的字节
void DS18B20_Write_Byte(u8 dat)
{
u8 j;
u8 testb;
DS18B20_IO_OUT(); //SET PG11 OUTPUT;
for (j=1;j<=8;j++)
{
testb=dat&0x01;
dat=dat>>1;
if (testb)
{
DS18B20_DQ_OUT=0; // Write 1
delay_us(2);
DS18B20_DQ_OUT=1;
delay_us(60);
}
else
{
DS18B20_DQ_OUT=0; // Write 0
delay_us(60);
DS18B20_DQ_OUT=1;
delay_us(2);
}
}
}
//开始温度转换
void DS18B20_Start(void)
{
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc); // skip rom
DS18B20_Write_Byte(0x44); // convert
}
//初始化DS18B20的IO口 DQ 同时检测DS的存在
//返回1:不存在
//返回0:存在
u8 DS18B20_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE); //使能PORTG口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //PORTG.11 推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOG, &GPIO_InitStructure);
GPIO_SetBits(GPIOG,GPIO_Pin_11); //输出1
DS18B20_Rst();
return DS18B20_Check();
}
//从ds18b20得到温度值
//精度:0.1C
//返回值:温度值 (-550~1250)
short DS18B20_Get_Temp(void)
{
u8 temp;
u8 TL,TH;
short tem;
DS18B20_Start (); // ds1820 start convert
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc); // skip rom
DS18B20_Write_Byte(0xbe); // convert
TL=DS18B20_Read_Byte(); // LSB
TH=DS18B20_Read_Byte(); // MSB
if(TH>7)
{
TH=~TH;
TL=~TL;
temp=0; //温度为负
}else temp=1; //温度为正
tem=TH; //获得高八位
tem<<=8;
tem+=TL; //获得底八位
tem=(float)tem*0.625; //转换
if(temp)return tem; //返回温度值
else return -tem;
}
(五)ROM指令与DS18B20功能指令
ROM指令:
采用多个DS18B20时,需要写ROM指令来控制总线上的某个DS18B20。如果是单个DS18B20,直接写跳过ROM指令0xCC即可
DS18B20功能指令:
温度转换 0x44:开启温度读取转换,读取好的温度会存储在高速暂存器的第0个和第一个字节中
读取温度 0xBE:读取高速暂存器存储的数据
二、DB18B20实际应用
(一)DS18B20和LED数码管
1.任务要求:利用DS18B20和LED数码管实现单总线温度测量系统
2.proteus电路图
3.代码实现
#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++)
{
out=(temp/10)&0x0f;
smg1=0;
smg2=1;
delay5(1000);
out=(temp%10)&0x0f;
smg1=1;
smg2=0;
delay5(1000);
}
}
}
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;
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;
delay5(12);
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);
}