赞
踩
大家好,我是林白柏;
希望你看完之后,能有所收获,不足请指正!
PS:本文提到的模块都使用正点原子的stm32开发板战舰驱动,模块用的某宝现成的模块。
PS:代码为正点原子的代码,整理成自己习惯的接口
DS18B20是单总线接口的温度传感器,而且一个总线上可以挂多个DS18B20,通过传感器内部的64bit ROM区分不同的DS18B20(前8bit为家族码,随后48bit为序列号每个DS18B20的序列号均不相同,最后8bit是前56bit的CRC检验码),这点跟DHT11不同。
DS18B20的内部结构如下图
寄生电源电路可以使DS18B20在不接电源的情况下工作。本文演示时有外接VDD
另一个跟DHT11不同点是温度测量范围和精度,DS18B20测温范围-55_+125℃,精度±0.5℃(DHT11:0-50℃;±2℃),并且可以通过配置寄存器设置采样分辨率(温度数据的位数,9~12bits),位数越多采样需要的时间越长,对应关系如下图
温度数据读回来后不能直接用,需要根据规格书提供的表进行转换才能得到温度值(这点没有DHT11那么直观,但也不复杂),对应关系如下表
记温度值为T0,读回来的数据为T1,从表中可以得到T0 = T1 * 0.0625;
现在,你对DS18B20有了基本的了解,那接下来要解决的问题就是单片机怎么跟DS18B20通信了。
初始化信号(复位信号)
主机拉低最少480us,之后将引脚设置为输入等待DS18B20(后面简称从机)15-60us后响应;从机的响应信号为拉低60-240us,之后释放总线,由上拉电阻将总线拉高;时序图如下图所示
读/写“0”和“1”
写“1”和写“0”都是先拉低,如果是写“1”则在1us~15us内拉高,如果时写“0”,则在60us后拉高;
读“1”和读“0”都是先拉低,然后将引脚配置为输入,在拉低的1us~15us内读取引脚电平,为高则为“1”,为低则为“0”;时序图如下图所示
了解了初始化信号,读写“0”“1”的时序,接下来根据特定的通信流程,我们就能读出温度数据啦!
DS18B20的规格书有两页的通讯流程图,有兴趣的小伙伴可以去看下规格书下载,这里我们就用最简单的流程
触发传感器开始转换:
复位 -> 发 SKIP ROM 命令(0xCC) -> 发开始转换命令(0x44)
延时,等待转换完成
读取温度:
复位 -> 发送 SKIP ROM 命令(0xCC) -> 发读存储器命令(0xBE) -> 连续读出两个字节数据(即温度)
结束
发送命令 0xCC可跳过发送64bit ROM的环节,类似I2C的发送设备地址,因为我们只用了1个DS18B20,不用关心序列号,所以可以跳过;
发送命令0xBE可读出DS18B20的整个存储器的9bytes的数据,中间允许主机发送复位信号打断。因为我们只需要温度数据,所以只接收前2bytes的数据;读取数据时,数据是从Byte0(下图)的低位开始传输,写数据也是低位先写。更多命令请查看规格书
DS18B20的内存映射如下图所示
这部分只讲解部分代码,完整代码可通过文末链接获取
这部分跟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; }
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); } } }
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倍,用户处理起来也比较方便。
举例:
float
,用户如果直接传需要4bytes,否则得做转换;如果返回short
,则用户可以直接传,只用2bytes;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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。