目录
一.18B20组成
- 管脚顺序:1-GND 2-DQ 3-VDD
- 组成:64位ROM +存储器和控制器 +高速缓存存储器 + 8位CRC生成器 + 温度灵敏元件 + 高/低温触发器TH/TL + 配置寄存器
1.64位ROM
出厂前已经刻录好数据,用于存储18B20的地址序列号,由 8位产品类型标号 + 48位序列号 + 8位CRC循环冗余校验码。
用处:用于实现一根总线挂载多个18B20
2.内部存储器
内部存储器包括高度暂存器RAM + EEPROM;
EEPROM用于存放TH/TL高低温触发器+结构寄存器
3.配置寄存器
存储的第四字节为配置寄存器,用于配置传感器的精度;组成 1111 1+ R0 + R1 + 0 ,低4位和第7位不可改变。
R1 | R0 | 精度 |
---|---|---|
0 | 0 | 9bit |
0 | 1 | 10bit |
1 | 0 | 11bit |
1 | 1 | 12bit |
二.18B20温度转换规则及指令
- 核心功能:可直接读出数字的温度数值
- 精度:用户可编程 9位 --0.5℃;10位 --0.25℃;11位 --0.125℃;12位 --0.0625℃。默认精度:12位
- 工作过程:总线发出44H命令,进行测量温度和AD转换;转换后,产生数据以两字节的形式存储在高速缓存存储器中后,器件保持等待状态。
- 温度数据组成:16位,
- (MS 5bit)高5位为符号位,T>0时,全为0,实际温度 = 后7位数据 *1 ℃ + 后4位数据* 0.625℃;T<0时,全为1,实际温度 =( 后7位数据取反+1的数据前7位)* 1℃ +( 后4位数据取反+1的数据前7位)* 0.0625℃;
- (MS 3bit+ LS 4bit)高字节的后三位+低字节的前4位为整数部分
- (LS 4bit)低字节的后4位为小数部分
- eg:
- 1111 1111 1111 1000 (FFF8h)>>> 1111 1 温度为负 ,111 1111 1000取反+1 =000 0000 1000(8) ;实际的温度 = -8*0.0625 = 0.5℃
- 0000 0111 1101 1000 (07D8h)>>> 0000 0 温度为正 ,0000 0111 1101 1000 ;实际的温度 = (111 1101)125 * 1 +(1000)8 * 0.0625= 125 .5℃
三.18B20指令时序图及代码实现
指令 | 代码 | 功能 |
读ROM | 33H | 读取ROM的序列号 |
跳过ROM | CCH | 忽略64bitROM地址,直接可进行温度转换 |
温度转换 | 44H | 启动温度转换,转换时长最大750ms,存入内部RAM |
读暂存器 | 0BEH | 读取内部RAM的内容 |
1.18B20初始化

- 数据线拉0
- 延时480-960us
- 数据线拉1
- 延时80us,初始化成功15-60us内返回0,因为不可以无限等待,需要进行超时判断
- 延时,时间长度从数据线拉1算起不少于480us
unsigened char TempInit() //判断温度传感器是否存在
{
unsigened char i = 0;
DsPort = 0; //数据线拉0
i = 70; //延时480-960us
while(i--); //实际延时642us
DsPort = 1; //拉高后延时等待80us,判断DS是否响应,响应数据线会被再次拉低
i=0;
while(DsPort) //判断DS是否响应,拉低数据线
{
Delay1ms(1);
i++;
if(i>5)
{
return 0; //判断超时,温度传感器为不存在
}
}
return 1; //温度传感器存在
}
2.18B20读时序

1.数据线拉0
2.延时4us
3.数据线拉1,释放总线准备读取数据
4.延时10us
5.读取数据线的1bit数据,进行数据处理
6.延时45us
7.重复以上步骤,读完1Byte
unsigned char Ds18b20ReadByte() //读取数据
{
unsigned char i,j,bi,rdat;
rdat = 0;
for(j=8;j>0;j--)
{
DsPort = 0; //1.数据线拉低 2.延时15us 3.数据线拉高,释放总线,准备读数据
//4.延时10us 5.读数据的状态,进行数据处理 6.延时45us
i++; //延时
DsPort = 1; //数据线拉高
i++; //延时
i++;
bi = DsPort; //读取1bit数据,储存数据
rdat = ( rdat >> 1 ) | (bi << 7 ); //bi = 1, 1000 0000 | 000000 = 1000 0000
i = 4;
while(i--); //延时>45us
}
return rdat;
}
3.18B20写时序

1.数据拉0
2.延时15us
3.按低位>>高位顺序发送数据(一次1位)
4.延时60us
5.数据线拉1
6.重复以上步骤,发完1Byte数据
7.数据线拉1,总线释放
void Ds18b20WriteByte(unsigned char dat)
{
unsigned i,j;
for(j=0;j<8;j++)
{
DsPort = 0; //1.数据线拉低 2.延时15us 3.发送数据,低位到高位 4.延时60u 5.数据线拉高
i++; //延时15us
DsPort = dat & 0x01; //发送数据
i=6;
while(i--); //延时68us
DsPort =1; //数据线拉高
dat >>= 1; //右移一位,发送下一位
}
}
四.51单片机具体实现代码
1.18B20D读取数据函数
/********************
*18B20D读取数据函数 *
* *
********************/
#include "temp.h" //加载头文件
void Delay1ms(uint y) //延时yms函数
{
uint x=100;
for(;y>0;y--)
{
for(x=110;x>0;x--);
}
}
uchar TempInit() //18B20初始化,判断温度传感器是否存在
{
uchar i = 0;
DsPort = 0; //数据线拉0
i = 70; //延时480-960us
while(i--); //实际延时642us
DsPort = 1; //拉高后延时等待80us,判断DS是否响应,响应数据线会被再次拉低
i=0;
while(DsPort) //判断DS是否响应,拉低数据线
{
Delay1ms(1);
i++;
if(i>5)
{
return 0; //判断超时,温度传感器为不存在
}
}
return 1; //温度传感器存在
}
void Ds18b20WriteByte(uchar dat) //写入数据操作
{
uchar i,j;
for(j=0;j<8;j++)
{
DsPort = 0; //1.数据线拉低 2.延时15us 3.发送数据,低位到高位 4.延时60u 5.数据线拉高
i++; //延时15us
DsPort = dat & 0x01; //发送数据
i=6;
while(i--); //延时68us
DsPort =1; //数据线拉高
dat >>= 1; //右移一位,发送下一位
}
}
uchar Ds18b20ReadByte() //读取数据操作
{
uchar i,j,bi,rdat;
rdat = 0;
for(j=8;j>0;j--)
{
DsPort = 0; //1.数据线拉低 2.延时15us 3.数据线拉高,释放总线,准备读数据
//4.延时10us 5.读数据的状态,进行数据处理 6.延时45us
i++; //延时
DsPort = 1; //数据线拉高
i++; //延时
i++;
bi = DsPort;
rdat = ( rdat >> 1 ) | (bi << 7 ); //bi = 1, 1000 0000 | 000000 = 1000 0000
i = 4;
while(i--); //延时>45us
}
return rdat;
}
void ChangeTemp() //温度转换
{
TempInit(); //器件初始化
Delay1ms(1); //延时
Ds18b20WriteByte(0xCC); //CC为跳过ROM,直接可以温度转换
Ds18b20WriteByte(0x44); //44为启动温度转换,一次需要750ms,连续检测可以不进行延时
}
void ReadTempCom() //温度读取指令
{
TempInit(); //器件初始化
Delay1ms(1); //延时
Ds18b20WriteByte(0xCC); //CC为跳过ROM,直接可以温度转换
Ds18b20WriteByte(0xBE); //BE为读取ROM的数据,两个字节
}
int ReadTemp() //封装函数,读取温度
{
int temp = 0;
uchar tmh,tml; //存储温度高/低字节
ChangeTemp(); //温度转换
ReadTempCom(); //温度读取
tml = Ds18b20ReadByte(); //ROM的低字节
tmh = Ds18b20ReadByte(); //ROM的高字节
temp = tmh; //存储高字节
temp <<= 8;
temp |= tml; //存储低字节
return temp;
}
2.18B20D读取数据函数的头文件
/****************************
*18B20D读取数据函数的头文件 *
* *
****************************/
#ifndef _temp_H
#define _temp_H
#include <reg52.h>
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
int ReadTemp();
sbit DsPort = P3^7;
#endif
3.主函数
/*
DS18B20 温度传感器,1 GND 2 DATA 3 VDD
组成:
1. 64bit光刻ROM 是 B20的地址序列号: 28H 类型 + 48bit 序列号 + RCR循环冗余校验(8bit)
2. 精度可编程 9/10/11/12位,0.5/0.25/0.125/0.0625℃,默认12位
3. 工作过程:等待 >>> MUC 发送44H >>> 器件进行温度测量和AD转换 >>> 数据存储再温度寄存器(2字节/16bit)
>>> 等待
4 温度寄存器组成:MS + LS ,
MS的前5为符号位,T>0, 全为0 / T<0,全为1,且后面的数据需要取反加1
MS 3bit + LS 高4bit 为整数,转换为温度时只需要乘以1℃
LS 低4bit 为小数,转换为温度时只需要乘以0.0625 1000 = 8*0.0625 =0.5 摄氏度
eg:0000 0111 1101 1000 >>>> 125.5℃
*/
#include "reg52.h"
#include "temp.h"
typedef unsigned char u8;
typedef unsigned int u16;
sbit LSA = P2^2; //数码管选通
sbit LSB = P2^3;
sbit LSC = P2^4;
u8 code smgduan[] = {0x3f, 0x06, 0x5b, 0x4f,
0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c,
0x39, 0x5e, 0x79, 0x71};
//数据管0-F显示
u8 disp[4]; //数码管数据
u8 num;
int read;
void delay(u16 i) //延时函数
{
while(i--);
}
void datapros(int temp) //对读取18B20的数据进行处理
{
float tp; //暂时存储温度数据
read = temp; //用于串口通讯读取
if(temp<0) //温度小于0,需要取反+1;补码;我们还原时需要-1,取反
{
disp[0] = 0x40; //显示负号
temp = ~(temp-1); //数据还原,-1 ,取反
tp = temp;
temp = tp * 0.0625 * 100 + 0.5; //温度的精度0.0625,乘以100用于保留小数后两位,同时将float转为int;0.5用于进位
}
else //温度>0
{
disp[0] = 0x00; //符号位显示
tp = temp;
temp = tp * 0.0625 * 100 + 0.5;
}
disp[1] = smgduan[temp / 10000]; //除10000的目的时,之前将temp*100,这边返
回,然后取百分位
disp[2] = smgduan[temp % 10000 / 1000]; //十位
disp[3] = smgduan[temp % 10000 %1000 / 100] | 0x80;
//个位+小数点
disp[4] = smgduan[temp %100 / 10 ]; //小数第一位
disp[5] = smgduan[temp %100 % 10]; //小数第二位
}
void DigDisplay() //数码管显示函数
{
u8 i;
for(i= 0; i<6;i++) //对6个8位数码管进行循环扫描
{
LSA = i % 2;
LSB = i / 2 % 2;
LSC = i / 4 % 2;
P0 = disp[i];
delay(100);
P0 = 0X00; //消影
}
}
void UsartInit() //串口通讯初始化
{
TMOD |= 0X20; //TMOD ,低四位控制T0,GATE,C/~T ,M1 ,M0 ===> C/T=0定时器模式, M1,M0 = 1 0 模式2,8位定时器自动重装载,,GATE = 0软件控制定时器 =1是使用外部中断
//使用|=运算,是为了不影响前面位
TH1 = 0XF3; //设置定时器,用于输出波特率,设置波特率初值,对应的是4800, 波特率 = 2^smod * f / 32 /(12* (2^8-X))》》》
TL1 = 0XF3; // X = 256 - 32 * 12 * 波特率 / 2^smod / f
PCON = 0X80; //SMOD 用于倍频 ,但是不可以位寻址,所以需要对所在的PCON设置
TR1 = 1; //开启定时器1
SCON =0X50; //串行通讯控制 0101 0000 SM0 SM1 SM2(多机通讯) REN(串口接收允许位) TB8 RB8 TI RI(接收中断标志位);工作方式1:1帧10位
ES = 1; //中断使能
EA = 1;
}
void main() //主函数
{
UsartInit();
while(1)
{
datapros(ReadTemp());
DigDisplay();
}
}
void Usart() interrupt 4
{
u8 i,byte;
int a ; //int只占据两个字节
//u8 recData = SBUF; //读取缓存寄存器的数据,RI=1(中断标志位)进入中断
RI = 0; //需要手动清零
a = read;
//SBUF = recData;
for(i = 0; i < sizeof(a); i++) //循环发送两个字节
{
byte = (a >> (i * 8)) & 0xFF;
SBUF = byte; //把收到数据发送出去
while(!TI); //等待发送结束
TI = 0;
}
}