前言

在嵌入式开发中,温度测量是一个常见的应用场景,而DS18B20作为一种单总线数字温度传感器,以其高精度、低功耗、易操作等特点,成为了许多开发者的首选。本节课,我们将带领大家掌握如何使用STM32来驱动DS18B20,实现温度的精确测量。

一、准备

1.DS18B20简介

DS18B20是一种单总线数字温度传感器,它能够直接输出数字信号,避免了传统模拟温度传感器需要模数转换的复杂过程。它的主要特点如下:
(1)单总线接口
仅需一个GPIO引脚即可实现通信,大大简化了硬件连接。
(2)高精度
测量精度可达±0.5℃,温度范围为-55℃至+125℃。
(3)低功耗
在测量时仅消耗微弱电流,非常适合电池供电的便携式设备。
(4)多设备连接
单总线上可以挂接多个DS18B20,通过ROM码区分每个设备。
在这里插入图片描述

2.DS18B20控制方法

2.1 原理图
原理图如下图所示,注意,DQ引脚需要上拉4.7K电阻,以确保信号的稳定传输。
在这里插入图片描述
2.2 硬件连接
硬件连接示意图如下所示:
在这里插入图片描述

2.3 总线协议
(1)初始化时序

  • 复位脉冲
    主机拉低总线至少480us,然后释放总线。
  • 存在脉冲
    DS18B20在检测到总线的上升沿后,等待15-60us,然后发出一个低电平持续60-240us的存在脉冲。
    在这里插入图片描述

(2)写 ‘1’ 时序

  • 主机拉低数据线15us后迅速拉高,DS18B20在15-60us的窗口内采样数据。
    在这里插入图片描述

(3)写 ‘0’ 时序

  • 主机拉低数据线至少60us。
    在这里插入图片描述

(4)读 时序

  • 读触发信号
    主机拉低数据线小于15us,然后释放总线。
  • 数据采样
    主机在释放总线后15us开始采样数据。
    在这里插入图片描述

二、实例

1. 建立工程

将上节《OLED显示实验》代码复制并修改名字为:10.OLED,打开Project.ioc文件,配置PB4输出。
在这里插入图片描述

生成代码后,通过Keil打开工程,在Application/User/Driver处右击选择Add New Item…,添加C文件,名称命名为drv_ds18b20。
在这里插入图片描述

同样方法创建drv_ds18b20.h文件, 上图第2步选择Header File(.h)文件即可,drv_ds18b20.h内容如下:

#ifndef __DRV_DS18B20_H__
#define __DRV_DS18B20_H__

#include "main.h"

////IO操作函数                                               
#define    DS18B20_DQ_OUT PBout(4) //数据端口    PB4
#define    DS18B20_DQ_IN  PBin(4)  //数据端口    PB4

uint8_t DS18B20_Init(void);            //初始化DS18B20

float DS18B20_Get_Temp(void);    //获取温度

void DS18B20_Start(void);        //开始温度转换

void DS18B20_Write_Byte(uint8_t dat);//写入一个字节

uint8_t DS18B20_Read_Byte(void);        //读出一个字节

uint8_t DS18B20_Read_Bit(void);        //读出一个位

uint8_t DS18B20_Check(void);            //检测是否存在DS18B20

void DS18B20_Rst(void);            //复位DS18B20    

#endif

drv_ds18b20.c内容如下:

/****************************************Copyright (c)****************************************************
**     
** File name:               drv_ds18b20.c
** Created by:              XiaoYe
** Created date:            2025-06-01
** Version:                 v1.0
** Descriptions:            The original 
** Link address:            https://gitee.com/iot_camp/kits_inn_iot
**
*********************************************************************************************************/

#include "drv_ds18b20.h"
#include "gpio.h"

/**
  * @brief 设置PB4方向输入: 
  * @param 输入参数: 无
  * @return 返 回 值: 无
  */
void DS18B20_IO_IN(void)  
{
  GPIO_InitTypeDef GPIO_InitStruct;
  
  /* 串口外设功能GPIO配置 */
  GPIO_InitStruct.Pin   = DS18B20_Pin;
  GPIO_InitStruct.Mode  = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull  = GPIO_PULLUP;
  HAL_GPIO_Init(DS18B20_GPIO_Port, &GPIO_InitStruct);
}

/**
  * @brief 设置PB4方向输出: 
  * @param 输入参数: 无
  * @return 返 回 值: 无
  */
void DS18B20_IO_OUT(void) 
{
  GPIO_InitTypeDef GPIO_InitStruct;
  
  /* 串口外设功能GPIO配置 */
  GPIO_InitStruct.Pin = DS18B20_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(DS18B20_GPIO_Port, &GPIO_InitStruct);      
}

/**
  * @brief 复位DS18B20 
  * @param 输入参数: 无
  * @return 返 回 值: 无
  */
void DS18B20_Rst(void)       
{                 
    DS18B20_IO_OUT(); //SET PA0 OUTPUT
 DS18B20_DQ_OUT = 0; //拉低DQ
    delay_us(750);    //拉低750us
 DS18B20_DQ_OUT = 1; //DQ=1 
    delay_us(15);     //15US
}

/**
  * @brief 等待DS18B20的回应 
  * @param 输入参数: 无
  * @return 返 回 值: 1:未检测到DS18B20的存在, 0:存在
  */
uint8_t DS18B20_Check(void)        
{   
    uint8_t retry=0;
    DS18B20_IO_IN();//SET PA0 INPUT     
    while (DS18B20_DQ_IN&&retry<200)
    {
    retry++;
    delay_us(1);
    };     
    if(retry>=200)return 1;
    else retry=0;
 while (!DS18B20_DQ_IN&&retry<240)
    {
    retry++;
    delay_us(1);
    };
    if(retry>=240)return 1;        
    return 0;
}

/**
  * @brief 从DS18B20读取一个位 
  * @param 输入参数: 无
  * @return 返 回 值: 1/0
  */
uint8_t DS18B20_Read_Bit(void)              // read one bit
{
    uint8_t data;
    DS18B20_IO_OUT();//SET PA0 OUTPUT
 DS18B20_DQ_OUT = 0; 
    delay_us(2);
 DS18B20_DQ_OUT = 1; 
    DS18B20_IO_IN();//SET PA0 INPUT
    delay_us(12);
    if(DS18B20_DQ_IN)
     data=1;
 else 
     data=0;     
 delay_us(50);           
 return data;
}

/**
  * @brief 从DS18B20读取一个字节 
  * @param 输入参数: 无
  * @return 返 回 值: 读到的数据
  */
uint8_t DS18B20_Read_Byte(void)    // read one byte
{        
    uint8_t i,j,dat;
 dat=0;
    for (i=1;i<=8;i++) 
    {
    j=DS18B20_Read_Bit();
    dat=(j<<7)|(dat>>1);
    }                            
    return dat;
}

/**
  * @brief 写一个字节到DS18B20 
  * @param 输入参数: dat:要写入的字节
  * @return 返 回 值: 无
  */
void DS18B20_Write_Byte(uint8_t dat)     
 {
    uint8_t j;
    uint8_t testb;
    DS18B20_IO_OUT();//SET PA0 OUTPUT;
  for (j=1;j<=8;j++) 
    {
    testb=dat&0x01;
    dat=dat>>1;
    if (testb)
    {
        DS18B20_DQ_OUT = 0;// Write 1
        delay_us(2);                            
        DS18B20_DQ_OUT = 1;
        delay_us(60);             
    }
    else 
    {
        DS18B20_DQ_OUT = 0;// Write 0
        delay_us(60);             
        DS18B20_DQ_OUT = 1;
        delay_us(2);                          
    }
  }
}

/**
  * @brief 开始温度转换 
  * @param 输入参数: 无
  * @return 返 回 值: 无
  */
void DS18B20_Start(void)// ds1820 start convert
{                                          
    DS18B20_Rst();       
    DS18B20_Check();     
    DS18B20_Write_Byte(0xcc);// skip rom
    DS18B20_Write_Byte(0x44);// convert
} 
     
/**
  * @brief 初始化DS18B20的IO口 DQ 同时检测DS的存在 
  * @param 输入参数: 无
  * @return 返 回 值: 1:不存在, 0:存在
  */
uint8_t DS18B20_Init(void)
{
    DS18B20_Rst();
    return DS18B20_Check();
}  

/**
  * @brief 从ds18b20得到温度值 
  * @param 输入参数: 无
  * @return 返 回 值: 温度值 (-550~1250) 
  */
float DS18B20_Get_Temp(void)
{
    uint8_t temp;
    uint8_t TL,TH;
    short tem;
    float temperature;
    
    DS18B20_Start ();                    // ds1820 start convert
    DS18B20_Rst();
    DS18B20_Check();     
    DS18B20_Write_Byte(0xcc);// skip rom
    DS18B20_Write_Byte(0xbe);// Read Scratchpad        
    TL=DS18B20_Read_Byte(); // LSB   
    TH=DS18B20_Read_Byte(); // MSB  
              
    if(TH>7)
    {
        TH=~TH;
        TL=~TL; 
        temp=0;//温度为负  
    }else temp=1;//温度为正            
    tem=TH; //获得高八位
    tem<<=8;    
    tem+=TL;//获得低八位
        
    temperature=(float)tem*0.625/10;//转换
    if(temp)
        return temperature; //返回温度值
    else 
        return -temperature;    
} 

main函数内容如下, 修改OLED显示内容,while(1)中定期调用DS18B20_Get_Temp()获取实时温度数据,并更新OLED显示。

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */
    int ret = 0;
    int cnt = 300;
    float value_temp = 0;
    char str_temp[16] = { 0x00 };
    
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_USART3_UART_Init();
  MX_I2C1_Init();
  /* USER CODE BEGIN 2 */
    printf("start application\r\n");
    // 使能空闲中断
  __HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE); 

  HAL_UART_Receive_DMA(&huart3, (uint8_t *)Uart_Frame_Record.Data_RX_BUF, RX_BUF_MAX_LEN);  // 启动DMA接收 

    // 获取温度数据
  value_temp = DS18B20_Get_Temp();
    
    // 初始化 OLED
    oled_init();
    delay_ms(200);
    oled_fill(0xFF);//全屏点亮
    delay_ms(200);
    oled_fill(0x00);//全屏灭

    // 显示OLED屏幕信息
    oled_show_string(24, 0, (unsigned char *)" System", 2);
    oled_show_chinese(5, 4, 12);
    oled_show_chinese(20, 4, 13);
     
#ifdef SUPPORT_ESP8266
    // 连接路由器
    ESP8266_STAInit();
    delay_ms(1000);
    if (wifi_connect_status != WIFI_GOT_IP)
    {
     while(ESP8266_STAConnect((char *)Wssid, (char *)Wpassword));
    }
    
    // 连接MQTT 服务器
    printf("ready to connect to mqtt server\r\n");
    mqtt_task_init();
    
    mqtt_task_subcribe();

//mqtt_task_report();
  #endif

    
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    #ifdef SUPPORT_ESP8266
    mqtt_task_report();
    HAL_Delay(60000);
    #endif
    
    value_temp = DS18B20_Get_Temp();
    sprintf(str_temp, ": %.1f ", value_temp);
    oled_show_string(40, 4, (unsigned char *)str_temp, 2);
    HAL_Delay(1000);
  }
    return ret;
  /* USER CODE END 3 */
}

2. 核心函数

drv_ds18b20.c中核心函数为float DS18B20_Get_Temp(void),每次读取数据都会先发送复位信号,注意命令含义,0xCC表示使用此命令同时寻址总线上的所有设备,而无需发送任何ROM代码信息,主设备可以通过发出Skip ROM命令和Convert T[0x44]命令,使总线上的所有DS18B20同时执行温度转换。
注意,只有当总线上只有一个从属设备时,Read Scratchpad[0xBE]命令才能跟随Skip ROM命令。在这种情况下,通过允许主设备从从设备读取而不发送设备的64位ROM代码,可以节省时间。如果存在多个从属设备,则Skip ROM命令和Read Scratchpad命令将导致总线上的数据冲突,因为多个设备将尝试同时传输数据。

float DS18B20_Get_Temp(void)
{
    uint8_t temp;
    uint8_t TL,TH;
    short tem;
    float temperature;
    
    DS18B20_Start ();   // ds1820 start convert
    DS18B20_Rst();
    DS18B20_Check();     
    DS18B20_Write_Byte(0xcc);// skip rom
    DS18B20_Write_Byte(0xbe);// Read Scratchpad    
    TL=DS18B20_Read_Byte(); // LSB   
    TH=DS18B20_Read_Byte(); // MSB  
              
    if(TH>7)
    {
        TH=~TH;
        TL=~TL; 
        temp=0;//温度为负  
    }else temp=1;//温度为正            
    tem=TH; //获得高八位
    tem<<=8;    
    tem+=TL;//获得低八位
        
    temperature=(float)tem*0.625/10;//转换
    if(temp)
        return temperature; //返回温度值
    else 
        return -temperature;    
} 

3. 程序执行

本实验实现了上电初始化完成后,每隔1秒钟获取一次温度数据,并立刻显示在OLED上面。
在这里插入图片描述

三、小结

如您在使用过程中有任何问题,请加QQ群进一步交流。

QQ交流群:573122190 (备注:物联网项目交流)

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐