在DS18B20中有一个温度传感器(如上图所示),它能感知周围环境温度,并能将温度的结果直接转成数字信号存储起来。温度信号转成数字信号的过程我们可以不用关心。那么数字信号存在哪里了?DS18B20中有一个叫做ScratchPad的存储器,一共9个字节(Byte0-Byte8),数字信号就存储在Byte0_LSM和Byte1_MSB中。其中Byte0为低位,Byte1为高位。事实上,我们的目的就是读出Byte0和Byte1中存储的数字信号而已。由此可见,虽然名字是温度传感器模块,但是在我们的思考过程中丝毫不用关心传感的过程,这一点与前面讲到的LCD1602其实是一样,我们要做的事情只是去读或者写。这就是模块的作用,我们可以把它当作黑盒子,按照它的规则,只要掌握往里面输入,它会产出什么就行了,不需要关注中间的过程。我们把Byte0_LSM和Byte1_MSB组合成一个16bit的数字信号,Byte0_LSB为低位,Byte1_MSB为高位,并给它取个名字叫”T-16bit”,如上图所示。那么如何把这个”T-16bit”的数字信号转换成温度呢?从图中看出,bit11-bit15为S,表示温度的正负,如果S=0,那么温度为正,如果S=1那么温度为负。后面的bit10-bit0,数值每增加一就表示温度增加0.0625°C,举个例子: 假如LSB中读出来的是1100 0011,MSB读出来的是0000 0110,那么LSB和MSB结合起来”T-16bit”就是0000 0110 1100 0011,高5位为0,表示温度为正,余下11位bit10-bit0(110 1100 0011=1731),把1731×0.0625=108.1875,那么测量的温度为+108.1875°C. 如果LSB和MSB结合起来”T-16bit”为1111 1110 1100 0011,那么测量的温度为-108.1875°C. 在”T-16bit”中,我们也可以这样理解,bit0位每加1,表示20×0.0625°C,bit1位每变化1表示21×0.0625°C, 依此类推, bit4位每变化1为24×0.0625°C=1°C.
接着就是Byte2_TH和Byte3_TL报警的功能了,图中Byte2_TH表示设置的最高温度,Byte3_TL表示设置的最低温度。同样S表示温度的正负,如果S=0,那么表示正,如果S=1,那么温度为负。Byte2_TH和Byte3_TL就表示设定温度的范围了。注意这里的Byte2_TH与Byte3_TL与”T-16bit”中的bit11到bit4是对应的,如图中蓝色部分所示。前面讲了,在”T-16bit”中,bit4每变化1为1°C,因此在Byte2_TH与Byte3_TL中的最低位每变化1,表示温度变化1°C. 比如Byte2_TH设定为,01111001(十进制为121),首位为0,表示+121°C, Byte3_TL为01001001(十进制为73),首位为0,表示+73°C .因此温度的设定范围为:73-121°C. 当”T-16bit”中的温度高于/等于+121°C,或者低于/等于73°C时,将触发报警。
接下来就是温度采集的精度设置了,如图中的Byte4_Config所示。bit5和bit6分别为R0和R1,其他几位是固定的。R0和R1分别可以取0或者1,因此可以组合成4种情况,00/01/10/11,分别对应不同的精度,如下表所示:
Byte5-Byte7未给出,Byte8为CRC用与通信错误检测,我们暂且不用管它,我们先考虑简单的部分
通过上面的讲解,相信对DS18B20有了初步的认识,现在再来谈谈它与单片机如何沟通的。DS18B20一共三个引脚,两个电源引脚(电源正负极),一个数据引脚DQ. 其结构图如下所示。黑色壳体朝上,引脚朝下,平面对着自己,左手边第一个引脚为GND. DS18B20的正负极如果接反,可能会烧掉。
可见单片机与DS18B20的沟通只能靠这一根线了,另外这一根线上可以同时接很多个DS18B20. 那么单片机是如何通过这一根线准确的与多个DS18B20沟通的呢?单片机是不知道DQ线上有没有DS18B20的,或者有多个,或者有但是坏了。与人的沟通方式一样,当你不知道屋里情况的时候,会先打个招呼,问下有人没。同样,单片机也一样,先来个初始化,打个招呼,看看DQ线上有没有18B20。怎么样打招呼呢,与前面讲LCD1602一样,当然按照DS18B20的规则。单片机先要把DQ拉低至少480μS, 然后释放DQ,大约在15-60μS后,如果线上有DS18B20并能正常工作,那么DS18B20将DQ拉低并维持60-240μS. 单片机通过检查这个信号,从而判断线上是否有能正常工作的DS18B20,有多少个信号就挂着多少个DS18B20,这个就是DS18B20的规则,具体如下,
初始化后之后就可以与DS18B20沟通了?线上如果有多个DS18B20,你到底是要与哪个沟通呢?因此沟通之前要先来个确定对象。这里我们取最简单一个DS18B20,搞懂了一个,自然会做控制多个DS18B20了。我们来看看初始化代码如何写,
step1:首先DQ拉低,要维持至少480Us,这里我们取500Us保险一点
step2:释放总线(DQ=1),大约等待15-60Us后,DS18B20会把DQ拉低。那么这里到底是选15Us,还是60Us?
step3:此时DS18B20把DQ拉低信号(L_DQ)能持续60-240Us.那么到底选60Us还是240Us?
注意我们这里的目的是要检测出DS18B20是否拉低DQ的信号(L_DQ)。那么在step2中,释放总线(DQ=1)后,我们到底要延时多长时间(Tdelay),去读DQ是否等于0的信号?
我们来个极端假设,最小值和最大值
(1)在step2中, 单片机释放总线(DQ=1),等待15Us(最小值)后,DS18B20把DQ拉低,在step3中,DQ拉低持续了60Us(最小值)
在这种情况下,如果Tdelay>75Us,你就不能检测到L_DQ信号了。因此Tdelay要小于75Us.
(2)在step2中, 单片机释放总线(DQ=1),等待60Us(最大值)后,DS18B20把DQ拉低,在step3中,DQ拉低持续了60/240Us(两种情况)
在这种情况下,如果Tdelay<60Us,你就检测不到L_DQ信号了。比如你设置Tdelay=50Us然后去读DQ,此时DQ仍然为1,为什么?因为60Us后
DS18B20才把DQ拉低。因此Tdelay>60Us.
综上所述,保险时间为:75Us>Tdelay>60Us,代码如下:
void ds18b20_init(void)
{
DQ=0;//总线拉低
delayUs(240); //延时526Us
DQ=1;//释放总线
delayUs(28);//延时66Us(60-75Us之间)
if(DQ==0)
{
LED1=0;//(设置一个LED,如果检测到DQ=0,就把LED1点亮)
}
else
{
LED2=0;
}
delayUs(240);//注意读完DQ后要继续延时480Us
}
DS18B20的ID信号在ROM中,因此这里我们可以跳过读ROM(Skip ROM[CCh]).
接下来就到了与DS18B20沟通的部分了。DS18B20一上电的时候是钝态的,就是说它是被动的,没有指令它就不干活。所以,单片机给DS18B20的第一个指令就是让它开始测量温度(Convert T [44h]). 当DS18B20收到这条指令后干什么呢?这里就要回到前面的我们讲的图1,DS18B20开始感知周围温度并把温度高低转成数字信号存储在ScratchPad中的Byte0和Byte1中。从这里我们知道,我们仅仅通过一条指令Convert T,就让DS18B20开始温度测量并存储,至于这个过程怎么发生的我们并不关心。从这里我们看到,其实控制DS18B20模块与LCD1602模块没有本质上区别,我们要做的事情仅仅是去读懂规则,这些规则来源与它们的芯片手册,那么简单的讲就是阅读理解芯片手册。这一点其实被很多初学者忽略了,不愿意或者不习惯查看芯片手册。DS18B20把温度信号转成数字信号需要一定的时间,因此在发布下一条指令前,我们这里需要检测DS18B20是否仍处于工作之中,如果你想简化,来个延时也行。转换完成后,DS18B20又处于钝态了,这个转换过程不是持续的,因此你想让它再转换,那么就必须再发一次Convert T指令。
那么怎么发指令呢?这个就涉及到‘DS18B20写’的部分了。参考数据手册,找到‘写时序
首先看单片机如何往DS18B20中写0和1,
(1)写0:DQ拉低,维持至少60Us,在大约15-60Us的时候,DS18B20开始接受数据
(2)写1: DQ拉低,延时2Us,然后释放DQ,此时,上拉电阻会把DQ拉高,延时至少60Us,在15-60Us的时候,DS18B20开始接受数据
因此写指令的代码如下
void ds18b20_com(unsigned char mycom)
{
unsigned char temp;
unsigned char i;
for (i=0;i<8;i++)
{
temp=mycom & 0x01;
if (temp==1)
{
DQ=0;
_nop_();
_nop_();
DQ=1;
delayUs(35);
}
else
{
DQ=0;
delayUs(35);
DQ=1;
}
mycom>>=1;
}
}
所以,如果你要发指令 ds18b20_com(0xcch)//skip rom,DS18B20就知道了要跳过读ROM,如果要发指令Convert T, ds18b20_com(0x44h),DS18B20就开始温度转换,并存储在ScratchPad中的前两个字节。
当转换完成后,数据存储在Byte0和Byte1中,下面的任务就是去读Byte0和Byte1了。这里需要注意的是,再单片机每发一次指令之前都要: 初始化->ROM指令->功能指令。这个是芯片手册上规定的。因此在读Byte0和Byte1之前要再初始化一次,Skip ROM([CCh]), 就可以读Byte0和Byte1了。之后将Byte0和Byte1合并,然后转换成温度就可以了。
现在我们来看看如何读DS18B20,同样查看芯片手册,找到读的时序图
DS18B20要把数据发送给单片机,那么DS18B20怎么知道单片机什么时候开始要数据呢?这里同样有个规则,当单片机将DQ拉低,延时1Us,然后释放DQ。此时,DS18B20就知道了,哦,单片机开始要数据了。如果此时DS18B20要给0,就把DQ拉低,相反,如果要写1就让DQ维持高的状态。从单片机发出读数据信息,DS18B20给数据,单片机采集数据,这个过程要在15Us完成,之后需要延时45Us,为保险,我们这里取65Us。两个读之间,至少要有1Us的间隔。因此很容易得出单片机读DS18B20的代码:
unsigned int ds18b20_read(void)
{
unsigned char temp1=0;
unsigned int temp2=0;
unsigned int temp=0;
unsigned char i;
for (i=0;i<8;i++)
{
temp1>>=1;
DQ=0;
_nop_();
_nop_();
DQ=1;
if(DQ==1)
temp1 |=0x80;
delayUs(35);
}
for (i=0;i<8;i++)
{
temp2>>=1;
DQ=0;
_nop_();
_nop_();
DQ=1;
if(DQ==1)
temp2 |=0x80;
delayUs(35);
}
temp2 <<=8;
temp=temp1+temp2;
return temp;
}
通过LCD1602和DS18B20两个例子可以看出,对这两个模块的控制,其实只用了单片机的引脚控制和延时程序。重点和难点其实在理解各模块的控制规则。其实这些只需要通过仔细阅读对应的芯片手册就能找到。而单片机引脚的控制和延时部分与LED的操作并无区别,难怪有人说,如果你把操作LED的过程真正搞懂了,单片机你就学会了一半。后面我们会反复的使用LED作为例子,讲解单片机其它相关内容。
上一篇:初步认识51单片机-2.4单片机C语言模块化编程
下一篇:初步认识51单片机-2.2单片机控制LCD1602液晶显示模块
推荐阅读最新更新时间:2024-03-16 14:48