第20章 DS18B20温度传感器实验

第20章 DS18B20温度传感器实验

1. 导入

在前面章节, 我们介绍了如何使用单片机 IO 口模拟 IIC 总线时序。 这一章我们来学习精度较高的外部 DS18B20 数字温度传感器, 由于此传感器是单总线接口, 所以需要使用 51 单片机的一个 IO 口模拟单总线时序与 DS18B20 通信,将检测的环境温度读取出来。 开发板上集成了 1 个 DS18B20 温度传感器接口, 需插上 DS18B20 温度传感器后才能测试温度。

2. DS18B20介绍

DS18B20 是由 DALLAS 半导体公司推出的一种的“ 一线总线( 单总线) ” 接口的温度传感器。 与传统的热敏电阻等测温元件相比, 它是一种新型的体积小、适用电压宽、 与微处理器接口简单的数字化温度传感器。

屏幕截图 2024 06 17 092334

DS18B20 内部结构如下图所示:

屏幕截图 2024 06 18 081554

ROM 中的 64 位序列号是出厂前被光刻好的, 它可以看作是该 DS18B20 的地址序列号。 64 位光刻 ROM 的排列是: 开始 8 位( 28H) 是产品类型标号, 接着的 48 位是该 DS18B20 自身的序列号, 最后 8 位是前面 56 位的循环冗余校验码。 光刻 ROM 的作用是使每一个 DS18B20 都各不相同, 这样就可以实现一根总线上挂接多个 DS18B20 的目的。

DS18B20 温度传感器的内部存储器包括一个高速的暂存器 RAM 和一个非易失性的可电擦除的 EEPROM,后者存放高温度和低温度触发器 TH、 TL 和配置寄存器。

配置寄存器是配置不同的位数来确定温度和数字的转化, 配置寄存器结构如下:

屏幕截图 2024 06 18 081752

低五位一直都是"1", TM 是测试模式位, 用于设置 DS18B20 在工作模式还是在测试模式。 在 DS18B20 出厂时该位被设置为 0, 用户不需要去改动。 R1 和R0 用来设置 DS18B20 的精度( 分辨率) , 可设置为 9, 10, 11 或 12 位, 对应的分辨率温度是 0.5℃ , 0.25℃ , 0.125℃ 和 0.0625℃ 。 R0 和 R1 配置如下图:

屏幕截图 2024 06 18 081909

在初始状态下默认的精度是 12 位, 即 R0=1、 R1=1。 高速暂存存储器由 9 个字节组成, 其分配如下:

屏幕截图 2024 06 18 082005

当温度转换命令( 44H) 发布后, 经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第 0 和第 1 个字节。 存储的两个字节, 高字节的前 5 位是符号位 S, 单片机可通过单线接口读到该数据, 读取时低位在前, 高位在后,数据格式如下:

屏幕截图 2024 06 18 082058

如果测得的温度大于 0, 这 5 位为‘ 0’ , 只要将测到的数值乘以 0.0625 ( 默认精度是 12 位) 即可得到实际温度; 如果温度小于 0, 这 5 位为‘ 1’ ,测到的数值需要取反加 1 再乘以 0.0625 即可得到实际温度。 温度与数据对应关系如下:

屏幕截图 2024 06 18 082215

比如我们要计算+85 度, 数据输出十六进制是 0X0550, 因为高字节的高 5位为 0, 表明检测的温度是正温度, 0X0550 对应的十进制为 1360, 将这个值乘以 12 位精度 0.0625, 所以可以得到+85 度。

知道了怎么计算温度, 接下来我们就来看看如何读取温度数据, 由于 DS18B20是单总线器件, 所有的单总线器件都要求采用严格的信号时序, 以保证 数据的完整性。

DS18B20 时序包括如下几种: 初始化时序、 写( 0 和 1) 时序、 读( 0和 1) 时序。 DS18B20 发送所有的命令和数据都是字节的低位在前。 这里我们简单介绍这几个信号的时序:

  • 初始化时序

单总线上的所有通信都是以初始化序列开始。 主机输出低电平, 保持低电平时间至少 480us( 该时间的时间范围可以从 480 到 960 微妙) , 以产生复位脉冲。 接着主机释放总线, 外部的上拉电阻将单总线拉高, 延时 15~60 us, 并进入接收模式。 接着 DS18B20 拉低总线 60~240 us, 以产生低电平应答脉冲, 若为低电平, 还要做延时, 其延时的时间从外部上拉电阻将单总线拉高算起最少要480 微妙。 初始化时序图如下:

屏幕截图 2024 06 18 082413

  • 写时序

写时序包括写 0 时序和写 1 时序。 所有写时序至少需要 60us, 且在 2 次独立的写时序之间至少需要 1us 的恢复时间, 两种写时序均起始于主机拉低总线。 写 1 时序: 主机输出低电平, 延时 2us, 然后释放总线, 延时 60us。 写 0时序: 主机输出低电平, 延时 60us, 然后释放总线, 延时 2us。 写时序图如下:

屏幕截图 2024 06 18 082452

  • 读时序

单总线器件仅在主机发出读时序时, 才向主机传输数据, 所以, 在主机发出读数据命令后, 必须马上产生读时序, 以便从机能够传输数据。 所有读时序至少需要 60us, 且在 2 次独立的读时序之间至少需要 1us 的恢复时间。 每个读时序都由主机发起, 至少拉低总线 1us。 主机在读时序期间必须释放总线, 并且在时序起始后的 15us 之内采样总线状态。 读时序图如下:

屏幕截图 2024 06 18 082525

典型的读时序过程为: 主机输出低电平延时 2us, 然后主机转入输入模式延时 12us, 然后读取单总线当前的电平, 然后延时 50us。

在了解了单总线时序之后, 我们来看看 DS18B20 的典型温度读取过程, DS18B20 的典型温度读取过程为: 复位→发 SKIP ROM 命令( 0XCC) →发开始转换命令( 0X44) →延时→复位→发送 SKIP ROM 命令( 0XCC) →发读存储器命令( 0XBE) →连续读出两个字节数据(即温度)→结束。

更多内容可以参考必备资料中的数据手册或者:DS18B20温度传感器使用介绍_ds18b20用于输入是用什么方式-CSDN博客

3. 硬件设计

本实验使用到硬件资源如下:

  • 动态数码管

  • DS18B20

动态数码管电路在前面章节已介绍, 这里就不再重复。 下面来看下开发板上DS18B20 模块电路, 如下图所示:

屏幕截图 2024 06 17 092445

从上图中可以看出, 该电路是独立的, 并且该接口可以支持 DS18B20 温度传感器和 DHT11 温湿度传感器。 传感器接口的单总线管脚接至 J14 端子上, 在介绍单总线的时候我们说过, 为了让单总线默认为高电平, 通常会在单总线上接上拉电阻。

4. 软件设计

本章所要实现的功能是: 插上 DS18B20 温度传感器, 数码管显示检测的温度值。

程序框架如下:

  • 编写数码管显示功能

  • 编写 DS18B20 读取温度功能

  • 编写主函数

其中数码管显示函数我们已经写过现在只需拿过来用即可,这就是模块化编程的好处,下面我们开始写DS18B20相关函数

#ifndef _ds18b20_H
#define _ds18b20_H

#include "public.h"

//管脚定义
sbit DS18B20_PORT = P3^7; // DS18B20数据口定义


//函数声明
unsigned char ds18b20_init(void);
float ds18b20_read_temperture(void);

#endif
#include "ds18b20.h"
#include "intrins.h"

// 复位DS18B20  
void ds18b20_reset(void)
{
    DS18B20_PORT = 0; // 拉低DQ
    delay_10us(75); // 拉低750us
    DS18B20_PORT = 1; // DQ=1
    delay_10us(2); // 20US
}

// 检测DS18B20是否存在,1:未检测到DS18B20的存在,0:存在
unsigned char ds18b20_check(void)
{
    unsigned char time_temp = 0;

    while(DS18B20_PORT && time_temp < 20) // 等待DQ为低电平
    {
        time_temp++;
        delay_10us(1);    
    }

    if(time_temp >= 20)
        return 1; // 如果超时则强制返回1
    else 
        time_temp = 0;
    while((!DS18B20_PORT) && time_temp < 20) // 等待DQ为高电平
    {
        time_temp++;
        delay_10us(1);
    }
    if(time_temp>=20)
    {
        return 1; // 如果超时则强制返回1
    }
    return 0;
}

// 从DS18B20读取一个位
unsigned char ds18b20_read_bit(void)
{
    unsigned char 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;
} 

// ds18b20读取一个字节
unsigned char ds18b20_read_byte(void)
{
    unsigned char i = 0;
    unsigned char dat = 0;
    unsigned char temp = 0;

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

// 写一个字节到DS18B20
void ds18b20_write_byte(unsigned char dat)
{
    unsigned char i=0;
    unsigned char 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_();    
        }    
    }    
}

// 开始温度转换
void ds18b20_start(void)
{
    ds18b20_reset(); // 复位
    ds18b20_check(); // 检查DS18B20
    ds18b20_write_byte(0xcc); // SKIP ROM
    ds18b20_write_byte(0x44); // 转换命令    
}

// 初始化DS18B20的IO口 DQ 同时检测DS的存在 
unsigned char ds18b20_init(void)
{
    ds18b20_reset();

    return ds18b20_check();    
}

// 从ds18b20得到温度值
float ds18b20_read_temperture(void)
{
    float temp;
    unsigned char dath = 0;
    unsigned char datl = 0;
    unsigned int value = 0;

    ds18b20_start(); // 开始转换
    ds18b20_reset(); // 复位
    ds18b20_check();    
    ds18b20_write_byte(0xcc); // SKIP ROM
    ds18b20_write_byte(0xbe); // 读存储器

    datl = ds18b20_read_byte(); // 低字节
    dath = ds18b20_read_byte(); // 高字节
    value=(dath << 8) + datl; // 合并为16位数据

    if((value&0xf800) == 0xf800) // 判断符号位,负温度
    {
        value=(~value) + 1; // 数据取反再加1
        temp=value * (-0.0625); // 乘以精度    
    }
    else //正温度
    {
        temp = value*0.0625;    
    }

    return temp;
}

5. 小结

代码难度明显加强,但是我们也有详细的注释应对,核心逻辑我们已经介绍过无非是:复位→发 SKIP ROM 命令( 0XCC) →发开始转换命令( 0X44) →延时→复位→发送 SKIP ROM 命令( 0XCC) →发读存储器命令( 0XBE) →连续读出两个字节数据(即温度)→结束。

在主函数里先初始化 DS18B20, 在 while循环中间隔一定时间读取温度数据, 并将温度值保留小数点后一位, 然后将读取的温度数据转换为数码管可显示的段码, 最后调用数码管显示函数。

屏幕截图 2024 06 18 090637

屏幕截图 2024 06 18 090649

屏幕截图 2024 06 18 090701

屏幕截图 2024 06 18 090714屏幕截图 2024 06 18 090722


2024.7.21 第一次修订

2024.8.22 第二次修订,后期不再维护

posted @ 2024-08-22 09:32  hazy1k  阅读(11)  评论(0编辑  收藏  举报