DS18B20 数字温度传感器提供 9-Bit 到 12-Bit 的摄氏温度测量精度和一个用户可编程的非易失性且具有过温和低温触发报警的报警功能。DS18B20 采用的 1-Wire 通信即仅采用一个数据线(以及地)与微控制器进行通信。该传感器的温度检测范围为-55℃ 至 +125℃,并且在温度范围超过-10℃ 至 85℃ 之外时还具有 +-0.5℃ 的精度。此外,DS18B20 可以直接由数据线供电而不需要外部电源供电。
每片 DS18B20 都有一个独一无二的 64 位序列号,所以一个 1-Wire 总线上可连接多个 DS18B20 设备。因此,在一个分布式的大环境里用一个微控制器控制多个 DS18B20 是非常简单的。这些特征使得其在 HVAC 环境控制,在建筑、设备及机械的温度监控系统,以及温度过程控制系统中有着很大的优势。DS18B20 数字温度传感器的 3 种封装图如下图所示:
独特的 1-Wire 总线接口仅需要一个管脚来通信,应用于温度控制系统,工业系统,民用产品,温度传感器,或者任何温度检测系统中。RY-51 开发板根据数据手册要求,温度采集模块电路设计如下所示:
其中,Temp 与单片机的 P2.2 引脚相连接。
DS18B20 是一款直接式数字温度传感器,温度数据格式可设置为 9、10、11、12 位模式,出厂初始值为 12 为格式。对应的最小温度精度可达到 0.5℃、0.25℃、0.125℃ 或者 0.0625℃。当向 DS18B20 写入温度转换字 44H 之后,传感器内部会完成温度数据的采集,并将温度值以一个 16 位的有符号数存储在 2 个 8 位的存储单元中,如下图所示。
如上图所示,由两个字节的存储单元存储了温度数据,高 5 位的 s 表示符号,当 s=0 时,表示温度为正。当 s=1 时,表示温度为负值, 并以补码形式存储温度值,温度值与存储值转换关系如图 12-3 所示。当向传感器写入读温度指令 BEH 后,传感器会将上面两个 8 位存储单元的内容从最低位开始依次输出。上电后,温度存储单元中存储的初始温度为 85℃。
我们使用数字温度传感器的最终目的是通过它采集到温度值,单片机获得 DS18B20 温度值的过程一般遵循以下协议:传感器初始化——>ROM 操作命令——> 存储器操作命令——> 处理数据。下面逐一进行介绍。
单总线上的所有处理均从初始化序列开始。初始化序列由总线主机发出一复位脉冲,当传感器收到复位脉冲后,会返回存在脉冲,表示总线上存在传感器并准备好可以接收由主机发来的操作指令。初始化指令如下图所示,下面我们讲解如何来理解这个图的时序,图中包括四种线型,第一种为粗的实线表示总线由主机拉低,第二种为粗的虚线表示总线由 DS18B20 拉低,第三种为粗的实线虚线交叉表示主机和 DS18B20 同时拉低总线,第四种为细实线由阻抗拉高,即释放总线。
下面我们来分析一下初始化的时序步骤:
DS18B20 复位脉冲时序如下图:
DS18B20 初始化函数如下所示:
#include< reg52.h >
#include < intrins.h >
#define uchar unsigned char
#define uint unsigned int
sbit DS18B20 = P2^2; //DS18B20传感器I/O口定义
void DelayT_10us(uchar count)
{
while(count--)
{ //模拟10us延时
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
}
void Init_DS18B20(void)
{
//单片机拉低总线并延时600us
DS18B20 = 0;
DelayT_10us(50);
//单片机释放总线并延时60us
DS18B20 = 1;
DelayT_10us(6);
//当DS18B20返回低电平时,表示总线上存在传感器
while(!DS18B20);
DelayT_10us(50);
}
如上代码所示,Init_DS18B20()为初始化函数,DelayT_10us()为 10us 延时函数。DS18B20 为自定义的传感器 I/O 口名称。首先由单片机将 I/O 口拉低 500us,满足 480us 到 960us 持续时间要求。然后由单片机将 I/O 口拉高,即释放总线,持续时间为 60us,而时序要求里面表示在 15us 到 60us 之内传感器会给出存在信号,为了保证一定能收到存在信号,我们这里延时最大的 60us。最后一直检测传感器是否给出了低电平的存在信号,当检测到存在信号后继续延时 500us。
对 DS18B20 温度传感器而言,需要通过对它写字节命令或读字节命令来实现温度值的传输。而作为单总线传感器,字节命令都是通过连续的 8 次单 bit 命令来实现的,这节的主要内容为,单 bit 数据读写操作时序讲解。主机向 DS18B20 写“0”或“1”时序如下:
如上所示,图中左半边为写“0”时序,右边为写“1”时序。如左边所示,首先将总线拉低持续时间最低 60us 最高 120us。传感器 DS18B20 最快在第 15us 采集总线上的低电平,最典型的时间为第 30us,最慢也在第 60us 能采集完毕 ,也就是说只要我们保持低电平至少 60us,就能将“0”写入到传感器中。与写“0”类似,当要向传感器写入“1”的时候,首先由主机将总线拉低,持续时间大于 1us,随后立马将总线拉高,同样传感器最快在第 15us 采集总线上的高电平,最典型的时间为第 30us,最慢也在第 60us 能采集完毕。因此我们将写“0”和写“1”整合到一个函数中。主机向 DS18B20 传感器写数据函数:
void WrByte_18B20(uchar dat)
{
uchar j;
bit flag;
for(j=1;j<=8;j++)
{ //从低到高一次将1Byte数据写入DS18B20
flag = dat&0x01;
dat=dat > >1;
DS18B20 = 0;//拉低总线并延时2us
_nop_();
_nop_();
DS18B20 = flag;//将要写的位放到总线
DelayT_10us(6);//延时60us
DS18B20 = 1;//拉高释放总线
}
}
单片机读 DS18B20 传感器时序如下图所示:
如上图所示,当主机需要读取传感器的“0”时,首先将主机拉低,并在 15us 之内读取总线值,随后释放总线。当主机需要读取“1”时,首先将主机拉低,并延时超过 1us,并在 15us 之内读取总线值,随后释放总线。因此我们将读“0”和写“1”整合到一个函数中,如下主机读 DS18B20 函数:
uchar RdByte_18B20(void)
{
uchar dat,flag,j;
for(j=1;j<=8;j++)
{
DS18B20 = 0;//拉低总线并延时2us
_nop_();
_nop_();
DS18B20 = 1;//拉高释放总线并延时2us
_nop_();
_nop_();
flag = DS18B20;//采集
DelayT_10us(6);//延时60us
//读出的值最低位在前面
dat=(dat > >1)|(flag< < 7);
}
return dat;
}
下面介绍两个采集温度用到的写入不同的字节实现的功能,其他请参考 DS18B20 数据手册。
a) 写入字节[CCh]
功能:Skip ROM( 跳过 ROM ),在单点总线系统中,此命令通过允许总线。不给主机提供 64 位 ROM 编码而访问存储器操作来节省时间。如果在总线上存在多于一个的从属器件而且在 Skip ROM 命令之后发出读命令,那么由于多个从片同时发送数据,会在总线上发生数据冲突(漏极开路下拉会产生线与的效果)。
b) 写入字节[44h]
功能:Convert T(温度变换),这条命令启动一次温度转换而无需其他数据。温度转换命令被执行,而后 DS18B20 保持等待状态。如果总线控制器在这条命令之后跟着发出读时间隙,而 DS18B20 又忙于做时间转换的话,DS18B20 将在总线上输出“0”,若温度转换完成,则输出“1”。如果使用寄生电源,总线控制器必须在发出这条命令后立即起动强上拉,并保持 500ms。
单片机通过向 DS18B20 写入相应的字节命令完成对 DS18B20 存储器中数据的读写功能。本系统用到的功能为读取存储器中的温度值,写入字节[BEh]。
功能为:Read Scratchpad(读暂存存储器),这个命令读取暂存器的内容。读取将从字节 0 开始,一直进行下去,直到第 9(字节 8,CRC)字节读完。如果不想读完所有字节,控制器可以在任何时间发出复位命令来中止读取。读取的前两个字节为存储了温度值。
温度存储于上述的前两个字节中,通过读取前两个字节便可获得温度,两个字节内容与实际温度值对应所示,按照下关系如图 12-3 所示,按图进行相应的转换即可,另外在上电时温度的初始值为 85℃。
根据前面介绍,温度采集的步骤总结如下:
a) 温度传感器初始化;
b) 写入字节[CCh],跳过 ROM;
c) 写入字节[44h],启动一次温度转换;
d) 温度传感器初始化;
e) 写入字节[CCh],跳过 ROM;
f) 写入字节[BEh],发送读温度命令;
g) 读取返回的前两个字节,并转化为温度值。
温度采集函数代码如下:
uint GetT_18B20(void)
{
uchar Temp_L,Temp_H;
uint Temp;
Init_DS18B20(); //初始化
WrByte_18B20(0xCC);//跳过ROM
WrByte_18B20(0x44);//启动温度转换
Init_DS18B20(); //初始化
WrByte_18B20(0xCC);//跳过ROM
WrByte_18B20(0xBE);//发送读温度命令
//读取两个字节的温度值
Temp_L = RdByte_18B20();
Temp_H = RdByte_18B20();
Temp = ((uint)Temp_H< < 8) + Temp_L;//将温度组合成16变量
return Temp;
}
为了方便后续使用,我们将与 DS18B20 有关的函数都放到“Drive_DS18B20.h”、 “Drive_DS18B20.c”文件中。
“Drive_DS18B20.h”文件代码如下所示:
#ifndef __18b20_H__
#define __18b20_H__
extern unsigned int GetT_18B20(void);
#endif
“Drive_DS18B20.h”文件代码如下所示:
#include< reg52.h >
#include < intrins.h >
#define uchar unsigned char
#define uint unsigned int
sbit DS18B20 = P2^2; //DS18B20传感器I/O口定义
void DelayT_10us(uchar count)
{
while(count--)
{ //模拟10us延时
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
}
void Init_DS18B20(void)
{
//单片机拉低总线并延时600us
DS18B20 = 0;
DelayT_10us(50);
//单片机释放总线并延时60us
DS18B20 = 1;
DelayT_10us(6);
//当DS18B20返回低电平时,表示总线上存在传感器
while(!DS18B20);
DelayT_10us(50);
}
void WrByte_18B20(uchar dat)
{
uchar j;
bit flag;
for(j=1;j<=8;j++)
{ //从低到高一次将1Byte数据写入DS18B20
flag = dat&0x01;
dat=dat > >1;
DS18B20 = 0;//拉低总线并延时2us
_nop_();
_nop_();
DS18B20 = flag;//将要写的位放到总线
DelayT_10us(6);//延时60us
DS18B20 = 1;//拉高释放总线
}
}
uchar RdByte_18B20(void)
{
uchar dat,flag,j;
for(j=1;j<=8;j++)
{
DS18B20 = 0;//拉低总线并延时2us
_nop_();
_nop_();
DS18B20 = 1;//拉高释放总线并延时2us
_nop_();
_nop_();
flag = DS18B20;//采集
DelayT_10us(6);//延时60us
//读出的值最低位在前面
dat=(dat > >1)|(flag< < 7);
}
return dat;
}
uint GetT_18B20(void)
{
uchar Temp_L,Temp_H;
uint Temp;
Init_DS18B20(); //初始化
WrByte_18B20(0xCC);//跳过ROM
WrByte_18B20(0x44);//启动温度转换
Init_DS18B20(); //初始化
WrByte_18B20(0xCC);//跳过ROM
WrByte_18B20(0xBE);//发送读温度命令
//读取两个字节的温度值
Temp_L = RdByte_18B20();
Temp_H = RdByte_18B20();
Temp = ((uint)Temp_H< < 8) + Temp_L;//将温度组合成16变量
return Temp;
}
将上述两个文件添加到工程中,主程序文件中调用函数便可实现温度采集了。
下面我们讲解 DS18B20 的应用,要实现的功能为:单片机每秒采集一次温度值,并将温度值显示到液晶显示模块上。用定时器中断来时实现 1s 的定时,建立工程,将 1602、18B20 的文件添加到工程中,在主程序中调用相关大函数来实现显示和温度的采集,主程序代码如下所示:
#include< reg52.h >
#include"Drive_DS18B20.h"
#include"Drive_1602.h"
#define uchar unsigned char
#define uint unsigned int
uint Temp;
uchar str[10]=0;
#define FOSC 11059200 //单片机晶振频率
#define T_1ms (65536 - FOSC/12/1000) //定时器初始值计算
uint flag = 0;
uint T_count = 0;
uint Sec = 0;
sbit DU = P2^7;//数码管段选、位选引脚定义
sbit WE = P2^6;
void main(void)
{
Init_1602();//1602初始化
P0 = 0xff;//关闭所有数码管
WE = 1;
WE = 0;
TMOD = 0x01; //定时器工作模式配置
TL0 = T_1ms; //装载初始值
TH0 = T_1ms > >8;
TR0 = 1; //启动定时器
ET0 = 1; //允许定时器中断
EA = 1; //开总中断
Disp_1602_str(1,3,"RongYi RY-51");//第1行第3列开始显示"RongYi RY-51"
while(1)
{
if(T_count >=1000)//1s进行一次温度的采集以及显示
{
EA=0;//关闭中断,防止定时器中断影响温度传感器的读写
T_count =0;
Sec++;
Temp = GetT_18B20(); //采集温度
str[0] = (Temp > >4)/10 + '0';//左移4位获得温度整数部分
str[1] = (Temp > >4)%10 + '0';
str[2] = '.';
if((Temp > >3)%10)
str[3] = '5';
else
str[3] = '0';
str[4] = '�';
Disp_1602_str(2,3,str);//第2行第3列开始显示温度值
EA = 1;//显示完成后,开总中断
}
}
}
void timer0() interrupt 1
{
TL0 = T_1ms;//重装初始值
TH0 = T_1ms > >8;
T_count++;
}
本章详细介绍了DS18B20的工作原理,通信时序,以及函数的编写,后面使用时只需调用函数即可了,无需重复造轮子咯。
全部0条评论
快来发表一下你的评论吧 !