资料
链接:https://pan.baidu.com/s/1z-9P_q-K4msXoJ9s_1sZlA?pwd=65bp
提取码:65bp
详情见第24章。
一、DS18B20 介绍
1.简介
DS18B20 是由 DALLAS 半导体公司推出的一种的“一线总线(单总线)”接口的温度传感器。与传统的热敏电阻等测温元件相比,它是一种新型的体积小、适用电压宽、与微处理器接口简单的数字化温度传感器。
DS18B20 温度传感器具有如下特点:
1、适应电压范围更宽,电压范围:3.0~5.5V,在寄生电源方式下可由数据线供电。
2、独特的单线接口方式,DS18B20 在与微处理器连接时仅需要一条口线即可实现微处理器与 DS18B20 的双向通讯。
3、DS18B20 支持多点组网功能,多个 DS18B20 可以并联在唯一的三线上,实现组网多点测温。
4、DS18B20 在使用中不需要任何外围元件,全部传感元件及转换电路集成在形如一只三极管的集成电路内。
5、温范围-55℃~+125℃,在-10~+85℃时精度为±0.5℃
6、可编程的分辨率为 9~12 位,对应的可分辨温度分别为 0.5℃、0.25℃、0.125℃ 和 0.0625℃,可实现高精度测温。
7、在 9 位分辨率时最多在 93.75ms 内把温度转换为数字,12 位分辨率时最多在 750ms 内把温度值转换为数字,速度更快。
8、测量结果直接输出数字温度信号,以"一根总线"串行传送给 CPU,同时可传送 CRC 校验码,具有极强的抗干扰纠错能力。
9、负压特性:电源极性接反时,芯片不会因发热而烧毁,但不能正常工作。
DS18B20 实物图如下图所示:
2.DS18B20引脚排列及说明
从 DS18B20 引脚图可以看到,当我们正对传感器切面(传感器型号字符那
一面)时,传感器的管脚顺序是从左到右排列。管脚 1 为 GND,管脚 2 为数据DQ(Data Queue,传输数据的总线),管脚 3 为 VDD。如果把传感器插反,那么电源将短路,传感器就会发烫,
很容易损坏,所以一定要注意传感器方向,通常我们在开发板上都会标出传感器
的凸起出,所以只需要把传感器凸起的方向对着开发板凸起方向插入即可。
使用的时候只需要把 DS18B20 的数据引脚和单片机的一个 IO 口接上就可以了。
二、DS18B20的时序
1.单片机外部设备与总线的关系
单总线结构:CPU内部采用的是“一条总线”贯穿整个芯片的结构,所有的外部设备都挂在这条总线上,数据在总线上传输,CPU通过访问总线上的数据就可以访问到所有的外部设备了。在总线上传递的数据通常是以高低电平的形式呈现的。
本节讨论的DS18B20作为外部设备也不例外。
形象一点说:总线就像是一条大马路,外部设备就像马路两旁的商户。
2.时序
(1)定义
单片机时序是指单片机执行指令时应发出的控制信号的时间序列。这些控制信号在时间上的相互关系就是CPU的时序。它是一系列具有时间顺序的脉冲信号。
我的理解:时序就是程序控制单片机及其外部设备运行的节奏。运行的时候节奏不能出错,否则外部设备就达不到我们控制的效果。
(2)分类
DS18B20 时序包括如下几种:初始化时序、写(0和1)时序、 读(0和1)时序。 DS18B20 发送所有的命令和数据都是字节的低位在前。
3.初始化时序
(理论上是开始拉高就等待、延时,江协科技认为的是彻底拉高电位到1。可以把电平弹高的时间(几乎为0)忽略不计。我也这样认为算了,这样代码后面好设计一点。)
关键的是:主机检测的时候一定要在从机掌握总线并拉低电平之后,否则主机检测到高电平,时序就会出错。
(1)老大(单片机)发出信号唤醒小弟(外部设备:DS18B20)
刚开始总线是高电平。主机(单片机)首先发出一个480~960us的低电平脉冲(复位),然后再拉高电平。发出这个低电平的目的就是告诉DS18B20,把他叫醒,跟他说:“做好准备,我要跟你通信了。”
然后延时15~60us,才进入检测状态。
(2)小弟回应老大
作为从机的DS18B20上电后,就一直检测总线上是否有480到960us的低电平出现。当DS18B20接收到信号后,发出60~240us的低电平回应单片机,即拉低总线 60到240 us。告诉他:“我已经做好准备了。”因为拉的是低电平,还要做延时,其延时的时间从外部上拉电阻将单总线拉高算起最少要480us。
(这个应该是规定)
注意:主机为什么要等待(延时)15~60us才开始检测总线电平的变化呢?
因为延时15~60微秒是为了给予DS18B20足够的准备时间,以便它能够正确地检测到总线的释放(由低电平转为高电平),并做出响应(拉低总线产生应答脉冲),从而保证单总线通信的正确初始化。如果你立刻去检测,他可能还没发出低电平响应,或者发出的低电平响应不完整。
特别要注意的是:理论上主机是可以一直在检测主线上的数据,但是实际上他并不会那么做,主要是考虑到资源占用和效率,而是当有必要的时候才去检测。这里就是他已经预知了DS18B20最快的应答,他赶在应答之前一点点去检测即可。
(3)小节
至此,初始化时序完成。
从主机的角度看初始化过程:主机将总线拉成低电平至少保持480us后释放总线,延时15~60us后检测60到240us时间内检测总线是否为低电平(检测某个点是否为低电平就OK了,不必检测完),再延时240us保持时序的完整。
说人话:初始化就是老大叫小弟做好工作准备,并收到小弟回复的过程。
4.写(0和1)时序(主机发送数据给从机)
(1)时间限制
写时序(周期)最少为60us,最长不超过120us。
(2)步骤
①写0
写时序的开始,主机先把总线拉低至少1us,表示写时序开始。
随后若主机想写0,则继续拉低至少60us直至写时序结束,然后释放总线为高电平。
②写1
若主机想写1,在拉低总线1us后就释放总线为高电平,一直到写时序结束。
作为从机的DS18B20在检测到总线被拉低1后,等待15us,从15到45us对总线采样。(采样的典型时间在采样开始第15us),在采样时间内,若检测到总线为高电平则认为主机发送了1;若检测到总线为低电平则认为主机发送了0。
5.读(0和1)时序(从机发送数据给主机)
(1)时间限制
同样地,读时序(周期)最少为60us,最长不超过120us。
(2)步骤
①读0
读时序的开始,主机先把总线拉低至少1us,然后释放总线,表示读时序的开始。
主机释放总线后,若DS18B20发送0,即主线上送过来的数据是0,则仍保持把总线拉低,并持续至少从都周期开始的15us,然后释放总线弹回高电平。
②读1
若DS18B20发送1,即从机从主线上送过来的数据是1,则在主机释放总线后,总线迅速被拉至高电平。
无论读0或者读1,主机都须在15us内检测总线电平的高低。若检测到总线电平为低,则表示DS18B20发过来的是0;若检测到总线电平为高,则表示DS18B20发过来的是1。
6.读写时序小节
(1)无论是读还是写,都是以主机把总线拉低至少1us开始。
(2)无论是写0还是读0,都是以总线释放至少1us结束。对于写0 尤其需要注意(此时主机控制总线,设计到写代码),对于读0过程由DS18B20负责拉高(其实结束前早已拉高)。
7.归纳DS18B20的测温步骤
8.DS18B20的使用注意事项
三、实验
1.实验目标
(1)参考课件“【例10-1】利用DS18B20和LED数码管实现单总线温度测量系统”和普中实验板说明书第24章“24.3 软件设计:使用DS18B20温度传感器、数码管显示检测的温度值”的项目案例,分别在Proteus和普中开发板实物上完成 DS18B20 的环境温度采集。
(2)将上述实验成功的代码在Keil中进行仿真运行,使用虚拟逻辑分析仪显示单片机与DS18B20的DQ数据线连接引脚的波形,分析其“初始化–>写—>读”总线时序,与原理进行对比分析: 1)判断其是否符合技术要求;2)故意在代码中把时序弄错(改延时delay的延时值),再重新运行之前的实验,看DS18B20是否能正确相应并返回温度。
2.课件参考
(1)代码
#include "reg51.h"
#include "intrins.h"
#define uchar unsigned char
#define uint unsigned int
#define out P0
sbit smg1=out^4;
sbit smg2=out^5;
sbit DQ=P3^7;
void delay5(uchar);
void init_ds18b20(void);
uchar readbyte(void);
void writebyte(uchar);
uchar retemp(void);
void main(void)
{
uchar i,temp;
delay5(1000);
while(1)
{
temp=retemp();
for(i=0;i<10;i++)
{
out=(temp/10)&0x0f;
smg1=0;
smg2=1;
delay5(1000);
out=(temp%10)&0x0f;
smg1=1;
smg2=0;
delay5(1000);
}
}
}
void delay5(uchar n)
{
do
{
_nop_();
_nop_();
_nop_();
n--;
}
while(n);
}
void init_ds18b20(void)
{
uchar x=0;
DQ =0;
delay5(120);
DQ =1;
delay5(16);
delay5(80);
}
uchar readbyte(void)
{
uchar i=0;
uchar date=0;
for (i=8;i>0;i--)
{
DQ =0;
delay5(1);
DQ =1;
date>>=1;
if(DQ)
date|=0x80;
delay5(11);
}
return(date);
}
void writebyte(uchar dat)
{
uchar i=0;
for(i=8;i>0;i--)
{
DQ =0;
DQ =dat&0x01;
delay5(12);
DQ = 1;
dat>>=1;
delay5(5);
}
}
uchar retemp(void)
{
uchar a,b,tt;
uint t;
init_ds18b20();
writebyte(0xCC);
writebyte(0x44);
init_ds18b20();
writebyte(0xCC);
writebyte(0xBE);
a=readbyte();
b=readbyte();
t=b;
t<<=8;
t=t|a;
tt=t*0.0625;
return(tt);
}
(2)Proteus仿真
3.自己写
(1)代码
//main.c文件
#include <REGX52.H>
#include "LCD1602.h"
#include "OneWire.h"
#include "DS18B20.h"
#include "Delay.h"
float T; //全局变量T
void main()
{
DS18B20_ConvertT(); //上电先转换一次温度,防止第一次读数据错误
Delay(1000); //等待转换完成
LCD_Init();
LCD_ShowString(1,1,"Temperature:");
while(1)
{
DS18B20_ConvertT();
T=DS18B20_ReadT();
if(T<0)
{
LCD_ShowChar(2,1,'-');
T=-T;
}
else
{
LCD_ShowChar(2,1,'+');
}
LCD_ShowNum(2,2,T,3);
LCD_ShowChar(2,5,'.');
LCD_ShowNum(2,6,(unsigned long)(T*10000)%10000,4);
}
}
//DS18B20.c文件
#include <REGX52.H>
#include "OneWire.h"
#include "DS18B20.h"
#define DS18B20_SKIP_ROM 0xCC
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xBE
void DS18B20_ConvertT(void)
{
OneWire_Init();
OneWire_SendByte(DS18B20_SKIP_ROM);
OneWire_SendByte(DS18B20_CONVERT_T);
}
float DS18B20_ReadT(void)
{
unsigned char TLSB,TMSB; //分别是温度的第一个字节和第二个字节
int Temp;
float T; //局部变量T
OneWire_Init();
OneWire_SendByte(DS18B20_SKIP_ROM);
OneWire_SendByte(DS18B20_READ_SCRATCHPAD);
TLSB=OneWire_ReceiveByte();
TMSB=OneWire_ReceiveByte();
Temp=(TMSB<<8)|TLSB;
T=Temp/16.0;
return T;
}
//OneWire.c文件
#include <REGX52.H>
#include "DS18B20.h"
#include "OneWire.h"
sbit OneWire_DQ=P3^7;
unsigned char OneWire_Init(void)
{
unsigned char i;
unsigned char AckBit;//Acknowledgement Bit的缩写,确认位,主机用来接收总线的返回值(电平值)
OneWire_DQ=1; //开始总线是高电位
OneWire_DQ=0; //将总线电位拉低
i=247;while(--i); //最低延时480us,我们设计延时500us
OneWire_DQ=1; //释放总线再次将它拉高
i = 32;while (--i); //给他延时70us,这段时间是给DS18B20做出反应的时间
AckBit=OneWire_DQ; //此时恰好读取低电平
i=247;while(--i); //再延时500us,确保将剩下的480us走完,实际上已经走了70us了
return AckBit;
}
void OneWire_SendBit(unsigned char Bit) //写时序的函数,Bit为0或1
{
unsigned char i;
OneWire_DQ=0;
i=4;while(--i);//延时10us
OneWire_DQ=Bit; //给总线赋值
i=24;while(--i);//延时50us
OneWire_DQ=1;//释放总线弹回高电平
}
unsigned char OneWire_ReceiveBit(void) //读时序的函数
{
unsigned char i;
unsigned char Bit;
OneWire_DQ=0;
i=2;while(--i); //延时5us
OneWire_DQ=1;
i=2;while(--i); //延时5us
Bit=OneWire_DQ; //采样总线电平
i=24;while(--i); //延时50us
//我们不需要写代码释放总线,我们写的代码只控制总机,在总机接受信号的时候,从机后面控制总线,它后面会自己释放
return Bit;
}
void OneWire_SendByte(unsigned char Byte) //总线发送字节,从低位到高位
{
unsigned char i;
for(i=0;i<8;i++)
{
OneWire_SendBit(Byte&(0x01<<i));
}
}
unsigned char OneWire_ReceiveByte(void)
{
unsigned char i;
unsigned char Byte=0x00;
for(i=0;i<8;i++)
{
if (OneWire_ReceiveBit())
{
Byte|=(0x01<<i);
}
}
return Byte;
}
(2)实验现象
Week8_01
(3)波形分型
两者是一样的,因为我sbit OneWir_DQ=P3.7。
波形按照代码的设计起伏,符合要求。
(4)改变时序再查看结果
我直接在初始化时序的时候,把里面的延时部分,全部去掉。可以预料到,这下主机就会出问题,因为它检测太快了DS18B20根本没反应过来,他就开始检测了。所以后续的一切都会出问题。
波形如下:(和之前的比较,很明显没有长时间下拉电平做提示了)
现象如下:直接在重庆的夏日测出负温度,显然是错误的。
四、总结
这次学写了DS18B20温度传感器的用法,了解了时序有关的概念。我体会到在单片机学习中,特别是在代码设计过程中,对时序的考虑是一个非常重要的内容。往往有时候你的代码看起来合情合理,但是因为没有考虑到函数调用所花费的时间、外部设备反应所需要的时间等…会最终导致实验的结果与预期相差甚远。由于了解得比较浅,我在接下来的学习中还要好好把握时序的。
五、参考资料
1.https://www.bilibili.com/video/BV1Mb411e7re/?p=29&spm_id_from=333.880.my_history.page.click
2.https://blog.csdn.net/2201_75518371?type=blog