一.简述
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,有一篇很棒的文章,可以作为参考~~~