搜索
bottom↓
回复: 0
打印 上一主题 下一主题

《DNESP32S3使用指南-IDF版_V1.6》第二十九章 DS18B20实验

[复制链接]

出0入234汤圆

跳转到指定楼层
1
发表于 2024-7-25 12:19:39 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

1)实验平台:正点原子ESP32S3开发板
2)购买链接:https://detail.tmall.com/item.htm?id=768499342659
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-347618-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子手把手教你学ESP32S3快速入门视频教程:https://www.bilibili.com/video/BV1sH4y1W7Tc
6)正点原子FPGA交流群:132780729



第二十九章 DS18B20实验


       本章,我们将介绍ESP32-S3如何读取外部温度传感器的温度,来得到较为准确的环境温度。我们将学习单总线技术,通过它来实现ESP32-S3和外部温度传感器DS18B20的通信,并把从温度传感器得到的温度显示在LCD上。
本章分为如下几个小节:
       29.1 DS18B20简介
       29.2 硬件设计
       29.3 程序设计
       29.4 下载验证

       29.1 DS18B20介绍

       29.1.1 DS18B20简介
       DS18B20是由DALLAS半导体公司推出的一种“单总线”接口的温度传感器,实物图如下图所示。

图29.1.1.1 DS18B20实物图

       与传统的热敏电阻等测温元件相比,它是一种新型的体积小、适用电压宽、与微处理器接口简单的数字化温度传感器。单总线结构具有简洁且经济的特点,可使用户轻松地组建传感器网络,从而为测量系统的构建引入全新的概念,测试温度范围为-55~+125℃,精度为±0.5℃。现场温度直接以单总线的数字方式传输,大大提高了系统的抗干扰性。它能直接读出被测温度,并且可根据实际要求通过简单的编程实现9~12位的数字值读数方式。它工作在3~5.5V的电压范围,采用多种封装形式,从而使系统设置灵活、方便,设定分辨率以及用户设定的报警温度存储在EEPROM中,掉电后依然保存。其内部结构如下图所示。

图29.1.1.1 DS18B20内部结构图

       ROM中的64位序列号是出厂前被标记好的,它可以看作使该DS18B20的地址序列码,每个DS18B20的64位序列号均不相同。64位ROM的排列是:前8位是产品家族码,接着48位是DS18B20的序列号,最后8位是前面56位的循环冗余校验码(CRC=X8+X5+X4+1)。ROM作用是使每一个DS18B20都各不相同,这样设计可以允许一根总线上挂载多个DS18B20模块同时工作且不会引起冲突。

       29.1.2 DS18B20时序介绍
       所有单总线器件要求采用严格的信号时序,以保证数据的完整性。DS18B20共有6种信号类型:复位脉冲、应答脉冲、写0、写1、读0和读1。所有这些信号,除了应答脉冲以外,都是由主机发出同步信号。并且发送所有的命令和数据都是字节的低位在前。这里我们简单介绍这几个信号的时序。

       1,复位脉冲和应答脉冲

图29.1.2.1 复位脉冲和应答脉冲时序图

       单总线上的所有通信都是以初始化序列开始。主机输出低电平,保持低电平时间至少要在480us,以产生复位脉冲。接着主机释放总线,4.7K的上拉电阻将单总线拉高,延时时间要在15~60us,并进入接收模式(Rx)。接着DS18B20拉低总线60~240us,以产生低电平应答脉冲。

       2,写时序

图29.1.2.2 写时序图

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

       3,读时序

图29.1.2.3 读时序图

       单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令后,必须马上产生读时序,以便从机能够传输数据。所有读时序至少需要60us,且在2次独立的读时序之间至少需要1us的恢复时间。每个读时序都由主机发起,至少拉低总线1us。主机在读时序期间必须释放总线,并且在时序起始后的15us之内采样总线状态。典型的读时序过程为:主机输出低电平延时2us,然后主机转入输入模式延时12us,然后读取单总线当前的电平,然后延时50us。
       在了解单总线时序之后,我们来看一下DS18B20的典型温度读取过程,DS18B20的典型温度读取过程为:复位→发SKIP ROM(0xCC)→发开始转换命令(0x44)→延时→复位→发送SKIP ROM命令(0xCC)→发送存储器命令(0xBE)→连续读取两个字节数据(即温度)→结束。
       DS18B20的简介,我们就介绍到这里,关于该传感器的详细说明,请大家参考其数据手册。

       29.2 硬件设计

       29.2.1 例程功能
       DS18B20每隔100ms左右读取一次数据,并把温度显示在LCD上。LED闪烁用于提示程序正在运行。

       29.2.2 硬件资源

       1.LED灯
              LED-IO1

       2.USART0
              U0TXD-IO43
              U0RXD-IO44

       3.XL9555
              IIC_SDA-IO41
              IIC_SCL-IO42

       4.SPILCD
              CS-IO21
              SCK-IO12
              SDA-IO11
              DC-IO40(在P5端口,使用跳线帽将IO_SET和LCD_DC相连)
              PWR- IO1_3(XL9555)
              RST- IO1_2(XL9555)

       5.DS18B20
              1WIRE_DQ-IO0(在P5端口,使用跳线帽将IWIRE_DQ和IO0相连)

       29.2.3 原理图
       DS18B20原理图,如下图所示。

图29.2.3.1 DS18B20原理图

       从上图可以看出,1WIRE_DQ并没有直接跟ESP32-S3的IO0相连,而是需要通过跳线帽进行连接。1WIRE_DQ和IO0连接图如下图所示。

图29.2.3.2 WIRE_DQ和IO0连接图

图29.2.3.3 DS18B20与开发板连接的位置

       29.3 程序设计

       29.3.1 程序流程图
       程序流程图能帮助我们更好的理解一个工程的功能和实现的过程,对学习和设计工程有很好的主导作用。下面看看本实验的程序流程图:

图29.3.1.1 DS18B20实验程序流程图

       29.3.2 DS18B20函数解析
       这一章节除了涉及到GPIO的API函数,便没有再涉及到其他API函数。因此,有关GPIO的API函数介绍,请读者回顾此前的第十章的内容。接下来,笔者将直接介绍DS18B20的驱动代码。

       29.3.3 DS18B20驱动解析
       在IDF版19_ds18b20例程中,作者在19_ds18b20\components\BSP路径下新增了一个DS18B20文件夹,分别用于存放ds18b20.c、ds18b20.h这两个文件。其中,ds18b20.h文件负责声明DS18B20相关的函数和变量,而ds18b20.c文件则实现了DS18B20的驱动代码。下面,我们将详细解析这两个文件的实现内容。

       1,ds18b20.h文件
       由于数据线会存在输入输出模式的切换以及高低电平的设置,所以为DS18B20_DQ_PIN做了相关宏函数供单总线时序函数调用。
  1. #define DS18B20_DQ_GPIO_PIN       GPIO_NUM_0

  2. /* DS18B20引脚高低电平枚举 */
  3. typedef enum
  4. {
  5.     DS18B20_PIN_RESET = 0u,
  6.     DS18B20_PIN_SET
  7. }DS18B20_GPIO_PinState;

  8. /* IO操作 */
  9. #define DS18B20_DQ_IN     gpio_get_level(DS18B20_DQ_GPIO_PIN)   /* 数据端口输入 */

  10. /* DS18B20端口定义 */
  11. #define DS18B20_DQ_OUT(x) do{ x ?                                              \
  12.                        gpio_set_level(DS18B20_DQ_GPIO_PIN,DS18B20_PIN_SET) :   \
  13.                        gpio_set_level(DS18B20_DQ_GPIO_PIN, DS18B20_PIN_RESET); \
  14.                        }while(0)
复制代码
       gpio_get_level()与gpio_set_level()的介绍在GPIO章节已经介绍过了,在此不做赘述,请参照10.3.2小节的内容。
       DS18B20_DQ_OUT(x)宏函数的作用是设置GPIO_NUM_0的电平状态,x为0表示低电平,x为1表示高电平。
       DS18B20_DQ_IN宏函数的作用是获取GPIO_NUM_0的电平状态,返回值为0表示低电平,返回值为1表示高电平。

       2,ds18b20.c文件
  1. /**
  2. * @brief       初始化DS18B20
  3. * @param       无
  4. * @retval      0, 正常
  5. *              1, 不存在/不正常
  6. */
  7. uint8_t ds18b20_init(void)
  8. {
  9.     gpio_config_t gpio_init_struct;

  10.     gpio_init_struct.intr_type = GPIO_INTR_DISABLE;             /* 失能引脚中断 */
  11.     gpio_init_struct.mode = GPIO_MODE_INPUT_OUTPUT_OD;        /* 开漏模式的输入和输出 */
  12.     gpio_init_struct.pull_up_en = GPIO_PULLUP_ENABLE;                /* 使能上拉 */
  13. gpio_init_struct.pull_down_en = GPIO_PULLDOWN_DISABLE;        /* 失能下拉 */
  14. /* 设置的引脚的位掩码 */
  15.     gpio_init_struct.pin_bit_mask = 1ull << DS18B20_DQ_GPIO_PIN;
  16.     gpio_config(&gpio_init_struct);                        /* 配置DS18B20引脚 */

  17.     ds18b20_reset();
  18.     return ds18b20_check();
  19. }
复制代码
       在DS18B20初始化函数中,通过调用复位函数和自检函数,可以知道DS18B20是否正常。
       下面介绍一下在前面提及的几个信号类型。
  1. /**
  2. * @brief          复位DS18B20
  3. * @param          无
  4. * @retval         无
  5. */
  6. static void ds18b20_reset(void)
  7. {
  8.     DS18B20_DQ_OUT(0);                     /* 拉低DQ,复位 */
  9.     esp_rom_delay_us(750);                /* 拉低750us */
  10.     DS18B20_DQ_OUT(1);                /* DQ=1, 释放复位 */
  11.     esp_rom_delay_us(15);            /* 延迟15US */
  12. }

  13. /**
  14. * @brief         等待DS18B20的回应
  15. * @param         无
  16. * @retval         0, DS18B20正常
  17. *               1, DS18B20异常/不存在
  18. */
  19. uint8_t ds18b20_check(void)
  20. {
  21.     uint8_t retry = 0;
  22.     uint8_t rval = 0;

  23.     while (DS18B20_DQ_IN && retry < 200)                    /* 等待DQ变低, 等待200us */
  24.     {
  25.         retry++;
  26.         esp_rom_delay_us(1);
  27.     }

  28.     if (retry >= 240)
  29.     {
  30.         rval = 1;
  31.     }
  32.     else
  33.     {
  34.         retry = 0;

  35.         while (!DS18B20_DQ_IN && retry < 240)                /* 等待DQ变高, 等待240us */
  36.         {
  37.             retry++;
  38.             esp_rom_delay_us(1);
  39.         }

  40.         if (retry >= 240)
  41.         {
  42.             rval = 1;
  43.         }
  44.     }

  45.     return rval;
  46. }
复制代码
       以上两个函数分别代表着前面所说的复位脉冲与应答信号,大家可以对比前面的时序图进行理解。由于复位脉冲比较简单,所以这里不做展开。现在看一下应答信号函数,函数主要是对于DS18B20传感器的回应信号进行检测,对此判断其是否存在。函数的实现也是依据时序图进行逻辑判断,例如当主机发送了复位信号之后,按照时序,DS18B20会拉低数据线60~240us,同时主机接收最小时间为480us,我们就依据这两个硬性条件进行判断,首先需要设置一个时限等待DS18B20响应,后面也设置一个时限等待DS18B20释放数据线拉高,满足这两个条件即DS18B20成功响应。
       下面介绍的是写函数,其定义如下:
  1. /**
  2. * @brief          写一个字节到DS18B20
  3. * @param          data: 要写入的字节
  4. * @retval         无
  5. */
  6. static void ds18b20_write_byte(uint8_t data)
  7. {
  8.     uint8_t j;

  9.     for (j = 1; j <= 8; j++)
  10.     {
  11.         if (data & 0x01)
  12.         {
  13.             DS18B20_DQ_OUT(0);  
  14.             esp_rom_delay_us(2);
  15.             DS18B20_DQ_OUT(1);
  16.             esp_rom_delay_us(60);
  17.         }
  18.         else
  19.         {
  20.             DS18B20_DQ_OUT(0);  
  21.             esp_rom_delay_us(60);
  22.             DS18B20_DQ_OUT(1);
  23.             esp_rom_delay_us(2);
  24.         }

  25.         data >>= 1;            
  26.     }
  27. }
复制代码
       通过形参决定是写1还是写0,按照前面对写时序的分析,我们可以很清晰知道写函数的逻辑处理。
       有写函数肯定就有读函数,下面看一下读函数:
  1. /**
  2. * @brief   从DS18B20读取一个位
  3. * @param          无
  4. * @retval         读取到的位值: 0 / 1
  5. */
  6. static uint8_t ds18b20_read_bit(void)
  7. {
  8.     uint8_t data = 0;

  9.     DS18B20_DQ_OUT(0);
  10.     esp_rom_delay_us(2);
  11.     DS18B20_DQ_OUT(1);
  12.     esp_rom_delay_us(12);

  13.     if (DS18B20_DQ_IN)
  14.     {
  15.         data = 1;
  16.     }

  17.     esp_rom_delay_us(50);
  18.     return data;
  19. }

  20. /**
  21. * @brief          从DS18B20读取一个字节
  22. * @param   无
  23. * @retval  读到的数据
  24. */
  25. static uint8_t ds18b20_read_byte(void)
  26. {
  27.     uint8_t i, b, data = 0;

  28.     for (i = 0; i < 8; i++)
  29.     {
  30.         b = ds18b20_read_bit();         /* DS18B20先输出低位数据 ,高位数据后输出 */
  31.         
  32.         data |= b << i;                 /* 填充data的每一位 */
  33.     }

  34.     return data;
  35. }
复制代码
       在这里,ds18b20_read_bit函数从DS18B20处读取1位数据,在前面已经对读时序也进行了详细的分析,所以这里也不展开解释了。
       下面介绍读取温度函数,其定义如下:
  1. /**
  2. * @brief          从ds18b20得到温度值(精度:0.1C)
  3. * @param   无
  4. * @retval  温度值 (-550~1250)
  5. * @note         返回的温度值放大了10倍.
  6. *          实际使用的时候,要除以10才是实际温度.
  7. */
  8. short ds18b20_get_temperature(void)
  9. {
  10.     uint8_t flag = 1;                   /* 默认温度为正数 */
  11.     uint8_t TL, TH;
  12.     short temp;

  13.     ds18b20_start();                    /*  ds1820 start convert */
  14.     ds18b20_reset();
  15.     ds18b20_check();
  16.     ds18b20_write_byte(0xcc);                 /*  skip rom */
  17.     ds18b20_write_byte(0xbe);           /*  convert */
  18.     TL = ds18b20_read_byte();           /*  LSB */
  19.     TH = ds18b20_read_byte();           /*  MSB */

  20.     if (TH > 7)
  21.     { /* 温度为负,查看DS18B20的温度表示法与计算机存储正负数据的原理一致:
  22.         正数补码为寄存器存储的数据自身,负数补码为寄存器存储值按位取反后+1
  23.         所以我们直接取它实际的负数部分,但负数的补码为取反后加一,但考虑到
  24.         低位可能+1后有进位和代码冗余,我们这里先暂时没有作+1的处理,这里需要留意  */
  25.         TH = ~TH;
  26.         TL = ~TL;
  27.         flag = 0;                /* 温度为负 */
  28.     }

  29.     temp = TH;              /* 获得高八位 */
  30.     temp <<= 8;
  31.     temp += TL;             /* 获得底八位 */

  32.     /* 转换成实际温度 */
  33.     if (flag == 0)
  34.     {   /* 将温度转换成负温度,这里的+1参考前面的说明 */
  35.         temp = (double)(temp + 1) * 0.625;
  36.         temp = -temp;   
  37.     }
  38.     else
  39.     {
  40.         temp = (double)temp * 0.625;               
  41.     }

  42.     return temp;
  43. }
复制代码
       在这里简单介绍一下上面用到的RAM指令:
       跳过ROM(0xCC),该指令只适合总线只有一个节点,它通过允许总线上的主机不提供64位ROM序列号而直接访问RAM,节省了操作时间。
       温度转换(0x44),启动DS18B20进行温度转换,结果存入内部RAM。
       读暂存器(0xBE),读暂存器9个字节内容,该指令从RAM的第一个字节(字节0)开始读取,直到九个字节(字节8,CRC值)被读出为止。如果不需要读出所有字节的内容,那么主机可以在任何时候发出复位信号以中止读操作。

       29.3.4 CMakeLists.txt文件
       打开本实验BSP下的CMakeLists.txt文件,其内容如下所示:
  1. set(src_dirs
  2.             DS18B20
  3.             IIC
  4.             LCD
  5.             LED
  6.             SPI
  7.             XL9555)

  8. set(include_dirs
  9.             DS18B20
  10.             IIC
  11.             LCD
  12.             LED
  13.             SPI
  14.             XL9555)

  15. set(requires
  16.             driver)

  17. idf_component_register(SRC_DIRS ${src_dirs}
  18. INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})

  19. component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)
复制代码
       上述的红色IIC、XLP555驱动需要由开发者自行添加,以确保IIC_EXIO驱动能够顺利集成到构建系统中。这一步骤是必不可少的,它确保了IIC_EXIO驱动的正确性和可用性,为后续的开发工作提供了坚实的基础。

       29.3.5 实验应用代码
       打开main/main.c文件,该文件定义了工程入口函数,名为app_main。该函数代码如下。
  1. i2c_obj_t i2c0_master;

  2. /**
  3. * @brief       程序入口
  4. * @param       无
  5. * @retval      无
  6. */
  7. void app_main(void)
  8. {
  9.     uint8_t err;
  10.     uint8_t t = 0;
  11.     short temperature;
  12.     esp_err_t ret;
  13.    
  14.     /* 初始化NVS */
  15.     ret = nvs_flash_init();

  16.     if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
  17.     {
  18.         ESP_ERROR_CHECK(nvs_flash_erase());
  19.         ret = nvs_flash_init();
  20.     }

  21.     /* 初始化LED */
  22.     led_init();
  23.    
  24.     /* 初始化IIC0 */
  25.     i2c0_master = iic_init(I2C_NUM_0);
  26.    
  27.     /* 初始化SPI */
  28.     spi2_init();
  29.    
  30.     /* 初始化XL9555 */
  31.     xl9555_init(i2c0_master);
  32.    
  33.     /* 初始化LCD */
  34.     lcd_init();

  35.     lcd_show_string(30, 50, 200, 16, 16, "ESP32", RED);
  36.     lcd_show_string(30, 70, 200, 16, 16, "DS18B20 TEST", RED);
  37.     lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);

  38.     /* 初始化DS18B20数字温度传感器 */
  39.     err = ds18b20_init();

  40.     if (err != 0)
  41.     {
  42.         while (1)
  43.         {
  44.             lcd_show_string(30, 110, 200, 16, 16, "DS18B20 Error", RED);
  45.             vTaskDelay(200);
  46.             lcd_fill(30, 110, 239, 130 + 16, WHITE);
  47.             vTaskDelay(200);
  48.         }
  49.     }
  50.     lcd_show_string(30, 110, 200, 16, 16, "DS18B20 OK", RED);
  51.     lcd_show_string(30, 130, 200, 16, 16, "Temp:   . C", BLUE);
  52.    
  53.     while (1)
  54.     {
  55.         /* 每100ms读取一次 */
  56.         if (t % 10 == 0)
  57.         {
  58.             temperature = ds18b20_get_temperature();

  59.             if (temperature < 0)
  60.             {
  61.                 /* 显示负号 */
  62.                 lcd_show_char(30 + 40, 130, '-', 16, 0, BLUE);
  63.                
  64.                 /* 转为正数 */
  65.                 temperature = -temperature;
  66.             }
  67.             else
  68.             {
  69.                 /* 去掉负号 */
  70.                 lcd_show_char(30 + 40, 130, ' ', 16, 0, BLUE);
  71.             }
  72.             
  73.             /* 显示正数部分 */
  74.             lcd_show_num(30 + 40 + 8, 130, temperature / 10, 2, 16, BLUE);
  75.             
  76.             /* 显示小数部分 */
  77.             lcd_show_num(30 + 40 + 32, 130, temperature % 10, 1, 16, BLUE);
  78.         }

  79.         vTaskDelay(10);
  80.         t++;

  81.         if (t == 20)
  82.         {
  83.             t = 0;
  84.             
  85.             /* LED闪烁 */
  86.             LED_TOGGLE();
  87.         }
  88. }
复制代码
       在main函数中,完成一系列外设初始化之后,调用ds18b20_init函数完成DS18B20初始化,LCD显示实验信息。
       在while循环中,间隔100毫秒调用ds18b20_get_temperature函数获取DS18B20传感器的温度数据,然后在LCD进行显示。LED灯每隔200毫秒状态翻转,实现闪烁效果。

       29.4 下载验证
       假定DS18B20传感器已经接上去正确的位置,将程序下载到开发板后,可以看到LED0不停的闪烁,提示程序已经在运行了。LCD显示当前的温度值的内容如下图所示:

图29.4.1 DS18B20实验测试图

       该程序还可以读取并显示负温度值,具备零下温度条件可以测试一下。

阿莫论坛20周年了!感谢大家的支持与爱护!!

该献的血还是要献的。你不献他不献。难道让我去献? --- 出自坛友:lovejp1981
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-9-1 06:40

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表