目录
- 1.DS18B20介绍
- 2.读取温度数据
-
- 2.1初始化时序
- [2.2 写时序](#2.2 写时序)
- [2.3 读时序](#2.3 读时序)
- 3.硬件设计
- 4.软件设计
-
- [4.1 DS18B20代码](#4.1 DS18B20代码)
- [4.2 整体代码整合](#4.2 整体代码整合)
- 5.实验现象
1.DS18B20介绍

DS18B20是由美国Dallas半导体公司推出的数字温度传感器,采用单总线协议,仅需一根信号线即可完成数据传输,无需额外的模拟到数字转换器(ADC)。其测量范围为-55°C至+125°C,在-10°C至+85°C范围内的测量精
度为**±0.5°C**。该传感器的封装小巧,适合嵌入狭小空间,且具有良好的抗干扰能力。
特点:
1.支持电压3.0 ~ 5.5V电压范围,如果说单片机系统电压是3.3v的,也是可以,如果单片机系统是5v的,也是可以。
在寄生电源方式下,可由数据线来供电,也就是说DS18B20除了给电源管脚供电之外,它还能让温度传感器工作在寄生电源方式模式下,可以直接通过数据线,也就是DQ引脚来供电,当然我们很少这样操作,直接给它的电源管教供电最简单。
2.它有独特的单线接口方式,从上面图可以看到DS18B20总共就三条线,除了电源VDD和接地线GND,那只剩下的一根数据线,所以DS18B20接口比较简单,它与微处理器进行连接的时候只需要一根线就可以实现数据通信了。
3.DS18B20可以支持多点组网功能,就是说多个DS18B20可以同时并联在单总线上,也就是三个DS18B20分别让三个GND相连,三个DQ相连,三个VDD相连,并联多个DS18B20实现组网。
4.DS18B20在使用中不需要任何的外围元器件,全部的传感器原件以及转换电路全部集成在DS18B20这个集成电路中,DS18B20外形,有点类似于插件类的三极管,所以在使用DS18B20的时候,需要查看表面的名称。
5.DS18B20工作范围比较宽,-55°C至+125°C, 在-10°C至+85°C范围内的测量精度为±0.5°C,精度比较高。
6.DS18B20可以实现对DS18B20的分辨率的位数进行设置,分辨率可以设置9位,10位,11位,12位等等,其中9位的分辨率温度范围是±0.5℃,10位的分辨率温度范围是±0.25℃,11位的分辨率温度范围是±0.125℃,12位的分辨率温度范围是±0.0625℃,默认情况下,DS18B20工作在12位的精度模式,所以精度是0.0625的精度,非常高。还有在DS18B20当中,分辨率不一样,对应的温度转换时间不一样,比如使用9位的话,温度转换时间最少是93.75毫秒,也就是说我们需要读取温度传感器的温度,差不多要间隔93.75个毫秒的时间读取,如果是12位的分辨率的温度是在750毫秒,所以我们需要读取这个温度值,需要大于750毫秒之后去读取,分辨率越高,转换时间越慢,这个DS18B20测量的结果直接以输出数字信号的,通过DQ也就是单总线,串行输出给CPU,可以传送CRC校验码,所以它抗干扰能力强,还有越高负压特性,也就是说VDD和GND接反的时候,芯片不会烧坏,但是它会发热,发热也不会烧坏,这是理论情况下,但是实际情况下,如果短接时间长了可能会烧掉,理论上VDD和GND接反了,它只发热不工作但不会烧坏。

从这个图中可以看到,它里面有一个ROM,这个ROM中64位序列号是出厂前被光刻好的,它可以看做DS18B20的地址序列号,64位的光刻ROM它的排列是按照8位,这个8位是产品类型的标号,通常是8(28H)+DDS18B20自身序列号48位+最后8位是前面56位的CRC校验 ,光刻ROM的作用是使每一个DS18B20序列号都不相同,这个可以实现一根单总线上面组网,因为这个DQ在总线上面连接了多个DS18B20设备,比如说在一根总线上连接了多个DS18B20上面,我想要访问哪一个DS18B20它依靠的就是光刻ROM,每一个DS18B20里面ROM数字是不一样的,所以通过8(28H) + 48位+ 8位这个来对应的我要使用的哪一个DS18B20的读取温度。
那么DS18B20内部存储器,有一个高速缓存存储器,这个高速缓存存储器包括了高速暂存器RAM和非易失性可电擦除的EEPROM,后者存储的是高温度触发和低温度触发以及配置寄存器等,这是非易失性可电擦除的EEPROM,所以DS18B20读取的温度,是保存在EEPROM中,然后我们读取出来。
那么它的配置寄存器是配置不同的位数来确定温度和数字的转化,配置寄存器的结构在这个图当中可以看到

八个位,其中后面五位全是1,全是1的话,那么就看高三位,高三位TM是测试模式位,用来设置DS18B20在工作模式还是测试模式,在出厂的时候,DS18B20第1位TM已经设置0了,我们用户是无法更改的,其中R1和R0是用来设置DS18B20的分辨率的,也就是它的精度,你是八位还是九位,还是十一位,或者十二位,通过R1和R0这两位进行设置,对应的设置如下图中的表可以看到。

当R1和R0也就是精度处于9位的模式,也就是 0 0 最大转换时间是读取温度,就是说在DS18B20内部一个转换器当中,需要转换成数字温度值需要93.75个毫秒
当是0 1的时候,也就是处于10位模式,需要187.5个毫秒
1 0的时候是11位,工作在11位模式,需要375个毫秒
1 1的时候是12位,工作在12位模式,需要750个毫秒
DS18B20默认的工作模式是1 1,也就是12位,所以它的转换数字温度最大时间需要750个毫秒,才能转换成功,DS18B20默认是12位,通常我们就用默认的这个值来读取温度,也就是12位的一个温度精度。
所以我们就是说不会去设置R1和R0,让它处于低精度的位置,所以既然我们使用温度传感器,我们希望它能获取更高精度的温度值,自然12位是最高的精度,所以我们就保持默认的就可以了,不需要更改R1和R0。
高速缓存存储器由9个字节组成,9个字节分别是
低温度寄存器(LS),高温度寄存器(MS),高温限制(TH),低温限制(TL),配置寄存器,三个保留,CRC校验
其中我们主要使用的是温度,至于那个高温和低温限制阈值我们不使用它,如果说我们需要对温度设置阈值,我们就通过软件设置就可以了,操作内部寄存器。
温度寄存器格式,当我们温度转换命令,44H发布之后,经转换所得到温度值是以二进制补码形式存放在高速暂存存储器当中,D0和第D1个两个字节,高速暂存存储器中,前面两个字节是温度,其中D0存储的温度的低位,D1存储的温度是高位,其中在温度的高字节前五位是表示符号位,因为我们得到的温度有可能是正温度,有可能是负温度,可以通过单片机IO口读取温度传感器的数据。

其中LS是温度的低八位字节

MS是温度的高八位,其中前面五位表示温度,后面的三位 + 低字节LS的八位表示温度值,如果说测的温度大于0,那么测得的前面高5位符号位就是为0,表示正温度,只要将测得的温度值 * 0.0625,因为DS18B20默认12位的分辨率,所以分辨率精度是0.0625,所以我们得到的温度值 * 0.0625就是我们的实际的温度值,如果说温度小于0的情况下,也就是得到了负温度,负温度情况下,它的高5位都是1,然后转换成实际的温度,是需要将测得的数值进行取反,也就是这两个字节数据取反,取反+1然后结果* 0.0625就得到我们的实际的温度了,

温度对应数据的关系,数据就是对应的高八位和第八位两个字节数据。
例如可以看到温度是85℃,二进制数据是0000 0101 0101 0000,对应的十六进制是0550,其中05表示高字节,50表示低字节,其中05可以看到二进制是0000 0101对应的就是05,前五位都是0,表示符号位都是0,为0的话表示正温度,高字节的后三位 + 低字节的8位就是实际的温度,如果是正温度的话,就是我们测得的值 * 0.0625就是实际的温度值.
例如85℃如果计算的,0550换算成10进制得到1360,然后1360 * 0.0625结果就是85。
就是说高字节前五位如果是0,表示正温度,然后将高字节后面3位 + 低字节8位一共11位有效数据值转换成16进制值,然后将16进制转换成二进制,将二进制转换成10进制,结果 * 0.0625就是最终的正温度的值。
例如-0.5℃如何计算的,它的16进制是FFF8,对应的二进制是1111 1111 1111 1000,变成0000 0000 0000 0111得到的新的二进制,然后+1变成 0000 0000 0000 1000 转换成16进制是0X08。对应的10进制的值是8。
然后将8 * 0.0625得到0.5。由于是负温度,所以是-0.5℃。
如果说高字节前五位如果是1,表示负温度,需要将高字节后面5位 + 低字节8位一共11位有效数据,全部取反,
然后+1得到新的二进制数,然后将新的二进制数转换成10进制数,十进制的值*0.0625就是最终的负温度的温度值。
2.读取温度数据
由于DS18B20是单总线器件,单总线器件都需要严格的信号时序来保证数据的完整性。DS18B20时序包括一下几种。
①初始化时序
②写0和写1的时序
③读0和读1的时序
DS18B20发送所有的命令,和数据都是字节的低位在前,这一点很重要,我们在编写程序时候,在传输数据的时候,一定要知道,这个字节在传输或者读取的时候都是低位开始的也就是LSB开始的,然后到高位MSB。
2.1初始化时序

刚开始主机是高电平,然后主机从高电平变成低电平时候,至少保持低电平480个微秒,这个时间范围是480 ~ 960个微秒,然后拉高电平释放总线,释放总线从低电平变成高电平时间是15 ~ 60个微秒,也就是说,从低电平变成高电平,高电平保持的时间需要是15 ~ 60个微秒范围,之后就进入的接收模式,灰色的线是DS18B20产生的信号线,它会拉低总线,拉低总线的时间是60 ~ 240个微秒范围,拉低之后产生了应答,一开始主机会产生复位信号(由高电平变成低电平)给DS18B20,DS18B20收到信号后,会给单片机应答信号,后面灰色的从高电平变成低电平区间就是DS18B20的应答信号,如果DS18B20后面还需继续延时,最终总延时时长是480个微秒,如果不延时,可以通过上拉电阻拉高,总共的时间是480个微秒。
2.2 写时序

写时序包含写0和写1,所有写时序至少保证60个微秒,并且在两次独立写时序之间,至少一微秒恢复时间,两次写时序均开始于主机拉低总线。
写1时序,主机拉低2个微秒,2个微秒之后,释放总线,拉高电平,拉高延时时间是60个微秒。
写0时序,由主机输出一个低电平,至少保持60微秒,开启数据的写,最后释放总线,拉高电平,延时2个微秒
2.3 读时序

单总线器件,仅在主机读时序的时候,才向主机传输数据,所以,在主机发出数据读取数据命令之后必须马上产生读时序,以便于从,从机能够传输数据,所以读时序至少需要60个微秒,单总线所要求的,并且两次读时序之间,至少需要1微秒恢复时间,每个读时序都是由主机发起,发起它是产生一个低电平,低电平的时间大概是1个微秒,主机在读时序期间呢,必须释放总线,也就是说在读时序的时候,我们要释放总线让DQ输出高电平,然后读取DQ上面的数据,释放总线的时候,并且在时序起始之后,也就是开始之后15个微秒,进行采集总线的状态,你是1还是0。
读1
开始由主机拉低总线,产生一个低电平,这个低电平延时至少1个微秒,然后释放总线,让总线拉高电平,控制在15个微秒之内,来读取这个总线上的数据,在读的过程中,整个完成至少是在60个微秒。
DS18B20的典型温度读取过程为:复位→发SKIPROM命令(OXCC)→发开始转换命令(OX44)→延时→复位→发送SKIPROM命令(OXCC)发读存储器命令(OXBE)→连续读出两个字节数据(即温度)→结束。
3.硬件设计


4.软件设计
实现的功能,插上DS18B20温度传感器,数码管显示检测的温度值。
4.1 DS18B20代码
初始化图示:

刚开始总线拉低,需要保持480 ~ 960个微秒之间,然后需要释放总线,将总线变成高电平,需要延时15 ~ 60微秒之间。
后面灰色部分就是从机DS18B20等待的过程,这个是自动执行拉低,但是它自动拉低电平,需要一个等待它拉低的时间,在60~240个微秒时间范围内去等待,如果超过这个时间范围,还是没有拉低电平表示应答失败,否则就是应答成功。
接着就是从机释放总线,也是等待从机自动完成释放总线的过程,也是在一个时间范围内等待从机释放总线,释放总线的等待时间范围也是60 ~ 480个微秒完成的,如果超过这个时间范围,还是没有拉高电平表示释放总线失败,否则就是释放总线成功。
所以需要两次等待从机,第一次等待从机应答,第二次等待从机释放总线,等待的过程是从机完成的,我们需要给它一个等待的时间,时间是在60~240个微秒范围。
初始化
c
//初始化
void ds18b20_reste(void) {
DS18B20_POST = 0; //复位总线
delay_10us(75); //延时480 ~ 960微秒
DS18B20_POST = 1; //释放总线
delay_10us(2); //延时15 ~ 60微秒
}
等待从机应答和主机释放总线
c
//等待从机应答
u8 ds18b20_check(void) {
u8 time_temp = 0;
//在200个微秒范围内等待DS18B20从机拉低信号
while(DS18B20_POST && time_temp < 20) {
time_temp++;
delay_10us(1);
}
if(time_temp >= 20) return 1;
else time_temp = 0;
//在200个微秒范围内等待DS18B20主机拉高信号
while((!DS18B20_POST) && time_temp < 20) {
time_temp++;
delay_10us(1);
}
if(time_temp >= 20) return 1;
return 0;
}
封装初始化
c
//封装初始化
u8 ds18b20_init(void){
ds18b20_reste();
return ds18b20_check();
}

写数据,从低位到高位一位一位的写入。
写1的时序:
主机拉低总线,延时需要大于1个微秒,大概2个微秒,如果晶振频率是12M,一个机器周期就是1微秒,nop()函数就是1个微秒,然后拉高总线,整体延时60个微秒。
写0时序:
总线拉低,60个微秒,然后拉高总线,2个微秒
c
//写一个字节数据
void ds18b20_write_byte(u8 dat) {
u8 i;
u8 temp = 0;
for (i = 0; i < 8; i++) {
temp = dat & 0x01; //每次取出最低位
dat >>= 1;
if (temp) { //写1时序
DS18B20_POST = 0;
_nop_();_nop_; //延时2个微秒
DS18B20_POST = 1;
delay_10us(6); //延时60个微秒
}
else { //写0时序
DS18B20_POST = 0;
delay_10us(6);
DS18B20_POST = 1;
_nop_();_nop_;
}
}
}

读时序:
主机拉低,延时2个微秒,然后延时15个微秒,让从机稳定,从机稳定后,然后读取从机上的位,最后延时50微秒
c
//读一位
//读一位
u8 ds18b20_read_bit(void) {
u8 dat = 0;
DS18B20_POST = 0;
_nop_();_nop_();
DS18B20_POST = 1;
_nop_();_nop_();
if (DS18B20_POST)
dat = 1;
else
dat = 0;
delay_10us(5);
return dat;
}
//读1字节
u8 ds18b20_read_byte() {
u8 i = 0;
u8 temp = 0;
u8 dat = 0;
for (i = 0; i < 8; i++) {
temp = ds18b20_read_bit();
dat = (dat >> 1) | (temp << 7); //运算规则,先整体右移动1位,然后将新的数字放最高位
}
return dat;
}
DS18B20读温度过程:
DS18B20的典型温度读取过程为:复位→发SKIPROM命令(OXCC)→发开始转换命令(OX44)→延时→复位→发送SKIPROM命令(OXCC)发读存储器命令(OXBE)→连续读出两个字节数据(即温度)→结束。
c
//开始转换
//复位-发送0XCC命令-发送0x44命令
void ds18b20_start(void) {
ds18b20_reste(); //复位
ds18b20_check(); //等待应答
ds18b20_write_byte(0xCC);
ds18b20_write_byte(0x44);
}
//读温度
float ds18b20_read_temperture(void) {
u8 dat_H = 0;
u8 dat_L = 0;
u16 value = 0;
float temp = 0.0;
ds18b20_start(); //开始转换
delay_ms(1); //延时1ms
ds18b20_reste(); //复位和等待应答是一起的
ds18b20_check(); //等待应答
ds18b20_write_byte(0xCC);
ds18b20_write_byte(0xBE);
dat_L = ds18b20_read_byte(); //读取低8位
dat_H = ds18b20_read_byte(); //读取高8位
value = ((u16)dat_H << 8) | dat_L;
//与前五位进行判断是正温度,负温度
if ((value & 0XF800) == 0XF800) {
value = (~value) + 1;
temp = -(float)value * 0.0625;
}
else {
temp = (float)value * 0.0625;
}
return temp;
}
4.2 整体代码整合
main.c
c
#include "public.h"
#include "smg.h"
#include "ds18b20.h"
void main() {
u8 i = 0;
int temp_value = 0;
int temp = 0;
u8 temp_buf[6] = { 0X00 };
//检测初始化是否存在 存在return 0 不存在 return 1
ds18b20_init();
while(1) {
i++;
if (i % 75 == 0) //延时750个毫秒,确保下一次温度的读取
{
temp_value = ds18b20_read_temperture() * 100;
}
temp = temp_value;
if (temp_value < 0) {
temp_buf[0] = 0x40; //存储负号
temp = -temp_value;
}
else {
temp_buf[0] = 0x00;
}
temp_buf[1] = gsmg_code[temp / 10000]; //万
temp_buf[2] = gsmg_code[temp / 1000 % 10]; //千
temp_buf[3] = gsmg_code[temp / 100 % 10] | 0X80; //百
temp_buf[4] = gsmg_code[temp / 10 % 10]; //十
temp_buf[5] = gsmg_code[temp % 10]; //个
smg_display(temp_buf, 3);
}
}
ds18b20.c
c
#include "ds18b20.h"
#include "intrins.h"
//初始化
void ds18b20_reste(void) {
DS18B20_POST = 0; //复位总线
delay_10us(75); //延时480 ~ 960微秒
DS18B20_POST = 1; //释放总线
delay_10us(2); //延时15 ~ 60微秒
}
//等待从机应答
u8 ds18b20_check(void) {
u8 time_temp = 0;
//在200个微秒范围内等待DS18B20从机拉低信号
while(DS18B20_POST && time_temp < 20) {
time_temp++;
delay_10us(1);
}
if(time_temp >= 20) return 1;
else time_temp = 0;
//在200个微秒范围内等待DS18B20主机拉高信号
while((!DS18B20_POST) && time_temp < 20) {
time_temp++;
delay_10us(1);
}
if(time_temp >= 20) return 1;
return 0;
}
//封装初始化
u8 ds18b20_init(void){
ds18b20_reste();
return ds18b20_check();
}
//写1字节数据
void ds18b20_write_byte(u8 dat) {
u8 i;
u8 temp = 0;
for (i = 0; i < 8; i++) {
temp = dat & 0x01; //每次取出最低位
dat >>= 1;
if (temp) { //写1时序
DS18B20_POST = 0;
_nop_();_nop_(); //延时2个微秒
DS18B20_POST = 1;
delay_10us(6); //延时60个微秒
}
else { //写0时序
DS18B20_POST = 0;
delay_10us(6);
DS18B20_POST = 1;
_nop_();_nop_();
}
}
}
//读一位
u8 ds18b20_read_bit(void) {
u8 dat = 0;
DS18B20_POST = 0;
_nop_();_nop_();
DS18B20_POST = 1;
_nop_();_nop_();
if (DS18B20_POST)
dat = 1;
else
dat = 0;
delay_10us(5);
return dat;
}
//读1字节
u8 ds18b20_read_byte() {
u8 i = 0;
u8 temp = 0;
u8 dat = 0;
for (i = 0; i < 8; i++) {
temp = ds18b20_read_bit();
dat = (dat >> 1) | (temp << 7); //运算规则,先整体右移动1位,然后将新的数字放最高位
}
return dat;
}
//开始转换
//复位-发送0XCC命令-发送0x44命令
void ds18b20_start(void) {
ds18b20_reste(); //复位
ds18b20_check(); //等待应答
ds18b20_write_byte(0xCC);
ds18b20_write_byte(0x44);
}
//读温度
float ds18b20_read_temperture(void) {
u8 dat_H = 0;
u8 dat_L = 0;
u16 value = 0;
float temp = 0.0;
ds18b20_start(); //开始转换
ds18b20_reste(); //复位和等待应答是一起的
ds18b20_check(); //等待应答
ds18b20_write_byte(0xCC);
ds18b20_write_byte(0xBE);
dat_L = ds18b20_read_byte(); //读取低8位
dat_H = ds18b20_read_byte(); //读取高8位
value = ((u16)dat_H << 8) | dat_L;
//与前五位进行判断是正温度,负温度
if ((value & 0XF800) == 0XF800) {
value = (~value) + 1;
temp = value * (-0.0625);
}
else {
temp = value * 0.0625;
}
return temp;
}
ds18b20.h
c
#ifndef _ds18b20_H
#define _de18b20_H
#include "public.h"
sbit DS18B20_POST = P3^7;
//初始化
void ds18b20_reste(void);
//等待从机应答
u8 ds18b20_check(void);
//封装初始化
u8 ds18b20_init(void);
//写1字节数据
void ds18b20_write_byte(u8 dat);
//读一位
u8 ds18b20_read_bit(void);
//读1字节
u8 ds18b20_read_byte();
//开始转换
//复位-发送0XCC命令-发送0x44命令
void ds18b20_start(void);
//读温度
float ds18b20_read_temperture(void);
#endif
smg.c
c
#include "smg.h"
u8 gsmg_code[17] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; //共阴数码管0-F
void smg_display(u8 dat[], u8 pos) {
u8 i = 0;
u8 pos_temp = pos-1;
for (i = pos_temp; i < 8; i++)
{
switch(i) {
case 0: LSA = 0; LSB = 0; LSC = 0; break;
case 1: LSA = 1; LSB = 0; LSC = 0; break;
case 2: LSA = 0; LSB = 1; LSC = 0; break;
case 3: LSA = 1; LSB = 1; LSC = 0; break;
case 4: LSA = 0; LSB = 0; LSC = 1; break;
case 5: LSA = 1; LSB = 0; LSC = 1; break;
case 6: LSA = 0; LSB = 1; LSC = 1; break;
case 7: LSA = 1; LSB = 1; LSC = 1; break;
}
SMG_A_DP_POST = dat[i - pos_temp];
delay_10us(100);
SMG_A_DP_POST = 0X00;//消影
}
}
smg.h
c
#ifndef _smg_H
#define _smg_H
#include "public.h"
#define SMG_A_DP_POST P0
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;
extern u8 gsmg_code[17];
void smg_display(u8 dat[], u8 pos);
#endif
public.c
c
#include "public.h"
#include "stdio.h"
void delay_10us(u16 us) {
while(us--);
}
void delay_ms(u16 ms)
{
u16 i,j;
for(i=ms;i>0;i--)
for(j=110;j>0;j--);
}
public.h
c
#ifndef _public_H
#define _public_H
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
void delay_10us(u16 us);
void delay_ms(u16 ms);
#endif
5.实验现象
录制_2026_02_19_18_31_10_437