yinwuqing110 发表于 2025-6-19 21:08

【AT-START-M412测评】+ ③驱动DS18B20温度采集

本帖最后由 yinwuqing110 于 2025-6-19 21:28 编辑

         温度传感器有多种,采用IIC通讯的方式的比较多,而在IO口资源有限的单片机设计中,使用DS18B20的仍然比较常见。之所以DS18B20不会退出市场,不少教学开发板上仍保留它的一席之地,是因为它的驱动方式只需要一根线,也就是一个IO口即能完成数据的精准采集,而且灵敏度不输IIC通讯方式的温敏传感器。“一线式”通讯方式,资源占用少,但驱动起来其实比IIC通讯要求更苛刻,尤其是时序上的要求。今天,咱们使用AT-START-M412驱动DS18B20模块,温度值显示在LCD屏上,并通过串口打印相关信息。当用手握DS18B20模块给传感器加温,采集温度瞬间增加;挪开手,则采集的温度值明显下降。


          首先来认识一下DS18B20模块,实物图如下:

          由上可知,除了给模块正常上电外,只需一个DQ引脚,完成数字信号的输入输出,读写时序需要严格按照官方给定的数据手册来。
          DS18B20可以测量-55℃至+125℃的温度范围,通常采用外部供电方式,并在数据线上并联一个4.7k的上拉电阻,以增强数据的抗干扰能力。
          为了高效得驱动DS18B20,先来阅读一下数据手册。

          其中比较重要的是读写时序,该数据手册中提供了关于驱动DS18B20的读写时序图,如下图所示:

       解读控制时序:
       ①、初始化时序:与DS18B20的所有通信都由初始化时序开始,该时序包括从主设备发出的复位脉冲及从DS18B20响应的存在脉冲。当DS18B20响应复位信号的存在脉冲后,表明其在总线上并且已经准备好进行操作。
       ②、读时序:读时序包括读1时段和读0时段。主设备通过读1时段来读取DS18B20中的逻辑1,通过读0时段来读取逻辑0。每个读时段最小必须有60us的持续时间,并且独立的读时段间至少有1us的恢复时间。
       ③、写时序:写时序包括写1时段和写0时段。主设备通过写1时段向DS18B20中写入逻辑1,通过写0时段写入逻辑0。每个写时段最小必须有60us的持续时间,并且独立的写时段间至少有1us的恢复时间。
       解读读取温度流程:
       ①、对DS18B20进行复位操作。
       ②、发送开始转换指令(指令值:0x44)。
       ③、再次进行复位操作。
       ④、等待DS18B20应答。
       ⑤、发送读取温度指令。
       ⑥、读取16位的数据(有效位最大为12位)。

      经过上述了解,接下来,咱们编写相关的驱动代码,源码展示见如下。
DS18B20.h
#ifndef __DS18B20_H__
#define __DS18B20_H__

/* 包含头文件 ----------------------------------------------------------------*/
#include "at32m412_416_board.h"

/************************** DS18B20 连接引脚定义********************************/
#define DS18B20_DQ_GPIO_CLK                     CRM_GPIOB_PERIPH_CLOCK

#define DS18B20_DQ_GPIO_PORT                      GPIOB
#define DS18B20_DQ_GPIO_PIN                     GPIO_PINS_12

/************************** DS18B20 函数宏定义********************************/
#define DS18B20_DQ_0                                    gpio_bits_reset(DS18B20_DQ_GPIO_PORT,DS18B20_DQ_GPIO_PIN)
#define DS18B20_DQ_1                                    gpio_bits_set(DS18B20_DQ_GPIO_PORT,DS18B20_DQ_GPIO_PIN)
#define DS18B20_DQ_IN()                              gpio_input_data_bit_read(DS18B20_DQ_GPIO_PORT,DS18B20_DQ_GPIO_PIN)

/************************** DS18B20 函数声明 ********************************/
uint8_t DS18B20_Init(void);
void DS18B20_ReadId( uint8_t * ds18b20_id);
float DS18B20_GetTemp_SkipRom(void);
float DS18B20_GetTemp_MatchRom(uint8_t *ds18b20_id);

#endif DS18B20.c
#include "at32m412_416_board.h"
#include "at32m412_416_clock.h"

#include "DS18B20.h"

/**
* 函数功能: DS18B20 初始化函数
* 输入参数: 无
* 返 回 值: 0:初始化成功,检测到传感器,1:初始化失败
* 说    明:无
*/
uint8_t DS18B20_Init(void)
{
      DS18B20_GPIO_Config ();
      DS18B20_DQ_1;
      DS18B20_Rst();
      return DS18B20_Presence ();
}

/**
* 函数功能: 配置DS18B20用到的I/O口
* 输入参数: 无
* 返 回 值: 无
* 说    明:无
*/
static void DS18B20_GPIO_Config(void)
{               
gpio_init_type gpio_init_structure;
crm_periph_clock_enable(DS18B20_DQ_GPIO_CLK, TRUE);
                                                                                          
gpio_init_structure.gpio_pins = DS18B20_DQ_GPIO_PIN;      
gpio_init_structure.gpio_out_type= GPIO_OUTPUT_PUSH_PULL;
gpio_init_structure.gpio_mode = GPIO_MODE_OUTPUT;
gpio_init_structure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init(DS18B20_DQ_GPIO_PORT , &gpio_init_structure);
}

/**
* 函数功能: 使DS18B20-DATA引脚变为输入模式
* 输入参数: 无
* 返 回 值: 无
* 说    明:无
*/
static void DS18B20_Mode_IPU(void)
{
         gpio_init_type gpio_init_structure;

          gpio_init_structure.gpio_pins = DS18B20_DQ_GPIO_PIN;
          gpio_init_structure.gpio_pull= GPIO_PULL_NONE;      
          gpio_init_structure.gpio_mode = GPIO_MODE_INPUT;      
          gpio_init(DS18B20_DQ_GPIO_PORT, &gpio_init_structure);
}

/**
* 函数功能: 使DS18B20-DATA引脚变为输出模式
* 输入参数: 无
* 返 回 值: 无
* 说    明:无
*/
static void DS18B20_Mode_Out_PP(void)
{
         gpio_init_type gpio_init_structure;
                                                                                                         
      gpio_init_structure.gpio_pins = DS18B20_DQ_GPIO_PIN;      
      gpio_init_structure.gpio_out_type= GPIO_OUTPUT_PUSH_PULL;
      gpio_init_structure.gpio_mode = GPIO_MODE_OUTPUT;
      gpio_init_structure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
      gpio_init(DS18B20_DQ_GPIO_PORT, &gpio_init_structure);
}

/**
* 函数功能: 主机给从机发送复位脉冲
* 输入参数: 无
* 返 回 值: 无
* 说    明:无
*/
static void DS18B20_Rst(void)
{
      DS18B20_Mode_Out_PP();
      DS18B20_DQ_0;
      delay_us(750);
      DS18B20_DQ_1;
      delay_us(15);
}

/**
* 函数功能: 检测从机给主机返回的存在脉冲
* 输入参数: 无
* 返 回 值: 0:成功,1:失败
* 说    明:无
*/
static uint8_t DS18B20_Presence(void)
{
      uint8_t pulse_time = 0;
      
      DS18B20_Mode_IPU();
      while( DS18B20_DQ_IN() && pulse_time<100 )
      {
                pulse_time++;
                delay_us(1);
      }      
      if( pulse_time >=100 )
                return 1;
      else
                pulse_time = 0;
      
      while( !DS18B20_DQ_IN() && pulse_time<240 )
      {
                pulse_time++;
                delay_us(1);
      }      
      if( pulse_time >=240 )
                return 1;
      else
                return 0;
}

/**
* 函数功能: 从DS18B20读取一个bit
* 输入参数: 无
* 返 回 值: 读取到的数据
* 说    明:无
*/
static uint8_t DS18B20_ReadBit(void)
{
      uint8_t dat;
      
      DS18B20_Mode_Out_PP();
      DS18B20_DQ_0;
      delay_us(10);
      DS18B20_Mode_IPU();
      if( DS18B20_DQ_IN() == SET )
                dat = 1;
      else
                dat = 0;
      delay_us(45);
      
      return dat;
}

/**
* 函数功能: 从DS18B20读一个字节,低位先行
* 输入参数: 无
* 返 回 值: 读到的数据
* 说    明:无
*/
static uint8_t DS18B20_ReadByte(void)
{
      uint8_t i, j, dat = 0;      
      
      for(i=0; i<8; i++)
      {
                j = DS18B20_ReadBit();               
                dat = (dat) | (j<<i);
      }
      
      return dat;
}

/**
* 函数功能: 写一个字节到DS18B20,低位先行
* 输入参数: dat:待写入数据
* 返 回 值: 无
* 说    明:无
*/
static void DS18B20_WriteByte(uint8_t dat)
{
      uint8_t i, testb;
      DS18B20_Mode_Out_PP();
      
      for( i=0; i<8; i++ )
      {
                testb = dat&0x01;
                dat = dat>>1;               
                /* 写0和写1的时间至少要大于60us */
                if (testb)
                {                        
                        DS18B20_DQ_0;
                        /* 1us < 这个延时 < 15us */
                        delay_us(8);
                        
                        DS18B20_DQ_1;
                        delay_us(58);
                }               
                else
                {                        
                        DS18B20_DQ_0;
                        /* 60us < Tx 0 < 120us */
                        delay_us(70);
                        
                        DS18B20_DQ_1;                        
                        /* 1us < Trec(恢复时间) < 无限大*/
                        delay_us(2);
                }
      }
}

/**
* 函数功能: 跳过匹配 DS18B20 ROM
* 输入参数: 无
* 返 回 值: 无
* 说    明:无
*/
static void DS18B20_SkipRom ( void )
{
      DS18B20_Rst();                  
      DS18B20_Presence();               
      DS18B20_WriteByte(0XCC);      /* 跳过 ROM */      
}

/**
* 函数功能: 执行匹配 DS18B20 ROM
* 输入参数: 无
* 返 回 值: 无
* 说    明:无
*/
static void DS18B20_MatchRom ( void )
{
      DS18B20_Rst();                  
      DS18B20_Presence();               
      DS18B20_WriteByte(0X55);
}

/**
* 函数功能: 在跳过匹配 ROM 情况下获取 DS18B20 温度值
* 输入参数: 无
* 返 回 值: 温度值
* 说    明:无
*/
float DS18B20_GetTemp_SkipRom ( void )
{
      uint8_t tpmsb, tplsb;
      short s_tem;
      float f_tem;
      
      DS18B20_SkipRom ();
      DS18B20_WriteByte(0X44);      /* 开始转换 */
      
      DS18B20_SkipRom ();
      DS18B20_WriteByte(0XBE);      /* 读温度值 */
      
      tplsb = DS18B20_ReadByte();               
      tpmsb = DS18B20_ReadByte();
      
      s_tem = tpmsb<<8;
      s_tem = s_tem | tplsb;
      
      if( s_tem < 0 )            /* 负温度 */
                f_tem = (~s_tem+1) * 0.0625;      
      else
                f_tem = s_tem * 0.0625;
      
      return f_tem;         
}

/**
* 函数功能: 在匹配 ROM 情况下获取 DS18B20 温度值
* 输入参数: ds18b20_id:用于存放 DS18B20 序列号的数组的首地址
* 返 回 值: 无
* 说    明:无
*/
void DS18B20_ReadId ( uint8_t * ds18b20_id )
{
      uint8_t uc;
               
      DS18B20_WriteByte(0x33);      //读取序列号
      
      for ( uc = 0; uc < 8; uc ++ )
          ds18b20_id [ uc ] = DS18B20_ReadByte();      
}

/**
* 函数功能: 在匹配 ROM 情况下获取 DS18B20 温度值
* 输入参数: ds18b20_id:存放 DS18B20 序列号的数组的首地址
* 返 回 值: 温度值
* 说    明:无
*/
float DS18B20_GetTemp_MatchRom ( uint8_t * ds18b20_id )
{
      uint8_t tpmsb, tplsb, i;
      short s_tem;
      float f_tem;
      
      DS18B20_MatchRom ();      //匹配ROM
      
    for(i=0;i<8;i++)
                DS18B20_WriteByte ( ds18b20_id [ i ] );      
      
      DS18B20_WriteByte(0X44);      //开始转换

      DS18B20_MatchRom ();      //匹配ROM
      
      for(i=0;i<8;i++)
                DS18B20_WriteByte ( ds18b20_id [ i ] );      
      
      DS18B20_WriteByte(0XBE);      //读温度值
      
      tplsb = DS18B20_ReadByte();               
      tpmsb = DS18B20_ReadByte();
      
      s_tem = tpmsb<<8;
      s_tem = s_tem | tplsb;
      
      if( s_tem < 0 )         /* 负温度 */
                f_tem = (~s_tem+1) * 0.0625;      
      else
                f_tem = s_tem * 0.0625;
      
      return f_tem;               
}
main.c
/* includes */
#include "at32m412_416_board.h"
#include "at32m412_416_clock.h"

#include "lcd_st7735.h"
#include "DS18B20.h"

int main(void)
{
uint8_t i, DS18B20ID;
char str;
float temperature;
      
system_clock_config();

at32_board_init();
uart_print_init(115200);
LcdInit();
printf("DS18B20温度传感器信息读取\n");
LcdFill(0,0,128,160,BLACK);
LcdShowString(2,24,"DS18B20",RED, BLACK,16);
LcdShow16x16Hz(60, 24, 10, YELLOW, BLACK);
LcdShow16x16Hz(76, 24, 11, YELLOW, BLACK);
LcdShow16x16Hz(92, 24, 12, YELLOW, BLACK);
LcdShow16x16Hz(108, 24, 13, YELLOW, BLACK);
LcdShow16x16Hz(2, 48, 14, YELLOW, BLACK);
LcdShow16x16Hz(18, 48, 15, YELLOW, BLACK);
LcdShow16x16Hz(34, 48, 16, YELLOW, BLACK);
LcdShow16x16Hz(50, 48, 17, YELLOW, BLACK);
LcdShow16x16Hz(66, 48, 18, YELLOW, BLACK);
delay_ms(2000);
while(DS18B20_Init())      
{
    printf("DS18B20温度传感器不存在\n");
    LcdFill(0,0,128,160,BLACK);
    LcdShowString(2,24,"DS18B20",RED, BLACK,16);
    LcdShow16x16Hz(60, 24, 10, YELLOW, BLACK);
    LcdShow16x16Hz(76, 24, 11, YELLOW, BLACK);
    LcdShow16x16Hz(92, 24, 12, YELLOW, BLACK);
    LcdShow16x16Hz(108, 24, 13, YELLOW, BLACK);
    LcdShow16x16Hz(2, 48, 14, YELLOW, BLACK);
    LcdShow16x16Hz(18, 48, 19, YELLOW, BLACK);
    LcdShow16x16Hz(34, 48, 20, YELLOW, BLACK);
    LcdShow16x16Hz(50, 48, 21, YELLOW, BLACK);
    delay_ms(1000);
}
printf("检测到DS18B20温度传感器,并初始化成功\n");
LcdFill(0,0,128,160,BLACK);
LcdShow16x16Hz(2, 24, 22, YELLOW, BLACK);
LcdShow16x16Hz(18, 24, 23, YELLOW, BLACK);
LcdShow16x16Hz(34, 24, 24, YELLOW, BLACK);
LcdShowString(50,24,"DS18B20",RED, BLACK,16);
LcdShow16x16Hz(108, 24, 10, YELLOW, BLACK);
LcdShow16x16Hz(2, 48, 11, YELLOW, BLACK);
LcdShow16x16Hz(18, 48, 12, YELLOW, BLACK);
LcdShow16x16Hz(34, 48, 13, YELLOW, BLACK);
LcdShow16x16Hz(50, 48, 14, YELLOW, BLACK);
LcdShowString(66,48,",",YELLOW, BLACK,16);
LcdShow16x16Hz(74, 48, 25, YELLOW, BLACK);
LcdShow16x16Hz(90, 48, 26, YELLOW, BLACK);
LcdShow16x16Hz(106, 48, 27, YELLOW, BLACK);
LcdShow16x16Hz(2, 72, 28, YELLOW, BLACK);
LcdShow16x16Hz(18, 72, 29, YELLOW, BLACK);
LcdShow16x16Hz(34, 72, 30, YELLOW, BLACK);

DS18B20_ReadId(DS18B20ID);
printf("DS18B20的序列号是: 0x");
      for ( i = 0; i < 8; i ++ )            
          printf ( "%.2X", DS18B20ID);
printf("\n");
      
sprintf(str,"0x%02X%02X%02X%02X%02X%02X%02X%02X",DS18B20ID,DS18B20ID,DS18B20ID,DS18B20ID,
                                                                     DS18B20ID,DS18B20ID,DS18B20ID,DS18B20ID);

LcdShowString(8,96,"DS18B20",RED, BLACK,12);
LcdShow16x16Hz(50, 96, 32, YELLOW, BLACK);
LcdShow16x16Hz(66, 96, 33, YELLOW, BLACK);
LcdShow16x16Hz(82, 96, 34, YELLOW, BLACK);
LcdShow16x16Hz(98, 96, 35, YELLOW, BLACK);
LcdShow16x16Hz(114, 96, 40, YELLOW, BLACK);
LcdShowString(8,120,str,RED,BLACK,12);
delay_ms(2000);
while(1)
{
    temperature=DS18B20_GetTemp_MatchRom(DS18B20ID);
    /* 打印通过 DS18B20 序列号获取的温度值 */
    printf("获取该序列号器件的温度:%.1f\n",temperature);
    LcdFill(0,0,128,160,BLACK);
    LcdShow16x16Hz(2, 8, 36, YELLOW, BLACK);
    LcdShow16x16Hz(18, 8, 37, YELLOW, BLACK);
    LcdShow16x16Hz(34, 8, 10, YELLOW, BLACK);
    LcdShow16x16Hz(50, 8, 11, YELLOW, BLACK);
    LcdShow16x16Hz(66, 8, 38, YELLOW, BLACK);
    LcdShow16x16Hz(82, 8, 39, YELLOW, BLACK);
    LcdShow16x16Hz(98, 8, 40, YELLOW, BLACK);
    LCD_ShowFloatNum1(8,32,temperature,4,RED, BLACK,24);
    LcdShow16x16Hz(70, 36, 41, RED, BLACK);
    /* 0.5s 读取一次温度值 */
    delay_ms(500);               
}
}      以上代码均在上一个驱动LCD工程上完成,因此直接下载到开发板后,可以在LCD屏上直观得看到DS18B20实时采集的温度值变化。当然也是支持串口同步打印输出温度值变化的。
      例如串口打印信息:

      一段实时操作演示见B站视频:https://www.bilibili.com/video/BV1YFNnzrENG/
https://www.bilibili.com/video/BV1YFNnzrENG/

页: [1]
查看完整版本: 【AT-START-M412测评】+ ③驱动DS18B20温度采集