本文是武汉市海联天下物联网有限公司技术团队内部学习笔记,将详细讲解DS18B20温度显示以及51单片机如何利用单总线与DS18B20通信,DS18B20重要时序掌握。——技术部 张傲
现象描述
使用DS18B20测量温度,并在数码管中显示出来。
DS18B20简介
DS18B20 单线数字温度传感器,即“一线器件”,其具有独特的优点:
(1)采用单总线的接口方式 与微处理器连接时 仅需要一条口线即可实现微处理器与 DS18B20 的双向通讯。 单总线具有经济性好,抗干扰能力强,适合于恶劣环境的现场温度测量,使用方便等优点,使用户可轻松地组建传感器网络。
(2)测量温度范围宽,测量精度高 。DS18B20 的测量范围为 -55 ℃ ~+ 125 ℃ ; 在 -10~+ 85°C 范围内,精度为 ± 0.5°C 。
(3)多点组网功能 多个 DS18B20 并联在惟一的单线上,实现多点测温。
单线总线特点
单总线即只有一根数据线,系统中的数据交换,控制都由这根线完成。单总线通常要求外接一个约为 4.7K—10K 的上拉电阻,这样,当总线闲置时其状态为高电平。
DS18B20的初始化
主机首先发出一个480-960微秒的低电平脉冲,然后释放总线变为高电平,并在随后的480微秒时间内对总线进行检测,如果有低电平出现说明总线上有器件已做出应答。若无低电平出现一直都是高电平说明总线上无器件应答。
作为从器件的DS18B20在一上电后就一直在检测总线上是否有480-960微秒的低电平出现,如果有,在总线转为高电平后等待15-60微秒后将总线电平拉低60-240微秒做出响应存在脉冲,告诉主机本器件已做好准备。若没有检测到就一直在检测等待。
sbit DS = P2^2; //单片机P22引脚与通信总线连接着
void dsreset(void)
{
uint i;
DS=0; //主机发出一个480-960微秒的低电平脉冲
i=103; // 480-960us延时
while(i>0)i--;
DS=1; //释放总线变为高电平
i=4;
while(i>0)i--; //15-60us延时
}
主机发出各种操作命令都是向DS18B20写0和写1组成的命令字节,接收数据时也是从DS18B20读取0或1的过程。因此首先要搞清主机是如何进行写0、写1、读0和读1的。
写周期最少为60微秒,最长不超过120微秒。
写周期一开始做为主机先把总线拉低1微秒表示写周期开始。随后若主机想写0,则将总线置为低电平,若主机想写1,则将总线置为高电平,持续时间最少60微秒直至写周期结束,然后 释放总线为高电平至少1微秒给总线恢复 。而DS18B20则在检测到总线被拉底后等待15微秒然后从15us到45us开始对总线采样,在采样期内总线为高电平则为1,若采样期内总线为低电平则为0。
void tmpwritebyte(uchar dat)
{
uint i; uchar j; bit testb; //bit定义位变量,分配内存地址
for(j=1;j<=8;j++) //因为按位接收,所以每次只存储1位,每字节要8位
{testb=dat&0x01; //从最低位开始赋值,dat是8位要写的内容
dat=dat>>1; //将第7位右移,再赋值给testb
if(testb)
{ DS=0; i++;i++; //写 1的时候总线先拉低1us
DS=1; i=8;while(i>0)i--; //释放总线,写周期至少为60us
}
else
{ DS=0;i=8; while(i>0)i--; //写 0的时候总线先拉低1us,写周期至少60us
DS=1; i++;i++; //写0完成的时候释放总线
}
}
}
对于读数据操作时序也分为读0时序和读1时序两个过程。
读周期是从主机把单总线拉低1微秒之后就得释放单总线为高电平,以让DS18B20把数据传输到单总线上。
作为从机DS18B20在检测到总线被拉低1微秒后,便开始送出数据,若是要送出0就把总线拉为低电平直到读周期结束。若要送出1则释放总线为高电平。
主机在一开始拉低总线1微秒后释放总线,然后在包括前面的拉低总线电平1微秒在内的15微秒时间内完成对总线进行采样检测,采样期内总线为低电平则确认为0。采样期内总线为高电平则确认为1。完成一个读时序过程,至少需要60微秒才能完成。
bit tmpreadbit(void) //这里用到写一位,是把函数作为下一个函数的参{ 数来使用,因为函数做参数使用,所以有返回值
uint i;
bit dat; //局部变量只在函数内部有用,与写函数中的dat不冲突
DS = 0;i++; //读1字节的位数,总线拉低1us
DS = 1;i++;i++; //读1 时,总线释放为高电平
dat= DS; //将DS18b20读到的数据赋值给dat
i=8;while(i>0)i--; //读与写相同的点就是时序的周期都是60us
return (dat); //函数有返回值,可以将函数作参数使用
}
uchar tmpread(void) //读一个字节
{ uchar i,j,dat; //同样的dat变量名重复,但是局部变量只应用于函数内部
dat=0;
for(i=1;i<=8;i++) //一个字节8位,将读一位的函数循环8次,得到一个字节
{j=tmpreadbit(); //这里将读一位的函数返回值赋值给变量j
dat=(j<<7)|(dat>>1); //读出的数据最低位在最前面,这样刚好一个字节在DAT里
}return(dat);
}
经过单线接口访问DS18B20的协议(protocol)如下:
(1) 初始化
(2) ROM操作命令
(3) 存储器操作指令
(4) 处理数据
DS18B20的存储器由一个高速暂存RAM和一个非易失性、电可擦除(E2)RAM组成:
配置寄存器:
出厂设置默认R0、R1为11。就是12位分辨率,也即是1位代表0.0625摄氏度。
ROM操作指令介绍
DS18B20经过转换所得的温度值以二进制补码形式存放在高速暂存存储器的第0和第1个字节。所以当我们只想简单的读取温度值的时候,只需要读取暂存器中的第0和第1个字节就可以了。
简单的读取温度值的步骤:
1:跳过ROM操作
2:发送温度转换命令
3:跳过ROM操作
4:发送读取温度命令
5:读取温度值
void tmpchange(void) //温度转换函数
{
dsreset(); //初始化
delay(1);
tmpwritebyte(0xcc); //跳过ROM
tmpwritebyte(0x44); //温度变换
}
uint tmp() //获取温度值的函数
{ float tt; //定义单精度浮点数类型,一般用来表示带有小数部分的实数
uchar a,b;
dsreset(); //初始化
delay(1);
tmpwritebyte(0xcc); //跳过ROM
tmpwritebyte(0xbe); //读暂存器
a=tmpread(); //低八位赋值给a
b=tmpread(); //高八位赋值给b
temp=b;
temp<<=8; //temp=temp<<8,把高8位数据左移8位
temp=temp|a; //用左移8位的值或上低8位的值,得到的就是温度的值
tt=temp*0.0625; //因为出厂设置分辨率是12,每一位是0.0625摄氏度
temp=tt*10+0.5; //tt是实际温度,但是是浮点数表示,*10转变为整数,+0.5为补偿
return temp; //返回值到主函数中
}
void display(uint temp) //显示函数
{
uchar A1, A2, A2t, A3;
A1 = temp / 100; //百位
A2t = temp % 100; //十位,%为取余运算符
A2 = A2t / 10; //个位
A3 = A2t % 10; //十分位
P0 = table[A1]; //P0是数码管控制脚,显示十位
P3 = 0xef;
delay(3);
P0 = table1[A2]; //显示个位
P3 = 0xdf;
delay(3);
P0 = table[A3]; //显示十分位
P3 = 0xbf;
delay(1);
}