温度传感 -DS18B20

温度传感 -DS18B20

单总线接口DS18B20温度传感器

DS18B20的硬件连接就不多说了,分为外部电源供电与寄生电源供电方式。

其内部方框图如下图,64位的ROM(只读),可以理解为DS18B20的ID。一个暂存器(掉电丢失数据)有8byte:从低字节开始是温度传感器的低字节、高字节、上限触发(用于设置温度报警上限)、下限触发、配置寄存器(用于设置温度传感器转化位数,默认12位)、(3字节保留位)、CRC。另外有一个EEPROM(掉电不丢失数据),用来保存暂存器相应的上限触发、下限触发、配置寄存器、CRC。

现在,我们需要跟传感器通信,通信的话一般先要先要有一个初始化,所谓初始化,说白了就是问一下DS18B20在不在,然后,如果DS18B20还“活“着的话,它会给你回话,当然这里的回话很简单,把原来高电平拉至低电平。(跟IIC差不多,平常状态是高电平,低电平的话就是非常态)

初始化的时序如下图:

仔细看这张图就知道,主控器先将电平拉低,最少拉低480us,然后将总线释放掉(回到高电平)。然后,DS18B20为了表示存在,会在60-240us这段时间之间把总线拉低。这样,你可以在将总线释放掉后大约70us时候,读取一下总线电平状态,就可以知道了。

打完了招呼之后,就需要聊天。没错,就是向其发送命令或者数据。但你得知道怎么在一条总线上发一位数据。

上图就是单片机向总线发送1位数据的时序图。先拉低总线(持续时间要大于1us),然后在15us之内,你要发送你的电平(如果是高电平,释放掉总线;如果是低电平,就持续拉低电平),在接下来的45us之内,DS18B20会采样总线电平值。最后你还要把总线释放掉,便会常态。通常需要一次发个8bit,在每比特之间,要记得至少有1us的延时,当然,延时没上限。

学会了发送数据,也要学会接受数据。

从总线上面接受数据也跟发送数据一样,仔细看时序图,就可以解决了。

通常,我们会写成发一个字节的函数(而不是一比特)。有了通信的手段,再去看看通信的内容。就像人类可以看懂a,b,c一样,DS18B20可以看懂的就比较少,而且你得按照规则发送,不然它也看不懂。

在数据手册上,分为ROM指令:包括MATCH ROM[0x55]、SKIP ROM[0xCC]等。当然SKIP ROM这几个单词是给人类看的,真正发送给DS18B20的是0xcc(十六进制)。还有功能指令:包括CONVERT T[0x44]等。

总结来看,你得这样跟DS18B20交流:

步骤1:初始化(打招呼)

步骤2:ROM操作指令(就是发送指令)

步骤3:DS18B20功能指令(还是发送指令)

当然,可能需要读取温度数据,就跟在发送完读取指令之后。需要提醒一下:各个步骤之间不必没有空隙,而是需要有个延时(总线一直处于高电平),这个延时很长也不要紧,就是不要太短,不然的话,DS18B20就工作不正常。

下面贴出我写的代码:

头文件部分:(通常头文件写常数、变量、宏定义、函数原型,C文件写函数实体)

#ifndef __hal_ds18b20_h__
#define __hal_ds18b20_h__

#include<reg52.h>
#include"datatype.h"
#include"hal.h"
#include"delay.h"

sbit dq=P1^2;

#define DQ dq

//DS18B20 ROM指令
#define SEARCH_ROM 0xf0
#define READ_ROM 0x33
#define MATH_ROM 0x55
#define SKIP_ROM 0xcc//使用该指令跳过ROM指令
#define ALARM_SEARCH 0xec

//DS18B20 功能指令
#define CONVERT_T 0x44//使用该指令开始转换温度
#define WRITE_SCRATCHPAD 0x4e
#define READ_SCRATCHPAD 0xbe//使用该指令读取温度值
#define COPY_SCRATCHPAD 0x48
#define RECALL_E2 0xb8
#define READ_POWER_SUPPLY 0xb4


bit hal_ds18b20_init();
void hal_ds18b20_bit_write(bit val);
bit hal_ds18b20_bit_read();
void hal_ds18b20_byte_write(uchar val);
uchar hal_ds18b20_byte_read();
uint hal_ds18b20_get_temp(bit length,uchar * flag);

#endif


C文件部分:

#include"hal_ds18b20.h"

//返回1:表示初始化成功
bit hal_ds18b20_init()
{
//DQ高电平
DQ=0;//先拉低
delay_480us();//等待480us
DQ=1;//再释放总线,进入等待状态
delay_70us();//等待70us
return !DQ;
//延时一段时间后,DQ高电平
}

void hal_ds18b20_bit_write(bit val)
{
//DQ高电平
DQ=0;//拉低
delay_8us();//延时8us
if(val)
{
DQ=1;//写1的话,需要在15us之内拉高
}
//如果写0,则DQ依旧是低电平
delay_52us();
DQ=1;//经过60us,然后释放总线
//DQ高电平
}

bit hal_ds18b20_bit_read()
{
bit tmp=0;
//DQ高电平
DQ=0;
_nop_();_nop_();//拉低电平2us
DQ=1;//释放总线
delay_10us();//时间到此有12us
tmp=DQ;//对总线进行采样
delay_48us();//时间到此有60us
return tmp;
//此时,DQ被ds18b20释放
//DQ高电平
}

//单总线要求从最低有效位开始传送
void hal_ds18b20_byte_write(uchar val)
{
uchar i;
for(i=0;i<8;i++)
{
_nop_();_nop_();//每传送一位,期间至少间隔1us
if(val&(0x01<<i))
hal_ds18b20_bit_write(1);
else
hal_ds18b20_bit_write(0);
}
}

uchar hal_ds18b20_byte_read()
{
uchar tmp=0,i;
for(i=0;i<8;i++)
{
_nop_();_nop_();//每传送一位,期间至少间隔1us
if(hal_ds18b20_bit_read())
tmp=tmp|(0x01<<i);
}
return tmp;
}

//参数length=0:返回精确度为1位小数,此时返回值扩大了10倍
//参数length=1:返回精确度为2位小数,此时返回值扩大了100倍
//参数*flag,只是作为温度正负值标志传出用。0:正;1:负
//注意接收返回值变量需要是int型的
uint hal_ds18b20_get_temp(bit length,uchar * flag)
{
uint val=0;
uchar tmp1,tmp2;
while(!hal_ds18b20_init())
DELAY_500MS();
delay_ms(1);//发指令期间延时比较重要
hal_ds18b20_byte_write(SKIP_ROM);
delay_ms(1);
hal_ds18b20_byte_write(CONVERT_T);
DELAY_1S();//12bit精度情况下,需要750ms转换温度时间
while(!hal_ds18b20_init())
DELAY_500MS();
delay_ms(1);
hal_ds18b20_byte_write(SKIP_ROM);
delay_ms(1);
hal_ds18b20_byte_write(READ_SCRATCHPAD);
delay_ms(1);
tmp1=hal_ds18b20_byte_read();//一共可以读取9字节
tmp2=hal_ds18b20_byte_read();//这里来只读取前两个字节,温度值
//tmp1低字节 tmp2高字节
*flag=(tmp2&0x80)?1:0;//传回温度正负
val=tmp1|tmp2<<8;//组成16位
if(*flag)//温度值是负数,需要取反加一
val=(~val)+1;
//按照12bit测温精度来算,扩大了100倍
return ((uint)(val*6.25+(length?0.5:50)))/(length?1:10);
}

posted @ 2025-11-05 15:19  张大帅哥  阅读(14)  评论(0)    收藏  举报