【单片机】温度传感器 DS18B20

前言

大家好,我是林白柏;

希望你看完之后,能有所收获,不足请指正!

PS:本文提到的模块都使用正点原子的stm32开发板战舰驱动,模块用的某宝现成的模块。

PS:代码为正点原子的代码,整理成自己习惯的接口

DS18B20介绍

基本情况

DS18B20是单总线接口的温度传感器,而且一个总线上可以挂多个DS18B20,通过传感器内部的64bit ROM区分不同的DS18B20(前8bit为家族码,随后48bit为序列号每个DS18B20的序列号均不相同,最后8bit是前56bit的CRC检验码),这点跟DHT11不同。

DS18B20的内部结构如下图

image-20220424211510902

寄生电源电路可以使DS18B20在不接电源的情况下工作。本文演示时有外接VDD

image-20220424212157388

另一个跟DHT11不同点是温度测量范围和精度,DS18B20测温范围-55_+125℃,精度±0.5℃(DHT11:0-50℃;±2℃),并且可以通过配置寄存器设置采样分辨率(温度数据的位数,9~12bits),位数越多采样需要的时间越长,对应关系如下图

image-20220424212538012

温度数据读回来后不能直接用,需要根据规格书提供的表进行转换才能得到温度值(这点没有DHT11那么直观,但也不复杂),对应关系如下表

image-20220424212810632

记温度值为T0,读回来的数据为T1,从表中可以得到T0 = T1 * 0.0625

怎么通信

现在,你对DS18B20有了基本的了解,那接下来要解决的问题就是单片机怎么跟DS18B20通信了。

时序

  • 初始化信号(复位信号)

    主机拉低最少480us,之后将引脚设置为输入等待DS18B20(后面简称从机)15-60us后响应;从机的响应信号为拉低60-240us,之后释放总线,由上拉电阻将总线拉高;时序图如下图所示

image-20220424220644249

  • 读/写“0”和“1”

    写“1”和写“0”都是先拉低,如果是写“1”则在1us~15us内拉高,如果时写“0”,则在60us后拉高

    读“1”和读“0”都是先拉低,然后将引脚配置为输入,在拉低的1us~15us内读取引脚电平,为高则为“1”,为低则为“0”;时序图如下图所示

    image-20220424221641095

通信流程

了解了初始化信号,读写“0”“1”的时序,接下来根据特定的通信流程,我们就能读出温度数据啦!

DS18B20的规格书有两页的通讯流程图,有兴趣的小伙伴可以去看下规格书下载,这里我们就用最简单的流程

  1. 触发传感器开始转换:

    复位 -> 发 SKIP ROM 命令(0xCC) -> 发开始转换命令(0x44)

  2. 延时,等待转换完成

  3. 读取温度:

    复位 -> 发送 SKIP ROM 命令(0xCC) -> 发读存储器命令(0xBE) -> 连续读出两个字节数据(即温度)

  4. 结束

发送命令 0xCC可跳过发送64bit ROM的环节,类似I2C的发送设备地址,因为我们只用了1个DS18B20,不用关心序列号,所以可以跳过;

发送命令0xBE可读出DS18B20的整个存储器的9bytes的数据,中间允许主机发送复位信号打断。因为我们只需要温度数据,所以只接收前2bytes的数据;读取数据时,数据是从Byte0(下图)的低位开始传输,写数据也是低位先写。更多命令请查看规格书

DS18B20的内存映射如下图所示

image-20220424215354959

代码讲解

这部分只讲解部分代码,完整代码可通过文末链接获取

基本信号

  • 复位信号

这部分跟DHT11的代码很像

void DS18B20_Rst(void)	   
{                 
	DS18B20_IO_OUT(); 	//SET PG11 OUTPUT
    DS18B20_DQ_OUT=0; 	//拉低DQ
    delay_us(750);    	//拉低750us(大于480us即可)
    DS18B20_DQ_OUT=1; 	//DQ=1 
	delay_us(15);     	//15US
}

复位信号之后等待DS18B20响应

u8 DS18B20_Check(void) 	   
{   
	u8 retry=0;
	DS18B20_IO_IN();	//SET PG11 INPUT	 
    while (DS18B20_DQ_IN&&retry<200)//DS18B20会拉低60-240us,没拉低说明传感器不存在
	{
		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 1;	    
	return 0;
}
  • 写1byte
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);                          
        }
    }
}
  • 读1byte
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;
}

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;
}

读一帧数据

需要说明一点,前面提到温度值为T0 = T1 * 0.0625,这样就需要返回float变量;代码中是*0.625,得到的值为实际温度值的10倍,用户处理起来也比较方便。
举例:

  1. 产品需要将采集到的温度传给其他设备,如果返回float,用户如果直接传需要4bytes,否则得做转换;如果返回short,则用户可以直接传,只用2bytes;
  2. 产品需要在oled显示温度值,如果是float,要取小数部分就比较麻烦一点;如果是short,直接%10就可以得到小数部分。
uint8_t ds18b20_read_data( short * data )
{
    u8 temp;
    u8 TL,TH;
	short tem;
    DS18B20_Start ();  			// ds1820 start convert
    DS18B20_Rst();
    if( DS18B20_Check() ) {
        return 1;
    } 
    DS18B20_Write_Byte(0xcc);	// skip rom
    DS18B20_Write_Byte(0xbe);	// read [function command]  
    TL=DS18B20_Read_Byte(); 	// 低字节
    TH=DS18B20_Read_Byte(); 	// 高字节
	    	  
    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) *data = tem; 		//返回温度值
	else *data = -tem;  

    return 0;
}

代码使用方法

int main(void){
    dht11_init();
    
    while(1) {
        short temp_data = 0;
        
        if( !ds18b20_read_data( &temp_data ) ) {
            _LOG_DEBUG( "[ds] temp %0.1fC\n\n", ((float)temp_data/10.0));
        }
        else {
            _LOG_INFO("[err] ds18b20 read\n");
        }
        delay_ms(2000);
    }
}

今天就分享到这里,希望你在这篇文章中有所收获;不变秃且变强[doge]。

代码获取

共用代码:https://gitee.com/sumoting1629/mcu-practice/tree/master/common

驱动代码:https://gitee.com/sumoting1629/mcu-practice/tree/master/component

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值