DS18B20数字温度计是DALLAS公司生产的1-Wire,即单总线器件,具有线路简单,体积小的特点。因此用它来组成一个测温系统,具有线路简单,在一根通信线,可以挂很多这样的数字温度计,十分方便。
1.DS18B20产品的特点
(1)、只要求一个端口即可实现通信。
(2)、在DS18B20中的每个器件上都有独一无二的序列号。
(3)、实际应用中不需要外部任何元器件即可实现测温。
(4)、测量温度范围在-55。C到+125。C之间。
(5)、数字温度计的分辨率用户可以从9位到12位选择。
(6)、内部有温度上、下限告警设置。
2.DS18B20的引脚介绍
TO-92封装的DS18B20的引脚排列见图1,其引脚功能描述见表1。
(底视图)图1
表1DS18B20详细引脚功能描述序号名称引脚功能描述
1GND地信号
2DQ数据输入/输出引脚。开漏单总线接口引脚。当被用着在寄生电源下,也可以向器件提供电源。
3VDD可选择的VDD引脚。当工作于寄生电源时,此引脚必须接地。
3.DS18B20的使用方法
由于DS18B20采用的是1-Wire总线协议方式,即在一根数据线实现数据的双向传输,而对AT89S51单片机来说,硬件上并不支持单总线协议,因此,我们必须采用软件的方法来模拟单总线的协议时序来完成对DS18B20芯片的访问。
由于DS18B20是在一根I/O线上读写数据,因此,对读写的数据位有着严格的时序要求。DS18B20有严格的通信协议来保证各位数据传输的正确性和完整性。该协议定义了几种信号的时序:初始化时序、读时序、写时序。所有时序都是将主机作为主设备,单总线器件作为从设备。而每一次命令和数据的传输都是从主机主动启动写时序开始,如果要求单总线器件回送数据,在进行写命令后,主机需启动读时序完成数据接收。数据和命令的传输都是低位在先。
DS18B20的复位时序
DS18B20的读时序
对于DS18B20的读时序分为读0时序和读1时序两个过程。
对于DS18B20的读时隙是从主机把单总线拉低之后,在15微秒之内就得释放单总线,以让DS18B20把数据传输到单总线上。DS18B20在完成一个读时序过程,至少需要60us才能完成。
DS18B20的写时序
对于DS18B20的写时序仍然分为写0时序和写1时序两个过程。
对于DS18B20写0时序和写1时序的要求不同,当要写0时序时,单总线要被拉低至少60us,保证DS18B20能够在15us到45us之间能够正确地采样IO总线上的“0”电平,当要写1时序时,单总线被拉低之后,在15us之内就得释放单总线。
4.C语言源程序
/******************************************************************** DS18B20温度测量程序 ** 晶 振 频 率:11.0592M ** 线路->单片机实验开发板A ******************************************************************/ #include <reg51.h> #include <intrins.h> #define uchar unsigned char #define uint unsigned int sbit PS2 = P2 ^ 6; //数码管十位 sbit PS1 = P2 ^ 7; //数码管个位 sbit P_L = P1 ^ 0; //测量指示 sbit DQ = P2 ^ 2; //DS18B20数据接口 void delay(uint);//延时函数 void DS18B20_init(void);//DS18B20初始化 void get_temper(void);//读取温度程序 void DS18B20_write(uchar in_data);//DS18B20写数据函数 uchar DS18B20_read(void);////读取数据程序 uchar htd(uchar a);//进制转换函数 void display(void);//显示函数 uchar temp;//温度寄存器 bit DS18B20;// 18B20存在标志,1---存在 0---不存在 uchar tab[10] = {0xcf, 0x03, 0x5d, 0x5b, 0x93, 0xda, 0xde, 0x43, 0xdf, 0xdb}; //字段转换表 void main(void) { uint a; while (1) { get_temper();//测量温度 for (a = 0; a < 200; a++) { //显示,兼有延时的作用 display(); } } } /****************************DS18B20读取温度函数**************************/ /*函数原型:void get_temper(void) /*函数功能:DS18B20读取温度 /*输入参数:无 /*输出参数:无 /*调用模块: /*建立时间:2005/11/14 /*作者:站长 /**********************************************************************/ void get_temper(void) { uchar k, T_sign, T_L, T_H; DS18B20_init();//DS18B20初始化 if (DS18B20) { //判断DS1820是否存在?若DS18B20不存在则返回 DS18B20_write(0xcc);// 跳过ROM匹配 DS18B20_write(0x44);//发出温度转换命令 DS18B20_init();//DS18B20初始化 if (DS18B20) { //判断DS1820是否存在?若DS18B20不存在则返回 DS18B20_write(0xcc);//跳过ROM匹配 DS18B20_write(0xbe);//发出读温度命令 T_L = DS18B20_read(); //数据读出 T_H = DS18B20_read(); k = T_H & 0xf8; if (k == 0xf8) { T_sign = 1; //温度是负数 } else { T_sign = 0; //温度是正数 } T_H = T_H & 0x07; temp = (T_H * 256 + T_L) * 0.0625; //温度转换常数 } } } /****************************DS18B20写数据函数**************************/ /*函数原型:void DS18B20_write(uchar in_data) /*函数功能:DS18B20写数据 /*输入参数:要发送写入的数据 /*输出参数:无 /*调用模块:_cror_() /*建立时间:2005/11/14 /*作者:站长 /**********************************************************************/ void DS18B20_write(uchar in_data)//写DS18B20的子程序(有具体的时序要求) { uchar i, out_data, k; out_data = in_data; for (i = 1; i < 9; i++) { //串行发送数据 DQ = 0; DQ = 1; _nop_(); _nop_(); k = out_data & 0x01; if (k == 0x01) { //判断数据 写1 DQ = 1; } else { //写0 DQ = 0; } delay(4);//延时62us DQ = 1; out_data = _cror_(out_data, 1); //循环右移1位 } } /**************************DS18B20读函数**************************/ /*函数原型:void DS18B20_read() /*函数功能:DS18B20读数据 /*输入参数:无 /*输出参数:读到的一字节内容 /*调用模块:delay() /*建立时间:2005/11/14 /*作者:站长 /******************************************************************/ uchar DS18B20_read() { uchar i, in_data, k; in_data = 0; for (i = 1; i < 9; i++) { //串行发送数据 DQ = 0; DQ = 1; _nop_(); _nop_(); k = DQ; //读DQ端 if (k == 1) { //读到的数据是1 in_data = in_data | 0x01; } else { in_data = in_data | 0x00; } delay(3);//延时51us DQ = 1; in_data = _cror_(in_data, 1); //循环左移1位 } return (in_data); } /**************************DS18B20初始化函数**************************/ /*函数原型:void DS18B20_init(void) /*函数功能:DS18B20初始化 /*输入参数:无 /*输出参数:无 /*调用模块:delay() /*建立时间:2005/11/14 /*作者:站长 /******************************************************************/ void DS18B20_init(void) { uchar a; DQ = 1; //主机发出复位低脉冲 DQ = 0; delay(44);//延时540US DQ = 1; for (a = 0; a < 0x36 && DQ == 1; a++) { //等待DS18B20回应 a++; a--; } if (DQ) { DS18B20 = 0; //18B20不存在 } else { DS18B20 = 1; // 18B20存在 delay(120);//复位成功!延时240US } } /**************************数码管显示函数**************************/ /*函数原型:void display(void) /*函数功能:数码管显示 /*输入参数:无 /*输出参数:无 /*调用模块:delay() /*建立时间:2005/11/14 /*作者:站长 /******************************************************************/ void display(void) { P0 = tab[htd(temp) >> 4]; //转换成十进制输出 PS2 = 1; PS1 = 0; delay(200); P0 = tab[htd(temp) & 0x0f]; //转换成十进制输出 PS1 = 1; PS2 = 0; delay(200); P_L = ~DS18B20; } /**************************十六进制转十进制函数**************************/ /*函数原型:uchar htd(uchar a) /*函数功能:十六进制转十进制 /*输入参数:要转换的数据 /*输出参数:转换后的数据 /*调用模块:无 /*建立时间:2005/11/14 /*作者:站长 /******************************************************************/ uchar htd(uchar a) { uchar b, c; b = a % 10; c = b; a = a / 10; b = a % 10; c = c | b << 4; return c; } /*******************************延时函数*********************************/ /*函数原型:delay(unsigned int delay_time) /*函数功能:延时函数 /*输入参数:delay_time (输入要延时的时间) /*输出参数:无 /*调用模块:无 /*建立时间:2005/10/21 /*作者:站长 /**********************************************************************/ void delay(unsigned int delay_time)//延时子程序 { for (; delay_time > 0; delay_time--) {} }
5.汇编语言源程序
ORG 0000H ;单片机内存分配申明! TEMPER_L EQU 29H ;用于保存读出温度的低8位 TEMPER_H EQU 28H ;用于保存读出温度的高8位 FLAG1 EQU 38H ;是否检测到DS18B20标志位 A_BIT EQU 20H ;数码管个位数存放内存位置 B_BIT EQU 21H ;数码管十位数存放内存位置 ;进行温度显示,这里我们考虑用网站提供的两位数码管来显示温度 ;显示范围00到99度,显示精度为1度 ;因为12位转化时每一位的精度为0.0625度,我们不要求显示小数所以可以抛弃29H的低4位 ;将28H中的低4位移入29H中的高4位,这样获得一个新字节,这个字节就是实际测量获得的温度 ;这个转化温度的方法非常简洁无需乘于0.0625系数 MAIN: LCALL GET_TEMPER ;调用读温度子程序 MOV A,29H MOV C,40H ;将28H中的最低位移入C RRC A MOV C,41H RRC A MOV C,42H RRC A MOV C,43H RRC A MOV 29H,A LCALL DISPLAY ;调用数码管显示子程序 JNB FLAG1,MM1 CLR P1.0 SJMP MAIN MM1: SETB P1.0 SJMP MAIN INIT_1820: SETB P2.2 ;这是DS18B20复位初始化子程序 NOP CLR P2.2 MOV R1,#3 ;主机发出延时537微秒的复位低脉冲 TSR1: MOV R0,#107 DJNZ R0,$ DJNZ R1,TSR1 SETB P2.2 ;然后拉高数据线 NOP NOP NOP MOV R0,#25H TSR2: JNB P2.2,TSR3 ;等待DS18B20回应 DJNZ R0,TSR2 LJMP TSR4 ;延时 TSR3: SETB FLAG1 ;置标志位,表示DS1820存在 LJMP TSR5 TSR4: CLR FLAG1 ;清标志位,表示DS1820不存在 LJMP TSR7 TSR5: MOV R0,#117 TSR6: DJNZ R0,TSR6 ;时序要求延时一段时间 TSR7: SETB P2.2 RET GET_TEMPER: SETB P2.2 ;读出转换后的温度值 LCALL INIT_1820;先复位DS18B20 JB FLAG1,TSS2 RET ;判断DS1820是否存在?若DS18B20不存在则返回 TSS2: MOV A,#0CCH ;跳过ROM匹配 LCALL WRITE_1820 MOV A,#44H ;发出温度转换命令 LCALL WRITE_1820 LCALL DISPLAY ;这里通过调用显示子程序实现延时一段时间,等待AD转换结束,12位的话750微秒 LCALL INIT_1820;准备读温度前先复位 MOV A,#0CCH ;跳过ROM匹配 LCALL WRITE_1820 MOV A,#0BEH ;发出读温度命令 LCALL WRITE_1820 LCALL READ_18200 ;将读出的温度数据保存到35H/36H RET WRITE_1820: MOV R2,#8 ;写DS18B20的子程序(有具体的时序要求),一共8位数据 CLR C WR1: CLR P2.2 MOV R3,#5 DJNZ R3,$ RRC A MOV P2.2,C MOV R3,#21 DJNZ R3,$ SETB P2.2 NOP DJNZ R2,WR1 SETB P2.2 RET READ_18200: MOV R4,#2 ;读DS18B20的程序,从DS18B20中读出两个字节的温度数据,将温度高位和低位从DS18B20中读出 MOV R1,#29H ;低位存入29H(TEMPER_L),高位存入28H(TEMPER_H) RE00: MOV R2,#8 ;数据一共有8位 RE01: CLR C SETB P2.2 NOP NOP CLR P2.2 NOP NOP NOP SETB P2.2 MOV R3,#8 RE10: DJNZ R3,RE10 MOV C,P2.2 MOV R3,#21 RE20: DJNZ R3,RE20 RRC A DJNZ R2,RE01 MOV @R1,A DEC R1 DJNZ R4,RE00 RET DISPLAY:MOV A,29H ;显示子程序将29H中的十六进制数转换成10进制 MOV B,#10 ;10进制/10=10进制 DIV AB MOV B_BIT,A ;十位在A MOV A_BIT,B ;个位在B MOV DPTR,#NUMTAB ;指定查表启始地址 MOV R0,#4 DPL1: MOV R1,#250 ;显示1000次 DPLOP: MOV A,A_BIT ;取个位数 MOVC A,@A+DPTR ;查个位数的7段代码 MOV P0,A ;送出个位的7段代码 CLR P2.6 ;开个位显示 ACALL D1MS ;显示1MS SETB P2.6 MOV A,B_BIT ;取十位数 MOVC A,@A+DPTR ;查十位数的7段代码 MOV P0,A ;送出十位的7段代码 CLR P2.7 ;开十位显示 ACALL D1MS ;显示1MS SETB P2.7 DJNZ R1,DPLOP ;100次没完循环 DJNZ R0,DPL1 ;4个100次没完循环 RET D1MS: MOV R7,#80 ;1MS延时 DJNZ R7,$ RET ;实验板上的7段数码管0~9数字的共阴显示代码 NUMTAB: DB 0CFH,03H,5DH,5BH,93H,0DAH,0DEH,43H,0DFH,0DBH END