模块思路分析:
第一 它既然是温度计,肯定是对外界的冷暖由感知,并且又是数字温度计,也就是说这种感知会转换成一种数字信号,也就是0和1.
第二 它既然是转换成数字信号 ,那么它是存储在那里的?所以有一个寄存器 它的名字就是 SCRATCHPAD 你就知道它是一个SRAM 就行。既然数字信号就存储在它里面,我们就要从这个里面读取出来。先看看 SCRATCHPAD 的内部分布
根据它的内部分布我们知道,这些数据就储存在 byte0 和 byte1 里面,也就是要读取 byte0 和 byte1 里面的数据,再来看看 byte0 和 byte1 的内部分布情况:
每一个又都有八位字节组成,MS Byte 为高八位 其中前五位代表符号位 若S= 0 代表 温度为正值 S= 1 代表 温度为负值。之后的 bit10 ~ bit0 表示 温度的数值 用对应的数值乘以 对应的值 然后相加就可以得到温度的值,但是此方法只能用于计算温度为正值的时候,因为这些数据是用补码表示的 正值的时候时候,补码与原码是相同的,可以直接计算,当为负值的时候,补码与原码是不相同的。
然后就是怎么读,观察芯片手册
READ SCRATCHPAD [BEh] This command allows the master to read the contents of the scratchpad. The data transfer starts with the least significant bit of byte 0 and continues through the scratchpad until the 9th byte (byte 8 – CRC) is read. The master may issue a reset to terminate reading at any time if only part of the scratchpad data is needed.
翻译:
READ SCRATCHPAD[BEh]
该命令允许主机读取暂存器的内容。数据传输从字节0的最低有效位开始,一直到暂存器,直到读取第9个字节(字节8 – CRC)。如果只需要暂存器数据的一部分,则主机可以随时发出复位以终止读取。
也就是说,当单片机向DS18B20发送 一个字节(0xbh), 这个时候 DS18B20 会向 单片机发送 一个含有九个字节的数据,也就是 SCRATCHPAD,我们只需要读取其中的前两个字节就行。
DS18B20_write_(0xbe);//read scratchpad
Low = DS18B20_read_();//read byte0
High = DS18B20_read_();//read byte1
但是 SCRATCHPAD 是 功能指令,
DS18B20功能指令需要进行的操作:
事务序列访问DS18B20的事务序列如下:
步骤1.初始化
步骤2. ROM命令(随后进行任何必需的数据交换)
步骤3. DS18B20功能命令(随后进行任何必要的数据交换)
请务必遵循每次访问DS18B20时都会执行此序列,因为如果序列中的任何步骤丢失或顺序混乱,DS18B20均不会响应。该规则的例外是Search ROM [F0h]和Alarm Search [ECh]命令。发出这两个ROM命令中的任何一个后,主机必须按顺序返回步骤1。
既然 SCRATCHPAD 是 功能指令,就要服从以上的三个步骤,
它要执行ROM指令,但是ROM指令又有很多,那会是那个?
SKIP ROM [CCh]
主机可以使用该命令同时寻址总线上的所有设备,而无需发送任何ROM代码信息。例如,主机可以通过发出Skip ROM命令和随后的Convert T [44h]命令,使总线上的所有DS18B20都同时执行温度转换。请注意,只有在总线上只有一个从设备时,Read Scratchpad [BEh]命令才能跟随“跳过ROM”命令。在这种情况下,可以通过允许主机从机读取而不发送设备的64位ROM代码来节省时间。如果有多个从站,则跳过ROM命令后跟Read Scratchpad命令将导致总线上的数据冲突,因为多个设备将尝试同时传输数据。
也就是说 我们可以发送 SKIP ROM
接下来就是初始化
DS18B20_init();// 初始化
DS18B20_write_(0xcc);//skip rom
DS18B20_write_(0xbe);//read scratchpad
Low = DS18B20_read_();//read byte0
High = DS18B20_read_();//read byte1
但是如果你用上述代码去读取温度,会发现温度是85℃。
因为根据芯片手册当单片机上电时,DS18B20会处于一种低功耗的状态,此时DS18B20并没有开始工作,但是此时你要是去读取温度,会得到DS18B20的默认值,85 ° C. 想要让DS18B20开始工作,必须发送一个DS18B20 功能指令 CONVERT T [44h] 同样它也要 有 ROM指令(SKIP ROM)和初始化值令(inint)
总的代码
DS18B20_init();// 初始化
DS18B20_write_(0xcc);// 初始化
DS18B20_write_(0x44);// 发送功能指令 convent t
delay750ms();// 此时间与测量精度有关,但是最大时间为 750ms
DS18B20_init();// 初始化
DS18B20_write_(0xcc);//skip rom
DS18B20_write_(0xbe);//read scratchpad
Low = DS18B20_read_();//read byte0
High = DS18B20_read_();//read byte1
现在开始写各个函数的代码
先看初始化代码
此处分析省去 代码
bit DS18B20_inint()
{
bit flag;
DS = 0; // DS18B20 总线
delay500us();
DS = 1;
delay68us();
flag = DS;
delay500us();
return flag;
}
写一个字节代码
void DS18B20_write(uchar dat)
{
uchar i;
for(i=0;i<8;i++)
{
DS = 0;
_nop_();
DS = dat & 0x01;
delay_us(10);//延时76.95
DS = 1;
_nop_();
dat >>= 1;
}
}
读一个字节
uchar DS18B20_read()
{
uchar i,j,dat;
for(i=0;i<8;i++)
{
DS = 0;
_nop_();
DS = 1;
_nop_();
j = DS;
delay_us(10);
DS = 0;
_nop_();
dat = (j<<7)|(dat>>1);
}
return dat;
}
注:采集的温度数据为小数,怎么显示在数码管上?
答:可以根据要求保留的位数,做乘10 运算,然后将数字显示在数码管上,只不过 有一位是需要显示小数点
显示小数点的方法:
P0 = table[Value%100/10]|0x80; //在具体位数后面 或上 0x80
源代码
#include <reg52.h>
#include <intrins.h>
#define MAIN_Fosc 11059200UL //宏定义主时钟HZ
typedef unsigned char INT8U;
typedef unsigned char uchar;
typedef unsigned int INT16U;
typedef unsigned int uint;
sbit DS = P2^2; //DS18B20单总线
sbit DU = P2^6; //数码管段选
sbit WE = P2^7; //数码管位选
/*====================================
共阴极数码管段选码
====================================*/
uchar code table[]={
//0 1 2 3 4 5 6 7 8
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,
//9 A B C D E F - . 关显示
0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00
};
uchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码
void Delay_Ms(INT16U ms)
{
INT16U i;
do{
i = MAIN_Fosc / 96000;
while(--i); //96T per loop
}while(--ms);
}
/*us延时函数,执行一次US--所需6.5us进入一次函数需要11.95us*/
void delay_us(uchar us)
{
while(us--);
}
void Display(INT16U Value) //注意由于需要显示的数大于一个字节所有形参需为int型
{
//------------------------------
DU = 0; //关闭段选
P0 = table[Value / 100]; //数码管显示百位
DU = 1; //打开段选
DU = 0; //关闭段选
WE = 0; //关闭位选
P0 = T_COM[0]; //第一位数码管
WE = 1; //打开位选
WE = 0; //关闭位选
Delay_Ms(3);
//-------------------------------
DU = 0;
P0 = table[Value%100/10]|0x80; //显示十位
DU = 1;
DU = 0;
WE = 0;
P0 = T_COM[1]; //第二位数码管
WE = 1;
WE = 0;
Delay_Ms(3);
//-------------------------------
DU = 0;
P0 = table[Value%10]; //显示个位
DU = 1;
DU = 0;
WE = 0;
P0 = T_COM[2]; //第三位数码管
WE = 1;
WE = 0;
Delay_Ms(3);
}
bit DS18B20_init()
{
bit flag;
DS = 0;
delay_us(75);
DS = 1;
delay_us(9);
flag = DS;
delay_us(75);
return flag;
}
void DS18B20_write(uchar dat)
{
uchar i;
for(i=0;i<8;i++)
{
DS = 0;
_nop_(); //延时2us 左右
DS = dat & 0x01; // 从dat的最低位开始发送
delay_us(10);//延时76.95
DS = 1;
_nop_();
dat >>= 1;
}
}
uchar DS18B20_read()
{
uchar i,j,dat;
for(i=0;i<8;i++)
{
DS = 0;
_nop_();
DS = 1;
_nop_();
j = DS; // 采样 将数据放在J里面 再J的最低位
delay_us(10);
DS = 0;
_nop_();
dat = (j<<7)|(dat>>1); //将J 的最低位左移到最高位 然后给 dat
}
return dat;
}
void main ()
{
uint i;
uchar low,high;
while(1)
{
DS18B20_init();
DS18B20_write(0xcc);
DS18B20_write(0x44);
delay_us(120);
DS18B20_init();
DS18B20_write(0xcc);
DS18B20_write(0xbe);
low = DS18B20_read(); // 温度的第一个字节
high = DS18B20_read(); // 温度的第二个字节
i = high;
i <<= 8;
i |= low;
i = i * 0.0625 * 10 + 0.5; // 因为数据是小数,处理的时候,可以根据保留地位数
Display(i);
}
}