文章目录
- 前言
- DS18B20介绍
-
- 基本情况
- 怎么通信
-
- 时序
- 通信流程
- 代码讲解
-
- 基本信号
- 读一帧数据
- 代码使用方法
- 代码获取
前言
大家好,我是林白柏;
希望看完能有所收获。请纠正不足!
PS:本文提到的所有模块都使用正点原子开发板战舰某宝现成的模块驱动模块。
PS:代码是正原子的代码,整理成自己习惯的界面
DS18B20介绍
基本情况
DS18B20是单总线接口的温度传感器,一条总线可以挂多个DS18B20通过传感器内部的64bit ROM区分不同的DS18B20(前8bit家族码,然后48bit每个序列号DS18B20的序列号不同,最后8bit是前56bit的CRC检验码),这个跟着DHT11不同。
DS18B20的内部结构如下图所示
可以使寄生电源电路DS18B20不接电源工作。
另一个跟DHT不同之处在于温度测量的范围和精度,(DHT11:0-50℃;±2℃),并且可以通过配置寄存器设置采样分辨率(温度数据的位数,9~12bits),采样时间越长,对应关系如下图所示
读回温度数据后,不能直接使用。温度值需要根据规格书提供的表进行转换温度值(这不是DHT11如此直观,但并不复杂),对应关系如下表所示
记温度值为T0.读回的数据是T一、可从表中获得;
怎么通信
现在,你对DS18B有了基本的了解,接下来要解决的问题就是了。
时序
-
初始化信号(复位信号)
主机,然后将引脚设置为输入等待DS18B20(以下简称从机);从机的,然后释放总线,;如下图所示,时序图
-
读/写0和1
写“1”和写“0”都是先拉低,如果是写“1”则,若时写0,则;
读1和读0都是先拉低,然后将引脚配置为输入,拉低,时序图如下图所示
通信流程
了解初始化信号,读写0和1时序,然后根据特定的通信流程,我们可以读取温度数据!
DS18B20规格书有两页通信流程图。有兴趣的朋友可以去看规格书下载。在这里,我们将使用最简单的流程
-
触发传感器开始转换:
复位 -> 发 命令(0xCC) -> 发命令(0x44)
-
延迟,等待转换完成
-
读取温度:
复位 -> 发送 SKIP ROM 命令(0xCC) -> 发命令(0xBE) -> 连续读出两个字节数据(即温度)
-
结束
发送命令 可跳过发送64bit ROM类似的链接I2C发送设备地址,因为我们只用了一个DS18B不用担心序列号,所以可以跳过;
发送命令可读出DS18B920整个存储器bytes中间允许主机发送复位信号打断数据。因为我们只需要温度数据,所以我们只接收前2名bytes读取数据时,,写数据也是低位写。更多命令请查看规格书
DS18B如下图所示
代码讲解
这部分只解释部分代码,完整代码可以通过文本末尾的链接获得
基本信号
- 复位信号
这部分跟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) span class="token punctuation">{
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倍,用户处理起来也比较方便。 举例:
- 产品需要将采集到的温度传给其他设备,如果返回
float
,用户如果直接传需要4bytes,否则得做转换;如果返回short
,则用户可以直接传,只用2bytes; - 产品需要在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