醒醒!,还在睡呢,开始干代码了!
1.简介:
单片机通过OneWire协议与DS18B20通信,最终测出环境温度
OneWire 总线的硬件接口很简单,只需要把 DS18B20 的数据引脚和单片机的一个 IO 口接上
2.DS18B20引脚及说明
说明:GND接地,DQ单引线用于数据的输入,VDD接电源正极(注意正负极不能接反)
3.储存形式
通过编程,将二进制的数字来表示温度
可以看出最高五位为符号位,若全为0则温度为正值,全为1则为负值
最后四位为小数位,最高可以精确到2的-4次方,可以知道每次变化最小变化值位0.0625
例如+25.0625°C是
1*2的-4次方+1*2的0次方+1*2的三次方+1*2的4次方
由于高五位都是0 所以最终结果是+25.0625°C;
二.各个模块的原理
2.1.初始化
在初始化阶段,总线控制器先拉低总线至少480微秒(这里可以拉低500微秒),然后释放总线
进入接收状态,等待 15-60us,然后发出一个由 60-240us低电平信号构成的存在脉冲。
unsigned char OneWire_Init()
{unsigned char i;unsigned char AckBit;OneWire_DQ=1;OneWire_DQ=0;i = 247;while (--i); //Delay 500usOneWire_DQ=1;i = 32;while (--i); //Delay 70usAckBit=OneWire_DQ;i = 247;while (--i); //Delay 500usreturn AckBit;
}
先拉低,延时500微秒等待,再拉高,延时70微秒等待,然后把应答位取出来,再延时500微秒返回应答位,0位应答,1为非应答
这里延时多少可以去用软件来生成代码
2.2.写时序
有两种写时序:写 1 时序和写 0 时序,总线控制器通过写 1 时序写逻辑 1 到DS18B20,写 0 时序写逻辑 0 到 DS18B20,所有写时序必须最少持续 60us
1.发送一位
void OneWire_SendBit(unsigned char Bit)
{unsigned char i;OneWire_DQ=0;i = 4;while (--i); //Delay 10usOneWire_DQ=Bit;i = 24;while (--i); //Delay 50usOneWire_DQ=1;
}
先把总线拉低,延迟10微秒,将bit赋值给DQ(把要发送的数据即bit放在总线上,发0即保持低电平,发1即变成高电平),然后延时50us,最后释放总线;
这里十分巧妙,给的数据是0的话就是0,不拉高总线,是1的话会拉高,延时50us后看变化如果是低电平说明发送的是0,高电平发送1,最后把总线拉高,这里10+50刚好等于60 满足最小的60us
2.发送一个字节(一字节也就是八位)
将上述情况重复八次就好
void OneWire_SendByte(unsigned char Byte)
{unsigned char i;for(i=0;i<8;i++){OneWire_SendBit(Byte&(0x01<<i));}
}
从低到高依次将Byte的每一位取出来。(一般取数据用&,将某个数据置1用 | )
2.3.读时序
总线控制器发起读时序时,DS18B20 仅被用来传输数据给控制器。因此,总线控制器在发出读暂存器指令[BEh]或读电源模式指令[B4H]后必须立刻开始读时序。
2.3.1接收(读)一位:
主机将总线拉低1~15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us末尾,这样成功的概率高) 读取为低电平则为
接收0,读取为高电平则为接收1,整个时间片应大于60us
unsigned char OneWire_ReceiveBit()
{unsigned char i;unsigned char Bit;OneWire_DQ=0;i = 2;while (--i); //Delay 5usOneWire_DQ=1;i = 2;while (--i); //Delay 5usBit=OneWire_DQ;i = 24;while (--i); //Delay 50usreturn Bit;
}
先拉低总线5us,再释放总线5us(此时控制权交给从机),现在时序到了10us,如果读取的数据是0,那么从机就强制拉低总线,如果读取到的数据是1,那就维持高电平,再延时50us满足时序需求.
2.3.2接收(读)一个字节:
和发送一个字节一样
将接收一位重复八次
unsigned char OneWire_ReceiveByte()
{unsigned char i;unsigned char Byte=0x00;for(i=0;i<8;i++){if(OneWire_ReceiveBit()){Byte|=(0x01<<i);}}return Byte;
}
这里定义一个Byte返回值初值为0x00,然后用Byte按位或0x01(从低位到高位)就可以得到Byte了.
上述的发送一位,一个字节;接收一位,一个字节,可以封装到一个文件中,起名为OneWire
下面是OneWire.c
#include <REGX52.H>sbit OneWire_DQ=P3^7;unsigned char OneWire_Init()
{unsigned char i;unsigned char AckBit;OneWire_DQ=1;OneWire_DQ=0;i = 247;while (--i); //Delay 500usOneWire_DQ=1;i = 32;while (--i); //Delay 70usAckBit=OneWire_DQ;i = 247;while (--i); //Delay 500usreturn AckBit;
}void OneWire_SendBit(unsigned char Bit)
{unsigned char i;OneWire_DQ=0;i = 4;while (--i); //Delay 10usOneWire_DQ=Bit;i = 24;while (--i); //Delay 50usOneWire_DQ=1;
}unsigned char OneWire_ReceiveBit()
{unsigned char i;unsigned char Bit;OneWire_DQ=0;i = 2;while (--i); //Delay 5usOneWire_DQ=1;i = 2;while (--i); //Delay 5usBit=OneWire_DQ;i = 24;while (--i); //Delay 50usreturn 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()
{unsigned char i;unsigned char Byte=0x00;for(i=0;i<8;i++){if(OneWire_ReceiveBit()){Byte|=(0x01<<i);}}return Byte;
}
下面是OneWire.h
#ifndef __ONEWIRE_H__
#define __ONEWIRE_H__
unsigned char OneWire_Init();
void OneWire_SendBit(unsigned char Bit);
unsigned char OneWire_ReceiveBit();
void OneWire_SendByte(unsigned char Byte);
unsigned char OneWire_ReceiveByte();#endif
接下来是DS18B20的
ConverT():发送指令,跳过ROM,温度变换
然后是读取温度,在读取函数里面发送完两条指令后,控制权交给从机
这里数据处理比较难理解
剖析:TLSB为第八位,TMSB为高八位
Temp=(TMSB<<8)|TLSB;//将高八位左移八位,把数据整合成16位的;
最后还要把Temp/16
为什么要除以16呢?
这里还是看上面存储形式的那张图片,最低四位为小数位,但是我们整合出来的Temp最低四位是整数位,那么我们只需将Temp右移动4位即可,在二进制里面向右移动一位相当于 /2,那么向右移四位就是 /16了。
#include <REGX52.H>
#include "OneWire.h"#define DS18B20_SKIP_ROM 0xCC
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xBE
void DS18B20_ConvertT()
{OneWire_Init();OneWire_SendByte(DS18B20_SKIP_ROM);OneWire_SendByte(DS18B20_CONVERT_T);
}float DS18B20_ReadT()
{unsigned char TLSB,TMSB;int Temp;float 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;
}
#ifndef __DS18B02_H__
#define __DS18B02_H__void DS18B20_ConvertT();
float DS18B20_ReadT();#endif
最后是主函数测试,这里要用到LCD1602显示屏
#include <REGX52.H>
#include "LCD1602.h"
#include "DS18B02.h"
#include "Delay.h"float 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);}
}
逻辑全在代码里面了,容易体会,要注意最后
LCD_ShowNum(2,2,T,3);
LCD_ShowChar(2,5,'.');
LCD_ShowNum(2,6,(unsigned long)(T*10000)%10000,4);//这里
温度要想显示出四位小数无误,要将float强转为long类型的,但是又不能直接转
所以我们先将整数部分取出显示出来,然后将T*10000%10000就可以取出小数部分了
这里就是DS18B20的基本内容了,还可以实现报警等等功能,在此基础上稍作修改即可,
精心摸鱼(hhh)