DS18B20是美国DALLAS公司生产的单总线数字式传感器,能够将温度物理量转化为数字信号,而且每个器件具有唯一的序号,因此能够实现多点测量。它的测量范围为-55°C~+125°C,其中在-10°C~+85°C的范围内的测量精度可以达到±0.5°C。
它只有三个引脚,GND、DQ、VDD,可以直接由数据线本身寄生供电,也可以采用外部供电,使用起来很方便,当然这种方便也是要付出一定代价的,DS18B20的时序要求相当严格,系统对DS18B20的各种操作必须按协议进行,操作的协议为:初始化DS18B20;发ROM功能命令;发存储器操作命令;处理数据。如果程序中有较多的中断,抑或是硬件连接时连线过长,都会造成时序波形的畸变,从而造成测量结果混乱,因此这种情况也使得该芯片的使用受到了一定的限制。
这个图是Proteus仿真时示波器观察到的DQ引脚的初始化时序情况:

主机在初始时刻发送一复位脉冲(最短为480us的低电平信号),接着释放总线并进入接收状态,DS18B20在检测到总线的上升沿之后,等待15~60us,接着若初始化成功,DS18B20会发出存在脉冲(低电平,持续60~240us)。
下边是之前做过的一个温度测量程序的硬件原理图,采集到的温度以16进制分别以高8位和低8位的形式通过RS232串口发送到上位机,后续的处理就交由上位机编程控制了。

这里值得注意的是DS18B20的DQ口需要接一个上拉电阻,这是因为一般的IO引脚驱动能力是20mA以内,而上拉电阻的意义就是为了增加驱动能力。不过要想使DS18B20进行精确的温度转换,I/O线必须保证在温度转换期间提供足够的能量,由于每个DS18B20在温度转换期间工作电流达到1mA,当几个温度传感器挂在同一根I/O线上进行多点测温时,只靠4.7K上拉电阻就无法提供足够的能量,会造成无法转换温度或温度误差极大。
下边是Keil C51的程序代码:
#include 
#include "intrins.h" //_nop_();延时函数用
#define XTAL 11059200 // CUP	晶振频率
#define baudrate 9600 // 通信波特率
#define uchar unsigned char
#define uint unsigned int
sbit DQ=P3^3; // 定义DS18B20端口DQ 
sbit BEEP=P3^7;
unsigned char presence;
unsigned char data  temp_data[2] = {0x00,0x00};
void beep();
bit flash=0; // 显示开关标记
void Delay(unsigned int num) { // 延时函数
  	while(--num);
}
uchar Init_DS18B20(void) { // 初始化ds1820
     DQ=1; // DQ复位
     Delay(8); // 稍做延时
     DQ=0; // 单片机将DQ拉低
     Delay(90); // 精确延时 大于 480us
     DQ=1; // 拉高总线
     Delay(8);
     presence=DQ; // 如果=0则初始化成功 =1则初始化失败
     Delay(100);
     DQ=1; 
     return(presence); // 返回信号,0=presence,1= no presence
}
uchar ReadOneChar(void) { // 读一个字节
	uchar i;
	uchar value=0;
	for(i=8;i>0;i--) {
		DQ=1;_nop_();_nop_();
		value>>=1;
		DQ=0;_nop_();_nop_();_nop_();_nop_(); //4 us
		DQ=1;_nop_();_nop_();_nop_();_nop_(); //4 us
		if(DQ) value|=0x80;
		Delay(6); //66 us
	}
	DQ=1;
	return(value);
}
void WriteOneChar(unsigned char dat) { // 写一个字节
  	unsigned char i=0;
  	for(i=8;i>0;i--) {
    	DQ=0;
    	DQ=dat&0x01;
    	Delay(5);
    	DQ=1;
    	dat>>=1;
  	}
}
void Read_Temperature(void) { // 读取温度
  	Init_DS18B20();
  	if(presence==1) { 
		beep();
		flash=1;
	} // DS18B20不正常,蜂鸣器报警
   	else {
     	flash=0;
     	WriteOneChar(0xCC); // 跳过读序号列号的操作
     	WriteOneChar(0x44); // 启动温度转换
     	Init_DS18B20();
     	WriteOneChar(0xCC); // 跳过读序号列号的操作
	    WriteOneChar(0xBE); // 读取温度寄存器
     	temp_data[0]=ReadOneChar(); // 温度低8位
     	temp_data[1]=ReadOneChar(); // 温度高8位 
   	}
}
void beep(void) {
    unsigned char i;
    for(i=0;i<100;i++) {
      	Delay(60);
      	BEEP=!BEEP; // BEEP取反
    } 
    BEEP=1; // 关闭蜂鸣器
}
void init(void) {
	EA=1; // "中断总允许"
	TMOD=0x20; // 定时器1工作于8位自动重载模式, 用于产生波特率
	TH1=(unsigned char)(256-(XTAL/(32L*12L*baudrate)));
	TL1=(unsigned char)(256-(XTAL/(32L*12L*baudrate))); // 定时器0赋初值
	SCON=0x50; // 设定串行口工作方式
	PCON&=0x00; // 波特率不倍增
	TR1=1; // 启动定时器1
	IE=0x00; // 禁止任何中断
}
// 传送十六位的温度数据,低位在前
void send_char(void) {
	unsigned i=0;
	while(i < 2) {
		SBUF=temp_data[i];
		while(!TI);// 等特数据传送
		TI=0;// 清除数据传送标志
		i++;
	}
}
void main(void) {
	uchar receive;
  	init();
  	while(1) {
		Read_Temperature(); // 读取温度
		if(flash==0) {
			// 显示温度
		} else {
			// DS18B20不正常
		}
		Delay(4000); //延时防止总线忙
		if(RI) { // 是否有数据到来
			RI=0;
			receive=SBUF;
			if(receive=='s') { // 是否开始采集温度
				send_char(); // 传送采集的温度
			}
		}
	}
}
如果接下来用串口调试助手调试,就会发现一旦上位机发送握手信号”s”,就会接收到硬件电路发送回来的16进制信号,比如温度为+85°C,就会收到50H 05H,下边的事情就是程序员通过软件语言对接收到的数据的处理并显示了。
下图是我当时做系统时候用VB6写的上位机界面:
