一、DS18B20简介

DS18B20是一种具有高精度、数字输出和单总线通信数字温度传感器,可以提供9位温度读数。

1. 主要特点

  1. 数字输出:DS18B20以数字形式输出温度数据,使得温度测量更为准确和方便。
  2. 高精度:DS18B20具有较高的温度测量精度,通常为±0.5°C。
  3. 单总线通信:DS18B20采用1-Wire总线协议进行通信,只需要一条数据线就可以完成通信和供电,简化了连接方式。
  4. 多种封装形式:DS18B20可提供不同的封装形式,包括TO-92、TO-220、SMD等,以适应不同的应用场景。
  5. 低功耗:DS18B20在工作时的功耗较低,适合于一些对电源要求较为严格的应用。
  6. 数字校准:DS18B20内置了温度校准寄存器,可以通过软件进行校准,提高了温度测量的准确性。

2. 工作原理

DS18B20温度传感器基于基于热敏电阻原理工作。其内部包含了一个温度敏感的热敏电阻和一个用于转换模拟信号为数字信号的模数转换器(ADC)。当传感器被激活时,它会测量周围环境的温度,并将温度转换为数字信号。然后,数字信号通过1-Wire总线协议传输给主控制器,主控制器可以读取并解析这些数据,并将其转换为实际温度值。

下面是DS1820的方框图:
在这里插入图片描述

3. 引脚说明

在这里插入图片描述

DS1820S引脚:

  • GND: 地
  • DQ:数字输入输出
  • VDD:可选的VDD
  • NC:空引脚
  • DNC:不连接

4. ROM

每个DS18B20包括了一个唯一的 64位长ROM编码,这样可以在一个I/O端口上连接多个DS18B20设备。
ROM编码格式如下:

8位CRC编号48位序列号8位产品系列编码

二、1-wire协议简介

1-Wire协议是一种串行通信协议,用于在单一数据线上进行通信和供电。它由独特的通信方式和协议约定组成,广泛应用于各种数字设备和传感器之间的通信。

1. 总线结构:

1-Wire总线通常由一条数据线(Data Line,通常简称为DQ线)组成,用于同时传输数据和提供电源。总线结构简单,只需要一条数据线就可以实现通信和供电,因此非常适合于一些有限空间和资源的应用场景。

2. 通信方式:

1-Wire通信采用两种基本状态:高电平和低电平。数据传输是通过一系列脉冲来实现的,每个脉冲的持续时间决定了数据位的传输速率。

3. 数据传输:

数据传输通过一系列脉冲来完成,包括复位脉冲(Reset Pulse)、写脉冲(Write Pulse)、读脉冲(Read Pulse)等。通信的开始通常由主设备发出复位脉冲来初始化通信。

4. 设备识别:

1-Wire协议允许多个设备连接到同一数据线上,每个设备具有唯一的64位ROM代码,可以通过该代码唯一识别每个设备。因此,在进行通信之前,需要先对设备进行识别和寻址。

5. 供电方式:

1-Wire设备通常具有内部电源管理电路,可以通过数据线提供电源。这意味着在连接1-Wire设备时,不仅可以传输数据,还可以通过数据线为设备提供电源。但对于某些特定的1-Wire设备,可能需要额外的电源引脚来提供电源。

6. 应用场景:

1-Wire协议广泛应用于各种数字设备和传感器之间的通信,包括温度传感器(如DS18B20)、实时时钟模块、智能卡等。由于其简单、灵活和节省资源的特点,1-Wire设备在一些特定的应用场景中具有很大的优势。

7. 优缺点:

  • 优点:简单、节省引脚、灵活性高、成本低。
  • 缺点:通信速率较慢、距离受限、抗干扰能力相对较差。

三、DS18B20 寄存器与ROM操作命令

1. 配置寄存器

TMR1R011111

配置寄存器主要使用R1、R0寄存器,用来配置精度,默认是 12bits。

设置DS18B20工作在不同精度上,最大转换时间也会不同,对应关系如下:

R1R0精度Bit位数最大转换时间
00±0.5°C9 bits94ms
01±0.25°C10 bits188ms
10±0.125°C11 bits375ms
11±0.0625°C12 bits750ms

2. 温度寄存器

DS18B20温度传感器内部有一个16位的温度寄存器,用于存储当前测量到的温度值。这个温度寄存器是只读的,主要用于将温度数据传递给主控制器。

(1)数据格式

温度寄存器内部存储的温度数据以二进制形式表示,包括整数部分和小数部分。对于12位分辨率的DS18B20,温度数据被表示为一个16位的二进制数,其中高字节存储整数部分,低字节存储小数部分。

(2)温度表示

DS18B20采用补码表示温度数据,温度的范围是-55°C到+125°C。温度数据的实际值可以通过以下公式计算得到:

温度值 = (温度寄存器内容 * 0.0625) °C

温度寄存器T0放温度的低字节,T1温度的高字节。 其中高字节的 bit15~bit11表示温度的正负。

  • 如果测量结果是正值,则符号位全是0,测量的结果*精度 (默认0.0625)就是实际结果。
  • 如果测量结果是负值,则符号位全是1,测量的结果要把测试的结果取反+1,再去乘精度。

示例:

温度数字输出二进制实际结果16进制
25°C00000000 001100100x0032
-25°C11111111 110011100xFFCE
0°C0000 00000x0000

这个表格列出了几个例子,包括温度、对应的数字输出二进制以及实际结果的16进制表示。

3. ROM操作命令

DS18B20温度传感器在ROM操作未定建立之前不能使用存储和控制操作。
主机必须首先提供5种ROM操作之一:

  • READ ROM
  • Match ROM
  • Search ROM
  • Skip ROM
  • Alarm Search

下面是ROM操作命令说明:

  1. Read ROM (0x33):读取ROM代码

    • 这个命令允许主机读取DS18B20的64位唯一ROM代码。
    • 主机发送此命令后,DS18B20会按顺序将其ROM代码的各个字节发送回主机。
  2. Match ROM (0x55):匹配ROM代码

    • 此命令用于向DS18B20设备发送一个64位的ROM代码,以便与设备进行匹配。
    • 当主机发送Match ROM命令并提供与DS18B20设备ROM代码相匹配的地址时,DS18B20设备会响应后续的命令。
  3. Skip ROM (0xCC):跳过ROM代码

    • 此命令用于跳过ROM代码匹配阶段,直接向总线上的所有DS18B20设备发送命令。
    • 当主机发送Skip ROM命令时,总线上所有的DS18B20设备都会响应后续的命令。
  4. Search ROM (0xF0):搜索ROM代码

    • 这个命令用于在总线上搜索所有连接的DS18B20设备的64位ROM代码。
    • 主机发送Search ROM命令后,总线上的所有DS18B20设备都会响应,按顺序发送其ROM代码。
  5. Alarm Search (0xEC):搜索报警状态

    • 此命令用于搜索总线上所有连接的DS18B20设备,并确定是否有设备处于报警状态。
    • 主机发送Alarm Search命令后,总线上的所有DS18B20设备都会响应,按顺序发送其报警状态。

通过这些ROM操作命令,主机可以与DS18B20设备进行通信、唯一识别每个设备并获取设备信息,从而实现对DS18B20温度传感器的精确控制和监控。

4. 时序

(1)初始化时序

在这里插入图片描述

  1. 主机输出低电平(480us~960us);
  2. 主机释放总线(I/O拉高,15-60us),进入接收模式;
  3. DS18B20应答,拉低 (60-240us);
  4. DS18B20拉高或持续拉低输出1bit数据。

(2)写时序

在这里插入图片描述
写时序分为写0、写1时序,一次读至少60us,两个读之间至少间隔1us恢复时间。

写1示例:

  1. 主机拉低总线(2us左右);
  2. 主机释放总线
  3. 主机拉高,(延时60us左右);

写0示例:
4. 主机拉低;
5. 继续低 ,60us左右;

(3)读时序

在这里插入图片描述
一次读时序至少60us,两次读之间至少1us恢复时间;

  1. 主机拉低(1us);
  2. 释放总线;
  3. 在15us内读取总线数据;

5. 温度测试操作过程

温度测量的操作过程通常包括以下步骤:

  1. 初始化:如果需要唯一识别特定的DS18B20设备,则需要发送ROM操作命令来匹配设备的ROM代码。
  2. 启动温度转换:发送给所有DS18B20设备的(Skip ROM命令,0XCC)。
  3. 发开始转换命令:0x44
  4. 延时
  5. 复位
  6. 发SKIP ROM
  7. 发读存储器命令: 0xBE
  8. 读两个字节数据
  9. 转换成温度值使用。

6. 寄生电源

DS18B20温度传感器具有一种称为"寄生电源"(Parasitic Power)的特殊模式,可以通过这种模式在单一数据线上进行通信和供电。在寄生电源模式下,DS18B20传感器不需要额外的电源引脚,而是通过数据线从主控制器获取所需的电源。

DS1820的工作电流达1mA,当几个SD1820在同一条I/O口并进行温度变换时,I/O口可能没有足够的驱动能力。 解决的方法有两种:

  1. 发生温度变换时,在I/O提供强上拉,这时VDD接地。
    在这里插入图片描述
  2. 另一种方法是VDD接外部电源,这样I/O就不需要强上拉。

7. 寄生电源/VDD供电约定

I/O口的主机通常不知道总线上的DS182是寄生电源供电还是外部VDD供电。如果MCU需要获知供电方式 , 在DS18B20内部采取了一种方法来通知主控制器它的供电方式。

当主控制器发送一个跳过(Skip)ROM命令后,紧接着发送一个读取电源状态的命令时,DS18B20会做出不同的响应以表明其供电方式。

  • 如果DS18B20采用寄生电源供电,它会在单一数据线上发送一个“0”;
  • 如果DS18B20采用外部VDD供电,它会发送一个“1”。

主控制器根据接收到的响应来确定是否需要在数据线上提供强上拉。

四、使用示例

1. 硬件连接

开发板 DS18B20连接在P3.7引脚,该引脚已经默认上拉。
在这里插入图片描述
在这里插入图片描述

2. 代码实现

(1)DS18B20 初始化

/**
* @brief  DS18B20初始化
*/
void ds18b20_reset(void){
	// 拉低DQ
	DS18B20_PORT=0;	
	// 持续750us
	delay_10us(75);
	// 释放DQ
	DS18B20_PORT=1;
	// 等待20us
	delay_10us(2);
}

(2)等待响应

/**
* @brief DS18B20响应信号,用来判断DS18B20是否存在
* @return 0:成功 1:失败
*/
u8 ds18b20_ack(void){
    u8 timeout = 0;
    // 读取响应信号,60us左右的低电平信号
    while(DS18B20_PORT && timeout <20){
        timeout ++;
        delay_10us(1);
    }
    if(timeout >= 20){
        // 超时了
        return 1;
    }else{
        timeout = 0;
    }
    // 释放引脚,引脚会被拉高,240us左右的高电平信号
		while((!DS18B20_PORT)&&timeout<20)	//等待DQ为高电平
		{
			timeout++;
			delay_10us(1);
		}
    if(timeout >= 20){
        return 1;
    }
    return 0;
}

(3)写字节

/**
* @brief DS18B20写一个字节
* @return 写字节
*/
void ds18b20_write_byte(u8 dat){
	u8 i=0;
	u8 temp=0;

	for(i=0;i<8;i++)//循环8次,每次写一位,且先写低位再写高位
	{
		temp=dat&0x01;//选择低位准备写入
		dat>>=1;//将次高位移到低位
		if(temp)
		{
			DS18B20_PORT=0;
			_nop_();_nop_();
			DS18B20_PORT=1;	
			delay_10us(6);
		}
		else
		{
			DS18B20_PORT=0;
			delay_10us(6);
			DS18B20_PORT=1;
			_nop_();_nop_();	
		}	
	}	
}

(4)读一个bit

/**
* @brief DS18B20读取一个bit
* @return 读取到的bit
*/
u8 ds18b20_read_bit(void){
	u8 dat=0;
	
	DS18B20_PORT=0;
	_nop_();_nop_();
	DS18B20_PORT=1;	
	_nop_();_nop_(); //该段时间不能过长,必须在15us内读取数据
	if(DS18B20_PORT)dat=1;	//如果总线上为1则数据dat为1,否则为0
	else dat=0;
	delay_10us(5);
	return dat;
}

(5)读一个字节

/**
* @brief DS18B20读取一个字节
* @return 读取到的字节
*/
u8 ds18b20_read_byte(void){
	u8 i=0;
	u8 dat=0;
	u8 temp=0;

	for(i=0;i<8;i++)//循环8次,每次读取一位,且先读低位再读高位
	{
		temp=ds18b20_read_bit();
		dat=(temp<<7)|(dat>>1);
	}
	return dat;	
}

(6)启动转换

/**
* @brief DS18B20开始转换
*/
void ds18b20_start(void){
    ds18b20_reset();
    ds18b20_ack();
    ds18b20_write_byte(0xcc);
    ds18b20_write_byte(0x44);
}

(7)读取温度全过程

/**
* @brief DS18B20读取温度
* @return 温度值
*/
float ds18b20_get_temperature(void){
	float temp;
	u8 tempH=0;
	u8 tempL=0;
	u16 value=0;

	// 开始转换
	ds18b20_start();
	// 复位
	ds18b20_reset();
	// 等待DS18B20的响应
	ds18b20_ack();
	// 发送SKIP ROM命令
	ds18b20_write_byte(0xcc);
	// 发送读存储器命令
    ds18b20_write_byte(0xbe);

	// 读取温度值低字节
	tempL = ds18b20_read_byte();
	// 读取温度值高字节
	tempH = ds18b20_read_byte();
	// 计算温度值
	value = (tempH<<8) + tempL;

	// 温度值转换,负温度是补码
	if((value&0xf800)==0xf800)//判断符号位,负温度
	{
		// 取反再加1
		value=(~value)+1; 
		temp=value*(-0.0625);
	}
	else 
	{
		// 正温度
		temp=value*0.0625;	
	}
	return temp;
}

为简便起见,本文示例代码未显示小数点。

(8)main.c

#include <reg52.h>
#include "led_utils.h"
#include "common_utils.h"
#include "timer_utils.h"
#include "uart_utils.h"
#include "key_utils.h"
#include "eeprom_utils.h"
#include "segment_display_utils.h"
#include "ds18b20.h"

/**
* @brief 主函数
*/
main()
{
	float temperature = 0;
	int intTemperature = 0;
	u8 i =0;
	// 关闭所有led
	led_all_off();
	uart_init(0xFA);
	ds18b20_init();
	
	while(1)
	{
		i++;
		if(i % 50==0){
			temperature = ds18b20_get_temperature();
			intTemperature = temperature * 10;
		}
		segment_show_int(intTemperature);
	}
}

本文代码开源地址:
https://gitee.com/xundh/learn51

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐