前面我们学习 DHT11 的时候提到了 DS18B20,它有很宽的测温范围,-55°C ~ 125°C。那么本次我们就来详细介绍一下 DS18B20。

DS18B20 是一种单总线数字温度传感器,它被广泛应用于各种领域,例如气象监测、室内温度控制、工业自动化等。DS18B20 采用了单总线接口(One-Wire Interface),这意味着可以通过单个引脚与微控制器通信,大大简化了连接和布线。

在本文中,我们将介绍 DS18B20 的参数、工作原理以及使用案例,帮助您深入了解和应用 DS18B20。

1. 源码下载及前置阅读

本文首发 :https://www.lxlinux.net/e/ ,欢迎关注!

本文所涉及的源码及安装包如下(由于平台限制,请点击以下链接阅读原文下载):

https://www.lxlinux.net/e/stm32/ds18b20-tutorial.html

如果你是嵌入式开发小白,那么建议你先读读下面几篇文章。

往期教程,有兴趣的小伙伴可以看看。

作者简介
大家好,我是良许,博客里所有的文章皆为我的原创。
下面是我的一些个人介绍,欢迎交个朋友:
· 211工科硕士,国家奖学金获得者;
· 深耕嵌入式11年,前世界500强外企高级嵌入式工程师;
· 书籍《速学Linux作者》,机械工业出版社专家委员会成员;
· 全网60W粉丝,博客分享大量原创成体系文章,全网阅读量累计超4000万;
· 靠自媒体连续年入百万,靠自己买房买车。

我本科及硕士都是学机械,通过自学成功进入世界500强外企。我已经将自己的学习经验写成了一本电子书,超千人通过此书学习并转行成功。现在将这本电子书免费分享给大家,希望对你们有帮助:

电子书链接:https://www.lxlinux.net/1024.html

2. DS18B20介绍

2.1 DS18B20型号介绍

DS18B20 是测温传感器的型号,我们经常把使用这个型号的测温模块也统称作 DS18B20。DS18B20 具有精度高、体积小、接线简单、抗干扰能力强的特点。适用于温度控制、工业系统、温度计以及任何热感测系统。

一些关于 DS18B20 的冷知识,有兴趣的小伙伴可以在文章头的手册中查看更多细节:

  1. 报警功能:DS18B20 数字温度计提供 9-12 位摄氏温度测量而且有一个由高低电平触发的可编程的不因电源消失而改变的报警功能。这里的报警是指 DS18B20 内部会置位一个报警标识,而不是闪灯或者蜂鸣器哦。
  2. 独特序列号:每个 DS18B20 都有一个独特的 64 位序列号,从而允许多只 DS18B20 同时连在一根单线总线上;因此,很简单就可以用一个微控制器去控制很多覆盖在一大片区域的 DS18B20。这一特性在 HVAC 环境控制、探测建筑物、仪器或机器的温度以及过程监测和控制等方面非常有用。
  3. 无需外部供电:DS18B20的另一个功能是可以在没有外部电源供电的情况下工作。当总线处于高电平状态,DQ与上拉电阻连接通过单总线对器件供电。同时处于高电平状态的总线信号对内部电容(Cpp)充电,在总线处于低电平状态时,该电容提供能量给器件。这种提供能量的形式被称为“寄生电源”。

2.2 DS18B20参数及引脚介绍

DS18B20参数:

  • 工作电压:DC 3V ~ 5.5V
  • 测温范围:-55℃ ~ +125℃(-67°F ~ +257°F)
  • 精度:±2℃(-55°C ~ +125°C),±0.5℃(-10°C ~ +85°C)
  • PCB 板尺寸:21mm × 10mm

参考接线如下:

DS18B20STM32
VCC3.3/5V
DQ任意一个GPIO口
GNDGND

3. DS18B20工作原理

3.1 高速暂存器

  • 字节 0~1 是温度存储器,只读,用来存储转换好的温度。第0个字节存(LSB)储温度低8位,第1个字节(MSB)存储温度高8位。
  • 字节 2~3 是用户用来设置最高报警和最低报警值(TH 和 TL)。
  • 字节 4 是配置寄存器,用来配置转换精度,可以设置为 9~12 位(默认 12 位,精度是0.0625℃)。
  • 字节 5~7 保留位,禁止写入。
  • 字节 8 CRC校验位,只读,是64位ROM中的前56位编码的校验码,由CRC发生器产生。

我们重点讲下字节 0~1,温度存储器。其他的,感兴趣的同学可以查看文章头的手册。

LSB 和 MSB 组成十六位的温度数据,具体示例如下:

温度为负时,寄存器存储值按位取反后 +1,× 精度,即为温度值。

例如:1111 1111 0101 1110 取反后为 0000 0000 1010 0001,转成十进制是161,161+1=162,162×0.0625=10.125,加上负号就是 -10.125了。

3.2 指令

3.2.1 ROM指令

总线控制器在发起一条 DS18B20 功能指令之前必须先发出一条 ROM 指令。

ROM指令(十六进制)意义说明
F0搜索 ROM当系统上电初始化的时候,总线控制器必须通过识别总线上所有 ROM 片序列码去得到从机的数目和型号。如果总线上只有一只从机,那么可以用读取ROM 指令代替。
33读取 ROM只有在总线上存在单只 DS18B20 的时候才能使用这条命令。该命令允许总线控制器在不使用搜索 ROM 指令的情况下读取从机的 64 位片序列码。如果总线上有不止一只从机,当所有从机试图同时传送信号时就会发生数据冲突。
55匹配 ROM匹配 ROM 指令,后跟 64 位 ROM 编码序列,让总线控制器在多点总线上定位一只特定的 DS18B20。只有和 64 位 ROM 片序列码完全匹配的 DS18B20 才能响应随后的存储器操作指令;不匹配将等待复位脉冲。
CC跳过 ROM这条指令允许总线控制器不用提供 64 位 ROM 编码就使用功能指令。
EC报警搜索这条指令的流程和搜索 ROM 指令相同,但只有满足报警条件的从机才对该命令作出响应。只有在最近一次测温后遇到符合报警条件的情况,DS18B20 才会响应这条命令。

3.2.2 功能指令

功能指令(十六进制)意义说明
44温度转换产生的温度转换结果数据以 2 个字节的形式被存储在高速暂存器中,而后 DS18B20 保持等待状态。
4E写暂存器这条命令向 DS18B20 的暂存器写入数据,开始位置在 TH 寄存器,接下来写入 TL 寄存器,最后写入配置寄存器。
BE读暂存器这条命令读取暂存器的内容。读取将从字节 0 开始,一直进行下去,直到第 9 字节(字节 8,CRC)读完。
48拷贝暂存器这条命令把 TH,TL 和配置寄存器(第 2、3、4 字节)的内容拷贝到 EEPROM 中。
B8召回 EEPROM这条命令把报警触发器的值(TH 和 TL)以及配置数据从 EEPROM 拷回暂存器。
B4读电源模式总线控制器在这条命令发给 DS18B20 后发出读时序,若是寄生电源模式,DS18B20将拉低总线;若是外部电源模式,DS18B20 将拉高总线。

3.3 DS18B20工作时序

DS18B20 需要严格的单总线协议以确保数据的完整性。协议包括集中单总线信号类型:复位脉冲、存在脉冲、写 0、写 1、读 0 和读 1。所有这些信号,除存在脉冲外,都是由总线控制器(单片机)发出的。

3.3.1 初始化

总线控制器拉低总线(DQ)并保持 480us 以发出一个复位脉冲,然后释放总线,进入接收状态。单总线由 5K 上拉电阻拉到高电平。当 DS18B20 探测到 I/O 引脚上的上升沿后,等待 15-60us 后发出一个由 60-240us 低电平信号构成的存在脉冲。

3.3.2 写时序

所有写时序必须最少持续 60us,包括两个写周期之间至少 1us 的恢复时间。当总线控制器把数据线从逻辑高电平拉到低电平的时候,写时序开始。

总线控制器要生产一个写时序,必须把数据线拉到低电平然后释放,在写时序开始后的 15us 释放总线。当总线被释放的时候,5K 的上拉电阻将拉高总线。总控制器要生成一个写 0 时序,必须把数据线拉到低电平并持续保持(至少 60us)。

总线控制器初始化写时序后,DS18B20 在一个 15us 到 60us 的窗口内对 I/O 线采样。如果线上是高电平,就是写 1。如果线上是低电平,就是写 0。

3.3.3 读时序

所有读时序必须最少 60us,包括两个读周期间至少 1us 的恢复时间。当总线控制器把数据线从高电平拉到低电平时,读时序开始,数据线必须至少保持 1us,然后总线被释放。

在总线控制器发出读时序后,DS18B20 通过拉高或拉低总线上来传输 1 或 0。当传输逻辑 0 结束后,总线将被释放,通过上拉电阻回到上升沿状态。从 DS18B20 输出的数据在读时序的下降沿出现后 15us 内有效。因此,总线控制器在读时序开始后必须停止把 I/O 脚驱动为低电平 15us,以读取I/O 脚状态。

4. 编程实战

实战目标:串口打印温度数据。

4.1 硬件接线

本教程使用的硬件如下:

  • 单片机:STM32F103C8T6
  • 温感模块:DS18B20
  • 串口:USB 转 TTL
  • 烧录器:ST-LINK V2
DHT11STM32USB 转 TTL
VCC3.3/5V
DQA11
GNDG
A10TX
A9RX
GGND

烧录的时候接线如下表,如果不会烧录的话可以看我之前的文章【STM32下载程序的五种方法】。

ST-Link V2STM32
SWCLKSWCLK
SWDIOSWDIO
GNDGND
3.3V3V3

接好如下图。开发板使用的是我们自绘的板子。大家也可以用自己的板子,只要是 STM32F103C8T6 主控芯片就行。

4.2 DS18B20初始化

因为 DS18B20 是单总线,按照常规思维,DS18B20 的 DQ 引脚模式是要在输入和推挽输出之间切换的。但是这样太麻烦了,所以我们直接将 DS18B20 的 DQ 引脚模式设置为开漏输出 + 上拉,这样就不用再设置 IO 方向了。开漏输出的时候(=1) 也可以读取外部信号的高低电平。

剩下的就是按照初始化时序编写代码。

void DS18B20_Rst(void)
{
    DS18B20_DQ_OUT_LOW;         //拉低总线并延时750us
    delay_us(750);

    DS18B20_DQ_OUT_HIGH;        //释放总线为高电平并延时等待15~60us
    delay_us(15);
}

uint8_t DS18B20_Check(void)
{
    uint8_t retry = 0;                      //脉冲持续时间

    while(DS18B20_DQ_IN && retry < 200){    //等待DQ变低, 等待200us
        retry++;
        delay_us(1);
    }

    if(retry >= 200)
        return 1;
    else
        retry = 0;


    while(!DS18B20_DQ_IN && retry < 240){    //判断DS18B20是否释放总线
        retry++;
        delay_us(1);
    }
    
    if(retry >= 240)
        return 2;

    return 0;
}

uint8_t DS18B20_Init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    
    DS18B20_DQ_GPIO_CLK_ENABLE();   /* 开启DQ引脚时钟 */

    gpio_init_struct.Pin = DS18B20_DQ_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_OD;            /* 开漏输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* 高速 */
    HAL_GPIO_Init(DS18B20_DQ_GPIO_PORT, &gpio_init_struct); /* 初始化DS18B20_DQ引脚 */

    DS18B20_Rst();
    return DS18B20_Check();
}

4.3 写/读一个字节

按照时序写就可以了,不多讲。

void DS18B20_Write_Byte(uint8_t data)
{
    uint8_t i;
    uint8_t databit;

    for(i = 0; i < 8; i++){
        databit = data & 0x01;       //取数据最低位
        data = data >> 1;            //右移一位
        if(databit){                 //当前位写1
            DS18B20_DQ_OUT_LOW;
            delay_us(2);
            DS18B20_DQ_OUT_HIGH;
            delay_us(60);
        }else{                       //当前位写0
            DS18B20_DQ_OUT_LOW;
            delay_us(60);
            DS18B20_DQ_OUT_HIGH;
            delay_us(2);
        }
    }
}

uint8_t DS18B20_Read_Bit(void){
    uint8_t data;

    DS18B20_DQ_OUT_LOW;     //至少拉低1us,开始读时序
    delay_us(2);
    DS18B20_DQ_OUT_HIGH;
    delay_us(12);

    if(DS18B20_DQ_IN)
        data = 1;           //读到1
    else
        data = 0;           //读到0

    delay_us(50);
    return data;
}

uint8_t DS18B20_Read_Byte(void){
    uint8_t i, b, data;
    data = 0;
    for(i = 0; i < 8; i++){
        b = DS18B20_Read_Bit();
        data |= b << i;         /* 填充data的每一位 */ 
     }
    return data;
}

4.4 获取温度

温度转换命令是 0x44,而在发起一条 DS18B20 功能指令之前必须先发出一条 ROM 指令,并且我们只有一个 DS18B20,所以我们先跳过 ROM,再发起温度转换指令。

如果温度为负,需要取反后+1,因为考虑到低位可能+1后有进位和代码冗余,所以我们放到最后在 × 精度前 +1。

void DS18B20_Start(void){
    DS18B20_Rst();
    DS18B20_Check();
    DS18B20_Write_Byte(0xcc);       //跳过ROM
    DS18B20_Write_Byte(0x44);       //温度转换指令
}

uint16_t DS18B20_Get_Temperature(void){
    uint8_t temp;
    uint8_t TL,TH;
    uint16_t temperature;

    DS18B20_Start();                //开始温度转换
    DS18B20_Rst();                  //复位DS18B20
    DS18B20_Check();                //检测DS18B20是否存在
    DS18B20_Write_Byte(0xcc);       //跳过ROM
    DS18B20_Write_Byte(0xbe);       //读暂存器
    TL = DS18B20_Read_Byte();       //低八位 LSB
    TH = DS18B20_Read_Byte();       //高八位 MSB

    if(TH>0x70){                    //判断温度值是否为负数
        TH = ~TH;
        TL = ~TL;
        temp = 0;
    }else
        temp = 1;

    temperature = TH;               //获得高八位
    temperature <<= 8;
    temperature += TL;              //获得低八位
    
    if (temp == 0)                  //温度为负需要加一,默认精度是0.0625
    {
        temperature = (float)(temperature+1) * 0.625;
        temperature = -temperature;
    }
    else
    {
        temperature = (float)temperature * 0.625;
    }
}

4.5 主函数

主函数如下:

int main(void)
{
    float temperature;
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    uart1_init(115200);                 /* 串口初始化,波特率115200 */

    printf("DS1820实验:\r\n");

    while(DS18B20_Init())               //初始化DS18B20,同时检测DS18B20的存在
    {
        printf("DS18B20 初始化失败\r\n");
        delay_ms(500);
     }
        printf("DS18B20 初始化成功\r\n");

    while(1)
    {
        temperature = DS18B20_Get_Temperature();    //读取温度
        if(temperature < 0)
        printf("当前温度: -%.2f ℃\r\n",temperature/10);
        else
        printf("当前温度: %.2f ℃\r\n",temperature/10);
        delay_s(2);
    }
}

4.6 最终效果

效果如下,我用手捂了一会 DS18B20 再放开,可以看到温度上升了一会又下降了。

5. 小结

通过本文的学习与实践,相信大家已经了解并掌握 DS18B20 的特性和使用,能够更好地应用于嵌入式开发。无论是构建智能家居系统还是开发物联网设备,DS18B20 都可以成为您的得力助手。感谢各位看官,peace and love!

另外,想进大厂的同学,一定要好好学算法,这是面试必备的。这里准备了一份 BAT 大佬总结的 LeetCode 刷题宝典,很多人靠它们进了大厂。

刷题 | LeetCode算法刷题神器,看完 BAT 随你挑!

有收获?希望老铁们来个三连击,给更多的人看到这篇文章

推荐阅读:

欢迎关注我的博客:良许嵌入式教程网,满满都是干货!


良许
1k 声望1.8k 粉丝