1.独特的单总线接口,就需一条线则可实现双向通信(测温)
2.测温范围:-55℃~+125℃,可通过编程设定9—12位分辨率,对应分辨温度分别为0.5、0.25、0.125、0.0625℃。
3.支持多点组网(可连接多个DS18B20温度传感器),多个DS18B20可以并联(3或2线)实现多个组网测温,但注意超过8个要解决好供电问题,否则电压过低会导致传输不稳定,从而数据不准确。
4.工作电压:3.0~5.5V (寄生电源方式下可由数据线供电)
5.在使用过程中不需要外围电路,全部传感元件及转换电路都在芯片内了。(上拉电阻)
6.测温结果直接是数字量输出,单总线串行传送方式,同时可传送CRC校验码(校验数据采集是否正确),具有极强的抗干扰和纠错能力。
7.在9位分辨率时最多在93.75ms内把温度转换为数字,12位分辨率时最多在750ms内把温度值转换为数字。
8.负压特性:电源极性接反时,芯片不会因发热而烧毁, 但不能正常工作。
DS18B20内部结构如图所示,其中与操作有关的是:64位光刻ROM、温度传感器、9个字节的RAM存储器、EEPROM(温度报警寄存器TH和TL、配置寄存器)。
光刻ROM中64位序列号是出厂前就光刻好的,相当地址序列号。排列是低位开始,低8位(产品类型标号),接着48位(自身序列号,)相当于身份证号、最高8位(前面56位的循环亢余校验码)。
如果一条总线挂接多个DS18B20需要MCU(微控制器)通过单总线对多个DS18B20进行寻址。
DS18B20温度传感器进行测温,测温是以16位的二进制形式提供。
存放格式:
16位中 低4位是温度的小数部分、最高5位是温度的正负(全为0为正,全为1为负),中间的7位则是温度的整数部分。小数部分十进制等于16进制乘0.0625。
例子:
注意:如果是负数温度,那么得按位取反+1。
比如数据为 1111 1100 1001 0000
首先低4位都为0.所以温度小数部分为0。最高5位为1,所以温度是负数,所以我们得按位取反才是正确的数, 温度的整数部分为 100 1001——>按位取反得:011 0110 再+1 则结果是 011 0111 ——>0x37(16进制)——>55℃(十进制)
配置寄存器
TM | R1 | R0 | 1 | 1 | 1 | 1 | 1 |
---|
TM:测试模式位,用于设置是在工作方式还是测试模式。在DS18B20出厂时该位设为0,用户不要改动。
R1 R0:分辨率设置
R1 | R0 | 分辨率/位 | 温度最大转向时间/ms |
---|---|---|---|
0 | 0 | 9 | 93.75 |
0 | 1 | 10 | 187.5 |
1 | 0 | 11 | 375 |
1 | 1 | 12 | 750 |
序号 | 指令 | 代码 | 说明 |
---|---|---|---|
1 | 读取ROM | 33H | 读DS18B20的64位序列号(只能适用于总线上只有一个DS18B20) |
2 | 符合 ROM | 55H | 匹配指令,发出64位ROM编码,与总线上一个或多个DS18B20匹配,完全对应则响应。(单个或多个均可) |
3 | 跳过 ROM | CCH | 忽略64位ROM地址,直接向DS18B20发温度变换指令,适用于单片机工作。(适用于单个DS18B20)。 |
4 | 搜索 ROM | F0H | 用于确定挂接在总线上DS18B20个数和 识别64位ROM地址(一般用于多个DS18B20)。 |
5 | 报警搜索命令 | ECH | 执行后搜索温度超过设定值上限或下限才做出响应。 |
6 | 写暂存器 | 4EH | 在该写暂存器指令后向DS18B20的暂存器TH.TL以及配置寄存器中写入数据。 |
7 | 读暂存器 | BEH | 发送该指令后DS18B20将从一个字节开始,依次送出9个字节的内容。如果不想读完所有字节。控制器可以在任何时间发出复位指令中止读取或直接不读取。 |
8 | 复制暂存器 | 48H | 将TH.TL和配置寄存器的内容拷贝到EEPROM中,如果使用寄生电源,总线控制器必须在这条指令发出后10us内启动强上拉并保持至少10ms时间。 |
9 | 启动温度转换指令 | 44H | 温度转换完成后存放在第1个和第2个字节中,如果是寄生电源,总线必须在发出这条指令后的10us内启动强上拉。 |
10 | 复制EEPROM指令 | B8H | 把TH.TL和配置寄存器的值拷贝回暂存器。这种拷贝操作在DS18B20上电时自动执行,上电后,暂存器里就存了有效数据。 |
11 | 读供电方式指令 | B4H | 发给DS18B20后,再发出读时间间隙,后返回电源模式:0为寄生电源、1为外部电源。 |
注:每个指令在写都是 低位在前 高位在后 DS18B20发送也是先发低位,再发高位。
比如发送跳过ROM指令(CCH) 二进制位:1100 1100 。发送顺序为
0、0 、1、1、 0、 0、 1、 1 。
多个DS18B20情况: 对某一个操作时,主机先逐个与DS18B20挂接-搜索ROM——(F0H),发出匹配ROM指令(55H),紧接着提供64位序列号,之后操作就是针对DS18B20的了。
单个DS18B20情况: 不需要搜索ROM指令,读ROM指令以及匹配ROM等操作,直接跳过ROM指令(CCH),温度转换(44H),读温度操作(8EH)。
一、 DS18B20硬件是简单,但软件就比较复杂,特别是时序要求。
二、 连接DS18B20线长限制:部分资料显示:
采用普通信号电缆传输超50m时,测温数据不稳定。
采用带屏蔽层双绞线电缆,正常通讯距离可达到150m。
采用每米绞合次数更多的带屏蔽层双绞线电缆时,通讯距离进一步加长。
三、 距离长了测温要考虑总线分布电容和阻抗匹配问题。
在测温程序设计中,一般如果硬件没什么问题,可以采用延时来跳过检测,但是如果要检测是否有应答要注意不要进入了死循环。
/*DS18B20初始化函数*/
void initDs18b20()
{
DS18B20 = 1;
delay20us();
DS18B20 = 0; //拉低电平
delay480us(); //480us-960us之间均可
DS18B20 = 1; //拉高电平
delay50us(); //等待15us-60us
if(DS18B20 == 1) //如果未响应
{
ds18b20Flag = 1; //DS18B20未响应 标志位置1
}
delay240us();
}
/*DS18B20写命令函数*/
void ds18b20WriteData(unsigned char com)
{
unsigned char i;
for(i=0;i<8;i++)
{
DS18B20 = 0; //拉低电平
//延时至少1us
DS18B20 = com&0x01;
delay60us(); //至少60us直到周期结束
DS18B20 = 1;
com = com>>1; //右移一位
}
}
/*DS18B20读数据函数*/
unsigned char ds18b20ReadData()
{
unsigned char i;
unsigned char dat = 0; //接收数据
for(i=0;i<8;i++)
{
DS18B20 = 0; //拉低电平至少1us
dat=dat>>1; //右移一位
DS18B20 = 1; //释放总线
if(DS18B20 == 1) //如果是1
{
dat = dat|0x80;
}
delay45us(); //保持45us
DS18B20 = 1; //释放总线
}
return dat;
}
每一次进行写命令都记得初始化。
对于单个DS18B20我们可以直接跳过ROM指令 直接温度转换。读取温度
步骤:
1.初始化
2.跳过ROM指令
3.启动温度转换(需要时间)
4.延时(等待温度转换)
5.初始化 (记得每写命令记得需要从初始化开始)
6.读取温度
7.显示温度
#include
#include "delay.h"
#include "lcd1602.h"
sbit DS18B20 = P3^7; //DS18B20引脚
unsigned char ds18b20Flag; //DS18B20是否响应标志位
unsigned char temperature[2] = {
'\0'}; //存放温度整数和小数
unsigned char code array[14] = {
"DS18B20 error!"};
unsigned char code array1[5] = {
"Temp:"};
/*DS18B20初始化函数*/
void initDs18b20()
{
DS18B20 = 1;
delay20us();
DS18B20 = 0; //拉低电平
delay480us(); //480us-960us之间均可
DS18B20 = 1; //拉高电平
delay50us(); //等待15us-60us
if(DS18B20 == 1) //如果未响应
{
ds18b20Flag = 1; //DS18B20未响应 标志位置1
}
delay240us();
}
/*DS18B20写命令函数*/
void ds18b20WriteData(unsigned char com)
{
unsigned char i;
for(i=0;i<8;i++)
{
DS18B20 = 0; //拉低电平
//延时至少1us
DS18B20 = com&0x01;
delay60us();
DS18B20 = 1;
com = com>>1;
}
}
/*DS18B20读数据函数*/
unsigned char ds18b20ReadData()
{
unsigned char i;
unsigned char dat = 0; //接收数据
for(i=0;i<8;i++)
{
DS18B20 = 0; //拉低电平至少1us
dat=dat>>1; //右移一位
DS18B20 = 1; //释放总线
if(DS18B20 == 1)
{
dat = dat|0x80;
}
delay45us(); //保持45us
DS18B20 = 1; //释放总线
}
return dat;
}
/*读取温度函数*/
void readTemperature()
{
unsigned char temperatureLow; //温度低字节
unsigned char temperatureHigh; //温度高字节
unsigned char tempFlag = 0; //温度正负标志位
unsigned char disposeValue; //处理值
initDs18b20(); //初始化DS18B20
ds18b20WriteData(0xcc); //跳过ROM
ds18b20WriteData(0x44); //启动温度转换指令
//延时一段时间
delay800ms();
initDs18b20(); //每一次写命令都需要初始化
ds18b20WriteData(0xcc); //跳过ROM
ds18b20WriteData(0xbe); //读暂存器内容
temperatureLow = ds18b20ReadData(); //获取温度的第一个字节
temperatureHigh = ds18b20ReadData(); //获取温度的第二个字节
disposeValue = (temperatureHigh<<4)|(temperatureLow>>4); //获取温度整数部分
if(disposeValue>=128) //温度是负数
{
disposeValue = ~disposeValue+1;
tempFlag = 1;
}
temperature[0] = disposeValue; //温度整数
temperature[1] = temperatureLow & 0x0f; //温度小数
}
void displayTemperature()
{
write_com(0x85);
write_data((temperature[0]/100) + 0x30); //温度百位
write_com(0x86);
write_data((temperature[0]/10%10) + 0x30); //温度十位
write_com(0x87);
write_data((temperature[0]%10) + 0x30); //温度个位
write_com(0x88);
write_data('.');
write_com(0x89);
write_data((temperature[1]*10/16) + 0x30); //小数第一位
// write_data((tempterature[1]*625/1000));
write_com(0x8a);
write_data((temperature[1]*100/16%10) + 0x30); //小数第二位
// write_data((tempterature[1]*625/100%10));
/* 显示 ℃ */
write_com(0x8b);
write_data(0xdf);
write_com(0x8c);
write_data('C');
}
void main()
{
unsigned char i;
init_lcd(); //初始化1602
write_com(0x80);
for(i=0;i<5;i++)
{
write_data(array1[i]);
}
while(1)
{
readTemperature(); //读取温度
if(ds18b20Flag == 1) //如果初始化错误
{
for(i=0;i<14;i++)
{
write_com(0x80);
write_data(array[i]); //显示DS18B20 error!
}
}
else
{
displayTemperature(); //温度显示
}
}
}
/*延时20us函数*/
void delay20us() //误差 -0.46875us
{
unsigned char a,b;
for(b=1;b>0;b--)
for(a=5;a>0;a--);
}
/*延时45us函数*/
void delay45us() //误差 -0.512152777778us
{
unsigned char a;
for(a=18;a>0;a--);
}
/*延时60us函数*/
void delay60us() //误差 -0.321180555556us
{
unsigned char a;
for(a=25;a>0;a--);
}
/*延时480us函数*/
void delay480us() //误差 -0.399305555556us
{
unsigned char a,b;
for(b=1;b>0;b--)
for(a=218;a>0;a--);
}
/*延时50us函数*/
void delay50us() //误差 -0.086805555556us
{
unsigned char a,b;
for(b=1;b>0;b--)
for(a=19;a>0;a--);
}
/*延时240us函数*/
void delay240us() //误差 -0.199652777778us
{
unsigned char a;
for(a=108;a>0;a--);
}
/*延时800ms函数*/
void delay800ms() //误差 -0.000000000195us
{
unsigned char a,b,c;
for(c=175;c>0;c--)
for(b=10;b>0;b--)
for(a=209;a>0;a--);
}
void delay15us(); //15us
void delay20us(); //20us
void delay480us(); //480us
void delay50us(); //50us
void delay240us(); //240us
void delay45us();//45us
void delay60us(); //60us
void delay800ms(); //800ms
#include
#include "lcd1602.h"
#define LCD P0
sbit E = P2^7;
sbit RS = P2^6;
sbit RW = P2^5;
/******延迟5毫秒函数********/
void delay5ms() //误差 -0.000000000001us
{
unsigned char a,b;
for(b=15;b>0;b--)
for(a=152;a>0;a--);
}
/******LCD1602写命令函数********/
void write_com(unsigned char command)
{
RS = 0;
RW = 0; //高读低写
LCD = command;
delay5ms(); //这里延时最低要30纳秒 我们直接给5ms
E = 1; //使能拉高
delay5ms(); //最低要求延迟150纳秒 我们直接给5ms
E = 0;
}
/******LCD1602写数据函数********/
void write_data(unsigned char dat)
{
RS = 1;
RW = 0;
LCD = dat;
delay5ms(); //这里延时最低要30纳秒 我们直接给5ms
E = 1; //使能拉高
delay5ms(); //最低要求延迟150纳秒 我们直接给5ms
E = 0;
}
/******初始化LCD1602********/
void init_lcd()
{
write_com(0x06); //写入数据后光标自动右移 整屏不移动。 0x40(光标左移 整屏不移动)0x05(左移 整屏右移)0x07(右移 整屏右移)
write_com(0x0c); //开显示功能 无光标 不闪烁
write_com(0x38); //数据总线8位 16X2显示 5*7点阵
write_com(0x01); //清屏 0000 0001
}
void write_com(unsigned char command); //写命令函数
void write_data(unsigned char dat); //写数据函数
void init_lcd(); //初始化LCD1602函数
void delay5ms(); //延时5ms函数