DS18B20 数字温度传感器

DS18B20 简介

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

DS18B20技术性能特征:

  • ① 独特的单总线接口方式,DS18B20在与微处理器连接时仅需要一条口线即可实现微处理器与DS18B20的双向通讯。大大提高了系统的抗干扰性。
  • ② 测温范围 -55℃~+125℃,精度为±0.5℃。
  • ③支持多点组网功能,多个DS18B20可以并联在唯一的三线上,最多只能并联8个,实现多点测温,如果数量过多,会使供电电源电压过低,从而造成信号传输的不稳定。
  • ④工作电源: 3.0~5.5V/DC (可以数据线寄生电源)。
  • ⑤ 在使用中不需要任何外围元件。
  • ⑥测量结果以9~12位数字量方式串行传送。

其内部结构如下图

ROM 中的 64 位序列号是出厂前被光记好的,它可以看作是该 DS18B20 的地址序列码,每DS18B20 的 64 位序列号均不相同。64 位 ROM 的排列是:前 8 位是产品家族码,接着 48 位是DS18B20 的序列号,最后 8 位是前面 56 位的循环冗余校验码(CRC=X8+X5 +X4 +1)。ROM 作用是使每一个 DS18B20 都各不相同,这样就可实现一根总线上挂接多个。所有的单总线器件要求采用严格的信号时序,以保证数据的完整性。

DS18B20 共有 6 种信号类型:复位脉冲应答脉冲写 0写 1读 0读 1。所有这些信号,除了应答脉冲以外,都由主机发出同步信号。并且发送所有的命令和数据都是字节的低位在前。这里我们简单介绍

这几个信号的时序

  • 1)复位脉冲和应答脉冲:单总线上的所有通信都是以初始化序列开始。主机输出低电平,保持低电平时间至少 480 us,,以产生复位脉冲。接着主机释放总线,4.7K 的上拉电阻将单总线拉高,延时15~60 us,并进入接收模式(Rx)。接着 DS18B20 拉低总线 60~240 us,以产生低电平应答脉冲,若为低电平,再延时 480 us。
  • 2)写时序:写时序包括写 0 时序和写 1 时序。所有写时序至少需要 60us,且在 2 次独立的写时序之间至少需要 1us 的恢复时间,两种写时序均起始于主机拉低总线。写 1 时序:主机输出低电平,延时 2us,然后释放总线,延时 60us。写 0 时序:主机输出低电平,延时 60us,然后释放总线,延时 2us。
  • 3)读时序:单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令后,必须马上产生读时序,以便从机能够传输数据。所有读时序至少需要 60us,且在 2 次独立的读时序之间至少需要 1us 的恢复时间。每个读时序都由主机发起,至少拉低总线 1us。主机在读时序期间必须释放总线,并且在时序起始后的 15us 之内采样总线状态。典型的读时序过程为:主机输出低电平延时 2us,然后主机转入输入模式延时 12us,然后读取单总线当前的电平,然后延时 50us。

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

==复位脉冲==

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

//复位DS18B20
void DS18B20_Rst(void)       
{                 
    DS18B20_IO_OUT(); //设置为输出模式
    DS18B20_DQ_OUT=0; //拉低DQ
    delay_us(750);    //拉低750us(至少480us)
    DS18B20_DQ_OUT=1; //DQ=1拉高释放总线 
    delay_us(15);     //15US
    //进入接受模式,等待应答信号。
}

==应答信号==

//等待DS18B20的回应
//返回1:未检测到DS18B20的存在    返回0:存在
u8 DS18B20_Check(void)        
{   
    u8 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;
}

==写时序==

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

  • 写1时序:主机输出低电平,延时2us,然后释放总线,延时60us。
  • 写0时序:主机输出低电平,延时60us,然后释放总线,延时2us。
//写一个字节到DS18B20
//dat:要写入的字节
void DS18B20_Write_Byte(u8 dat)     
 {             
    u8 j;  u8 testb;
    DS18B20_IO_OUT();//设置PA0为输出
    for (j=1;j<=8;j++) 
   {
        testb=dat&0x01;
        dat=dat>>1;
        if (testb) //输出高
        {
            DS18B20_DQ_OUT=0;// 主机输出低电平
            delay_us(2);                  //延时2us
            DS18B20_DQ_OUT=1;//释放总线
            delay_us(60); //延时60us            
        }
        else //输出低
        {
            DS18B20_DQ_OUT=0;//主机输出低电平
            delay_us(60);               //延时60us
            DS18B20_DQ_OUT=1;//释放总线
            delay_us(2);                  //延时2us        
        }
    }
}

==读时序==

单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令后,必须马上产生读时序,以便从机能够传输数据。

所有读时序至少需要60us,且在2次独立的读时序之间至少需要1us的恢复时间。每个读时序都由主机发起,至少拉低总线1us。主机在读时序期间必须释放总线,并且在时序起始后的15us之内采样总线状态。

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

//从DS18B20读取一个位
//返回值:1/0
u8 DS18B20_Read_Bit(void)              // read one bit
{
    u8 data;
    DS18B20_IO_OUT();//设置为输出
    DS18B20_DQ_OUT=0; //输出低电平2us
    delay_us(2);
    DS18B20_DQ_OUT=1; //拉高释放总线
   DS18B20_IO_IN();//设置为输入
   delay_us(12);//延时12us
   if(DS18B20_DQ_IN)data=1;//读取总线数据
    else data=0;     
    delay_us(50);  //延时50us         
    return data;
}

Code

==ds18b20.c==

#include "ds18b20.h"
#include "delay.h"    

//复位DS18B20
void DS18B20_Rst(void)       
{                 
    DS18B20_IO_OUT();     //SET PG11 OUTPUT
    DS18B20_DQ_OUT=0;     //拉低DQ
    delay_us(750);        //拉低750us
    DS18B20_DQ_OUT=1;     //DQ=1 
    delay_us(15);         //15US
}
//等待DS18B20的回应
//返回1:未检测到DS18B20的存在
//返回0:存在
u8 DS18B20_Check(void)        
{   
    u8 retry=0;
    DS18B20_IO_IN();    //SET PG11 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;
}
//从DS18B20读取一个位
//返回值:1/0
u8 DS18B20_Read_Bit(void)      
{
    u8 data;
    DS18B20_IO_OUT();    //SET PG11 OUTPUT
    DS18B20_DQ_OUT=0; 
    delay_us(2);
    DS18B20_DQ_OUT=1; 
    DS18B20_IO_IN();    //SET PG11 INPUT
    delay_us(12);
    if(DS18B20_DQ_IN)data=1;
    else data=0;     
    delay_us(50);           
    return data;
}
//从DS18B20读取一个字节
//返回值:读到的数据
u8 DS18B20_Read_Byte(void)     
{        
    u8 i,j,dat;
    dat=0;
    for (i=1;i<=8;i++) 
    {
        j=DS18B20_Read_Bit();
        dat=(j<<7)|(dat>>1);
    }                            
    return dat;
}
//写一个字节到DS18B20
//dat:要写入的字节
void DS18B20_Write_Byte(u8 dat)     
 {             
    u8 j;
    u8 testb;
    DS18B20_IO_OUT();    //SET PG11 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);                          
        }
    }
}
//开始温度转换
void DS18B20_Start(void) 
{                                          
    DS18B20_Rst();       
    DS18B20_Check();     
    DS18B20_Write_Byte(0xcc);    // skip rom
    DS18B20_Write_Byte(0x44);    // convert
} 

//初始化DS18B20的IO口 DQ 同时检测DS的存在
//返回1:不存在
//返回0:存在         
u8 DS18B20_Init(void)
{
     GPIO_InitTypeDef  GPIO_InitStructure;

     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE);     //使能PORTG口时钟 

     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;                //PORTG.11 推挽输出
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;           
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOG, &GPIO_InitStructure);

     GPIO_SetBits(GPIOG,GPIO_Pin_11);    //输出1

    DS18B20_Rst();

    return DS18B20_Check();
}  
//从ds18b20得到温度值
//精度:0.1C
//返回值:温度值 (-550~1250) 
short DS18B20_Get_Temp(void)
{
    u8 temp;
    u8 TL,TH;
    short tem;
    DS18B20_Start ();              // ds1820 start convert
    DS18B20_Rst();
    DS18B20_Check();     
    DS18B20_Write_Byte(0xcc);    // skip rom
    DS18B20_Write_Byte(0xbe);    // convert        
    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;                    //获得底八位
    tem=(float)tem*0.625;        //转换     
    if(temp)return tem;         //返回温度值
    else return -tem;    
}

==ds18b20.h==

#ifndef __DELAY_H
#define __DELAY_H                
#include "sys.h"  

void delay_init(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);

#endif

DHT11 数字温湿度传感器

DHT11 简介

DHT11 是一款湿温度一体化的数字传感器。该传感器包括一个电阻式测湿元件和一个 NTC测温元件,并与一个高性能 8 位单片机相连接。通过单片机等微处理器简单的电路连接就能够实时的采集本地湿度和温度。DHT11 与单片机之间能采用简单的单总线进行通信,仅仅需要一个 I/O 口。传感器内部湿度和温度数据 40Bit 的数据一次性传给单片机,数据采用校验和方式进行校验,有效的保证数据传输的准确性。DHT11 功耗很低,5V 电源电压下,工作平均最大电流 0.5mA。

DHT11 的技术参数如下

  • 工作电压范围:3.3V-5.5V
  • 工作电流 :平均 0.5mA
  • 输出:单总线数字信号
  • 测量范围:湿度 20~90%RH,温度 0~50℃
  • 精度:湿度±5%,温度±2℃
  • 分辨率:湿度 1%,温度 1℃

DHT11 的管脚排列如图

DHT11 数字湿温度传感器采用单总线数据格式。即,单个数据引脚端口完成输入输出双向传输。其数据包由 5Byte(40Bit)组成。数据分小数部分和整数部分,一次完整的数据传输为40bit,高位先出。

DHT11 的数据格式为

8bit 湿度整数数据+8bit 湿度小数数据+8bit 温度整数数据+8bit 温度小数数据+8bit 校验和。

其中校验和数据为前四个字节相加。

传感器数据输出的是未编码的二进制数据。数据(湿度、温度、整数、小数)之间应该分开处理。

例如,某次从 DHT11 读到的数据如下图:

由以上数据就可得到湿度和温度的值,计算方法

湿度= byte4 . byte3=45.0 (%RH)
温度= byte2 . byte1=28.0 ( ℃)
校验= byte4+ byte3+ byte2+ byte1=73(=湿度+温度)(校验正确)

DHT11 的数据格式是十分简单的,DHT11 和 MCU 的一次通信最大为 3ms 左右,建议主机连续读取时间间隔不要小于 100ms。

下 DHT11 的传输时序。DHT11 的数据发送流程如下图

首先主机发送开始信号,即:拉低数据线,保持 t1(至少 18ms)时间,然后拉高数据线 t2(20~40us)时间,然后读取 DHT11 的响应,正常的话,DHT11 会拉低数据线,保持 t3(40~50us)时间,作为响应信号,然后 DHT11 拉高数据线,保持 t4(40~50us)时间后,开始输出数据。

DHT11 输出数字‘0’的时序如下图

DHT11 输出数字‘1’的时序如下图

下面通过 STM32 来实现对 DHT11 的读取了。

==复位==

拉低数据线,保持t1(至少18ms)时间,然后拉高数据线t2(20~40us)时间

//复位DHT11
void DHT11_Rst(void)       
{                 
    DHT11_IO_OUT();     //SET OUTPUT
    DHT11_DQ_OUT=0;     //拉低DQ
    delay_ms(20);        //拉低至少18ms
    DHT11_DQ_OUT=1;     //DQ=1 
    delay_us(30);         //主机拉高20~40us
}

==等待回应==

//等待DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
u8 DHT11_Check(void)        
{   
   u8 retry=0;
  DHT11_IO_IN();//SET INPUT     
   while (DHT11_DQ_IN&&retry<100)/DHT11会拉低40~80us
  {
    retry++;   delay_us(1);
  };     
  if(retry>=100)return 1;
  else retry=0;
   while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us
  {
       retry++;  delay_us(1);
  };
  if(retry>=100)return 1;        
  return 0;
}

Code

==dht11.c==

#include "dht11.h"
#include "delay.h"

//复位DHT11
void DHT11_Rst(void)       
{                 
    DHT11_IO_OUT();     //SET OUTPUT
    DHT11_DQ_OUT=0;     //拉低DQ
    delay_ms(20);        //拉低至少18ms
    DHT11_DQ_OUT=1;     //DQ=1 
    delay_us(30);         //主机拉高20~40us
}
//等待DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
u8 DHT11_Check(void)        
{   
    u8 retry=0;
    DHT11_IO_IN();//SET INPUT     
    while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us
    {
        retry++;
        delay_us(1);
    };     
    if(retry>=100)return 1;
    else retry=0;
    while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us
    {
        retry++;
        delay_us(1);
    };
    if(retry>=100)return 1;        
    return 0;
}
//从DHT11读取一个位
//返回值:1/0
u8 DHT11_Read_Bit(void)              
{
     u8 retry=0;
    while(DHT11_DQ_IN&&retry<100)//等待变为低电平
    {
        retry++;
        delay_us(1);
    }
    retry=0;
    while(!DHT11_DQ_IN&&retry<100)//等待变高电平
    {
        retry++;
        delay_us(1);
    }
    delay_us(40);//等待40us
    if(DHT11_DQ_IN)return 1;
    else return 0;           
}
//从DHT11读取一个字节
//返回值:读到的数据
u8 DHT11_Read_Byte(void)    
{        
    u8 i,dat;
    dat=0;
    for (i=0;i<8;i++) 
    {
           dat<<=1; 
        dat|=DHT11_Read_Bit();
    }                            
    return dat;
}
//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
u8 DHT11_Read_Data(u8 *temp,u8 *humi)    
{        
     u8 buf[5];
    u8 i;
    DHT11_Rst();
    if(DHT11_Check()==0)
    {
        for(i=0;i<5;i++)//读取40位数据
        {
            buf[i]=DHT11_Read_Byte();
        }
        if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
        {
            *humi=buf[0];
            *temp=buf[2];
        }
    }else return 1;
    return 0;        
}
//初始化DHT11的IO口 DQ 同时检测DHT11的存在
//返回1:不存在
//返回0:存在         
u8 DHT11_Init(void)
{     
     GPIO_InitTypeDef  GPIO_InitStructure;

     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);     //使能PC端口时钟

     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;                 //PC11端口配置
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;          //推挽输出
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOC, &GPIO_InitStructure);                 //初始化IO口
     GPIO_SetBits(GPIOC,GPIO_Pin_11);                         //PC11 输出高

    DHT11_Rst();  //复位DHT11
    return DHT11_Check();//等待DHT11的回应
}

==dht11.h==

#ifndef __DHT11_H
#define __DHT11_H 
#include "sys.h"   

//IO方向设置
#define DHT11_IO_IN()  {GPIOc->CRH&=0XFFFF0FFF;GPIOc->CRH|=8<<12;}
#define DHT11_IO_OUT() {GPIOc->CRH&=0XFFFF0FFF;GPIOc->CRH|=3<<12;}
////IO操作函数                                               
#define    DHT11_DQ_OUT PCout(11) //数据端口    PC11 
#define    DHT11_DQ_IN  PCin(11)  //数据端口    PC11


u8 DHT11_Init(void);//初始化DHT11
u8 DHT11_Read_Data(u8 *temp,u8 *humi);//读取温湿度
u8 DHT11_Read_Byte(void);//读出一个字节
u8 DHT11_Read_Bit(void);//读出一个位
u8 DHT11_Check(void);//检测是否存在DHT11
void DHT11_Rst(void);//复位DHT11    
#endif

==delay.c==

#include "delay.h"

static u8  fac_us=0;                            //us延时倍乘数               
static u16 fac_ms=0;                            //ms延时倍乘数,在ucos下,代表每个节拍的ms数

//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init()
{

    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);    //选择外部时钟  HCLK/8
    fac_us=SystemCoreClock/8000000;                //为系统时钟的1/8  
    fac_ms=(u16)fac_us*1000;                    //非OS下,代表每个ms需要的systick时钟数   
}                                    

//延时nus
//nus为要延时的us数.                                               
void delay_us(u32 nus)
{        
    u32 temp;             
    SysTick->LOAD=nus*fac_us;                     //时间加载               
    SysTick->VAL=0x00;                            //清空计数器
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;    //开始倒数      
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));        //等待时间到达   
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;    //关闭计数器
    SysTick->VAL =0X00;                           //清空计数器     
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864 
void delay_ms(u16 nms)
{                     
    u32 temp;           
    SysTick->LOAD=(u32)nms*fac_ms;                //时间加载(SysTick->LOAD为24bit)
    SysTick->VAL =0x00;                            //清空计数器
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;    //开始倒数  
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));        //等待时间到达   
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;    //关闭计数器
    SysTick->VAL =0X00;                           //清空计数器              
}

==delay.h==

#ifndef __DELAY_H
#define __DELAY_H                
#include "sys.h"       
void delay_init(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);

#endif

==main.c==

#include "delay.h"
#include "sys.h"
#include "dht11.h"      

 int main(void)
 {     
    u8 t=0;                
    u8 temperature;          
    u8 humidity;           

    delay_init();             //延时函数初始化      
    while(1)
    {                
         if(t%10==0)            //每100ms读取一次
        {                                      
            DHT11_Read_Data(&temperature,&humidity);    //读取温湿度值                          
        }                   
         delay_ms(10);
        t++;
        if(t==20)
        {
            t=0;
        }
    }
}