一.简述
DS18B20是DALLAS公司出品的一种数字式单总线温度传感器,测温范围为-55°C到125°C(-67°F到257°F),被广泛应用在温度测量领域中。显著优点就是接线简单。因为它只有三个引脚:VCC,GND和DQ数据线,可以实现单总线数据传输,简化了硬件设计。
而且,它可以使用数据线供电,可以再省去一个电源线,只要接地和数据线即可。
下面是一个TSOC封装的引脚图:
在实际使用中,要注意不能把它的正负极引脚接反,否则很容易造成芯片损坏。在实际使用时还常常用到下面这种形态的封装。这时可以如图所示,面朝芯片的平面一侧,从左到右依次是GND,DQ,VDD引脚。供电电源可取3V到5.5V。
此外,每一个DS18B20的内部都有一个唯一的64位长的编号作为标识,所以可以将多个DS18B20串在一根总线上实现温度查询测量。这个64位长的序列存储在其ROM中,开始8位为产品编号,同系列的DS18B20是相同的;接下来的48位为每个器件的唯一编号,最后8位为前面56位的循环校验码CRC码。
二.将存储器数据转换为实际温度的方法
DS18B20用两个存储器存储温度测量之。其中高8位为符号存储。若温度小于0,则此8位全为1;若温度大于0,则此8位均为0。为什么要采用这样的存储方式呢??在这种存储方式下,数据实际上是以补码的方式在存储。所以当我们用C语言编程的时候,可以直接把高8位和低8位组合到一起,然后直接赋给一个int型的变量。考虑到它的1LSB为0.0625°,所以我们还要再乘以0.0625才能得到真实的温度。
三.硬件连接
下面是我的单片机开发板的硬件连接图:
首先是DS18B20模块:
可以看到,在芯片的DQ引脚上又加了一个上拉电阻,这是是选择了10K。这是因为DS18B20输出高电平的能力不强,一般采用接上拉电阻的方式增强其输出能力,上拉电阻一般取4到5K即可。这是一种比较简单的上拉方法,其中DALLAS公司的数据手册中还提出了他们建议的上拉方法,可以参考其手册来设计。
微控制器部分采用STC的89C52,如下:
在这里,我们把DQ接到了单片机的P2.2引脚。
四.工作时序和操作方法
1.总体设计
我们首先编写DS18B20的头文件,完成对DS18B20硬件操作的封装。
#ifndef _DS18B20_H_
#define _DS18B20_H_
#include <reg52.h>
//ROM操作指令
#define start_ZH 0x44
#define read_ROM 0x33
#define match_ROM 0x55
#define skip_ROM 0xcc
#define search_ROM 0xf0
#define alarm_SEARCH 0xec
#define write_RAM 0x4e
#define read_RAM 0xbe
#define copy_RAM 0x48
#define recall_EPROM 0xb8
#define read_POWER 0xb4
sbit ds=P2^2;
/*********************************
功能:初始化设备函数
参数:无
返回值:无
*********************************/
void Init_ds18b20();
/*********************************
功能:等待设备应答
参数:无
返回值:无
*********************************/
void Wait_ds18b20();
/*********************************
功能:读DS18B20的一位
参数:无
返回值:读取的一位数据
*********************************/
bit read_ds18b20_bit();
/*********************************
功能:读DS18B20的一字节
参数:无
返回值:读取的一个字节
*********************************/
uchar read_ds18b20_byte();
/*********************************
功能:写DS18B20的一位
参数:要写入的一位
返回值:无
*********************************/
void write_ds18b20_bit(bit dat);
/*********************************
功能:写DS18B20的一字节
参数:要写入的一个字节
返回值:无
*********************************/
void write_ds18b20_byte(uchar dat);
/*********************************
功能:发布指令
参数:无
返回值:无
*********************************/
void write_ds18b20_command(uchar com);
/*********************************
功能:得到当前温度值
参数:无
返回值:当前温度值
*********************************/
int getTemp();
#endif
在这个头文件里,我们编写了一般对一个可编程元件进行操作可能用到的函数声明。一个可编程的器件,一般要涉及到对它进行写指令操作,写/读数据操作。读/写 数据/指令 又是以字节为单位,而我们的总线上每次只能传递一个比特,所以要从bit的读写中扩展出字节的读写操作。
同样注意到我们在头文件中声明了大量的ROM操作指令。通过查阅数据手册可以知道,在单总线通信方式下,我们如果想对器件的内部存储器和向其写指令,都必须先进行对ROM的操作,主机必须先向DS18B20发出下面五种ROM指令之一:
(1)Read_ROM
(2)Match_ROM
(3)Search_ROM
(4)Skip_ROM
(5)Alarm_Search
下面我们将逐一实现头文件里的函数定义。.
2.初始化和等待设备应答
这里要注意的是DS18B20单总线的所有操作都要以初始化为开始。就好比和DS18B20通信时,单片机必须首先发出握手信号,等到18B20发回应答后,才能进行通信。
下面是数据手册给出的时序关系图:
单片机首先发出一个持续时间为480us到960us的低电平,随后总线复位为高电平,DS18B20在接收到高点平后,等待15到60us后,发出一持续时间为60到240us的低电平,表明自己的存在,双方可以通信。根据这一时序特点,我们可以编写初始化和等待应答函数:
/*********************************
功能:初始化设备函数
参数:无
返回值:无
备注:调用Wait_ds18b20()来接收DS18B20的回应
*********************************/
void Init_ds18b20()
{
uint i=100;
//总线拉低
ds=0;
//延时480us以上
while(i)
{
i--;
}
//总线拉高,等待DS18B20应答
ds=1;
}
/*********************************
功能:等待设备应答
参数:无
返回值:无
*********************************/
void Wait_ds18b20()
{
//等待应答
while(ds) ;
while(~ds) ;
}
3.读写bit操作
先来看数据手册中的内容,数据手册当中给出了完整的读写一个bit的时序:
写时序:
在写bit的时候,总线首先需要拉低15us,然后置高或置低并持续45us,即写1或0,然后把总线拉高,实现一个写操作。同时注意在写下一个bit的时候,要与上一个至少相隔1us。
读时序:
在读bit的时候,总线首先由高变低并最少持续1us,DS18B20在总线拉低后15us后将数据放到总线上,所以控制器的必须停止拉低总线,并在其读周期刚开始拉低总线15us后完成采样,并在结束后将总线复位到高电平。整个读周期应该持续60us,两次读之间间隔至少1us。
下面是根据时序图写出的读写bit操作的函数write_bit和read_bit。
/*********************************
功能:写DS18B20的一位
参数:要写入的一位
返回值:无
*********************************/
void write_ds18b20_bit(bit dat)
{
uint j=0;
//总线拉低
ds=0;
j++;j++;
//延时后把数据放到总线
ds=dat;
j=8;
while(j>0)
{
j--;
}
//待18B20读取数据后把总线拉高
ds=1;
j++;
}
/*********************************
功能:读DS18B20的一位
参数:无
返回值:读取的一位数据
*********************************/
bit read_ds18b20_bit()
{
uint i=1;
bit res;
ds=0;
i++;
//停止拉低总线,读取DQ引脚的值
ds=1;
//15us后读数据
i++;
i++;
res=ds;
//延时
i=10;
while(i)
{
i--;
}
return res;
}
4.读写btye
读写bit是读写byte的前提,而实现读写byte我们就能够向DS18B20内写入指令,读取存储器内容等。
这两个函数主要是设置了一个循环,做8个读写bit的操作就可以了,比较简单,直接把代码贴出来:
/*********************************
功能:写DS18B20的一字节
参数:要写入的一个字节
返回值:无
*********************************/
void write_ds18b20_byte(uchar dat)
{
uint i=0;
for(;i<8;i++)
{
//前面的宏定义有define BIT(n) (0x01<<n)
//BIT(0)=0x01
//即取dat的最后一位,来决定写1或者写0
if((dat>>i)&BIT(0))
write_ds18b20_bit(1);
else
write_ds18b20_bit(0);
}
}
/*********************************
功能:读DS18B20的一字节
参数:无
返回值:读取的一个字节
*********************************/
uchar read_ds18b20_byte()
{
uchar res=0;
uchar tem=0;
uint i=0;
for(;i<8;i++)
{
tem=read_ds18b20_bit();
//第i次读进来的是第i位
res |= (tem<<i);
}
return res;
}
下面这个函数实际也是写字节操作,只是我们上面提到过,对DS18B20的写指令都要以初始化和对它的ROM操作为先,所以我们单独把写指令这个功能拿出来封装成为一个独立的函数,方便调用。
/*********************************
功能:发布指令
参数:指令字节
返回值:无
*********************************/
void write_ds18b20_command(uchar com)
{
bit flag=0;
if(EA==1)
{
flag=1;
}
//先关中断,防止中断影响时序
//宏定义define DisableInterrupt (EA=0)
DisableInterrupt;
//初始化DS18B20
Init_ds18b20();
//等待回应
Wait_ds18b20();
//跳过ROM
write_ds18b20_byte(skip_ROM);
//写指令字节
write_ds18b20_byte(com);
//开中断
//宏定义define EnableInterrupt (EA=1)
if(flag==1)
{
EnableInterrupt;
}
}
5.获取温度值
/*********************************
功能:得到当前温度值
参数:无
返回值:当前温度值
*********************************/
int getTemp()
{
uchar low,high;
int value=0;
float tmp=0.0;
bit flag=0;
//发布命令,启动转换
write_ds18b20_command(start_ZH);
if(EA==1)
{
flag=1;
}
DisableInterrupt;
//读RAM获取温度
write_ds18b20_command(read_RAM);
low=read_ds18b20_byte();
high=read_ds18b20_byte();
//得到int
value = (high<<8|low);
//得到真实温度
tmp=value*0.0625;
//将真实的温度扩大100倍返回整形,即是取小数点后两位
value=(int)(tmp*100);
if(flag==1)
{
EnableInterrupt;
}
return value;
}
这样我们就得到了扩大100倍后的温度值。
于是只要在我们的其他程序里面调用getTemp就可以获得当前的温度值的100倍了,从而实现了对硬件设备的封装。
在以后的文章中我会把它放到一个具体的工程里面。实现特定的功能,通过调用getTemp函数来实现对DS18B20的访问,并把数据传回调用程序中。
在这里:http://bbs.21ic.com/icview-46868-1-1.html,有一篇很棒的文章,可以作为参考~~~