目录
一、功能描述
51单片机模拟1-Wire总线与DS18B20通信,测量温度,并用LCD_1602显示出来。可以设置温度的上限值和下限值,并用AT24C02保存上限值和下限值,具有高温、低温报警闪烁功能。
二、主要模块介绍
2.1 DS18B20介绍
DS18B20 是由 DALLAS 半导体公司推出的一种的“一线总线(单总线)”接口的温度传感器。与传统的热敏电阻等测温元件相比,它是一种新型的体积小、 适用电压宽、与微处理器接口简单的数字化温度传感器。
- 测温范围:-55℃~+125℃
- 通信接口:单总线
-
掉电保护功能 DS18B20 内部含有 EEPROM ,通过配置寄存器可以设定数字转换精度和报警温度,在系统掉电以后,它仍可保存分辨率及报警温度的设定值。
-
可寄生供电
2.1.1 DS18B20外部引脚
- VDD:电源3.0v~5.5v
- GND:电源地
- DQ:单总线接口
2.1.2 DS18B20内部结构
DS18B20 温度传感器的内部存储器包括一个高速的暂存器 RAM 和一个非易失性的可电擦除的 EEPROM,后者存放高温度和低温度触发器 TH、TL 和配置寄存器。
- 64位ROM:器件的地址,用于总线通信寻址。
- 高速暂存器RAM:用于总线的数据交互。
- EEPROM:用于保存温度触发阈值和配置参数。
配置寄存器是配置不同的位数来确定温度和数字的转化,配置寄存器结构如下:
低五位一直都是"1",TM 是测试模式位,用于设置 DS18B20 在工作模式还是在测试模式。在 DS18B20 出厂时该位被设置为 0,用户不需要去改动。R1 和 R0 用来设置 DS18B20 的精度(分辨率),可设置为 9,10,11 或 12 位,对 应的分辨率温度是 0.5℃,0.25℃,0.125℃和 0.0625℃。R0 和 R1 配置如下图:
在初始状态下默认的精度是 12 位,即 R0=1、R1=1。
高速暂存存储器由 9 个 字节组成,其分配如下:
当温度转换命令(44H)发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第 0 和第 1 个字节。存储的两个字节,高字节的前 5 位 是符号位 S,单片机可通过单线接口读到该数据,读取时低位在前,高位在后, 数据格式如下:
2.1.3 DS18B20的温度转换
前面已知,DS18B20的温度值是以二进制补码的形式存储在LS Byte和MS Byte,所以温度值是一个16位的数。温度分辨率是0.0625℃。如果温度>0℃,高5位是"0",把测量的16位数乘以0.0625就是实际温度值;如果温度<0℃,高5位是"1",因为负数的原码与补码不同,由原码=~(补码-1),求得原码,再把原码乘以0.0625就是实际温度值。
- 求测量值的原码(正数原码=补码,负数原码=~(补码-1))
- 实际温度=原码X0.0625。
2.1.4 DS18B20模块程序
- 温度变换:初始化->跳过ROM->开始温度变换
- 温度读取:初始化->跳过ROM->读暂存器中的测量值->转化成实际值
程序如下:
#include"OneWire.h"
#include"LCD1602.h"
#define DS18B20_Skip_ROM 0xcc
#define DS18B20_ConvertT 0x44
#define DS18B20_Read_scratchpad 0xbe
/*
*函数名: DS18B20_Convert_T()
*函数功能: 温度转换命令
*输入: 无
*输出: 无
*/
void DS18B20_Convert_T()
{
OneWire_Init();
OneWire_Sendbyte(DS18B20_Skip_ROM);
OneWire_Sendbyte(DS18B20_ConvertT);
}
/*
*函数名: DS18B20_ReadT()
*函数功能: 读取DS18B20测量的温度值数据
*输入: 无
*输出: T:温度值
*/
float DS18B20_ReadT()
{
unsigned char TLSB,TMSB;
int Temp=0;
float T=0;
OneWire_Init();
OneWire_Sendbyte(DS18B20_Skip_ROM);
OneWire_Sendbyte(DS18B20_Read_scratchpad);
TLSB=OneWire_Receivebyte();
TMSB=OneWire_Receivebyte();
Temp=(TMSB<<8)|TLSB;
Temp&=0x07ff;//把高三位变为0,其他位不变
if(Temp>0){T=Temp*0.0625;} //正数:原码=补码
else
{
T=(~(Temp-1))*0.0625; //负数:原码=~(补码-1)
}
return T;
}
2.2 1-Wire总线介绍
- 单总线(1-Wire BUS)是由Dallas公司开发的一种通用数据总线。
- 一根数据线:DQ。
- 通信方式:异步、半双工
- 单总线只需要一根通信线即可实现数据的双向传输,当采用寄生供电时,还可以省去VDD线,DQ同时供电和传输数据,此时只需要DQ和GND两根线。
2.2.1 单总线初始化时序
- 主机拉低总线480us后,释放总线。
- 从机等待15~60us,再拉低总线60~240us响应主机后,从机释放总线。
- 保证从主机释放总线到初始化结束至少480us。
程序如下:
sbit OneWire_DQ=P3^7;
/*
*函数名: OneWire_Init()
*函数功能: 单总线的初始化信号
*输入: 无
*输出: bit:从机的响应应答
*/
bit OneWire_Init()
{
unsigned char i=0;
bit ack=1;
OneWire_DQ=1;
OneWire_DQ=0;
i = 227;while (--i);//500us
OneWire_DQ=1; //释放总线
i = 25;while (--i);//60us
ack=OneWire_DQ;
i = 158;while (--i);//350us
return ack;
}
2.2.2 单总线写时序(主机发送一位数据)
写"0"时序:
- 主机拉低总线60~120us。
- 从机将在第15us~60us之间采样(从机读取数据)。
- 主机释放总线。
写"1"时序:
- 主机拉低总线1~15us,然后释放总线。
- 从机将在第15us~60us之间采样(从机读取数据)。
- 应保证整个时间片大于60us。
程序如下:
/*
*函数名: OneWire_Sendbite(unsigned char bite)
*函数功能: 主机发送一位数据
*输入: bite:一位数据
*输出: 无
*/
void OneWire_Sendbite(unsigned char bite)
{
unsigned char i=0;
if(bite==0)//发送0
{
OneWire_DQ=0;
i = 30;while (--i);//70us
OneWire_DQ=1;
}
if(bite!=0)
{
OneWire_DQ=0;
i = 2;while (--i);//10us
OneWire_DQ=1;
i = 25;while (--i);//60us
}
}
2.2.3 单总线读时序(主机接收一位数据)
- 主机拉低总线1us~小于15us的时间,然后释放总线。
- 从机控制总线。
- 主机在第15us末尾的地方进行采样。
程序如下:
/*
*函数名: OneWire_Receivebite()
*函数功能: 主机接收一位数据
*输入: 无
*输出: bite:接收的一位数据
*/
unsigned char OneWire_Receivebite()
{
unsigned char i=0;
unsigned char bite=0;
OneWire_DQ=0;
i = 2;while (--i);//5us
OneWire_DQ=1; //释放总线
i = 3;while (--i);//7us
bite=OneWire_DQ;
i = 22;while (--i);//50us
return bite;
}
2.2.4 单总线发送一个字节数据和接收一个字节数据
- 发送一个字节:连续调用8次写时序,依次发送一个字节的8位(低位在前)。
- 接收一个字节:连续调用8次读时序,依次接收一个字节的8位(低位在前)。
程序如下:
/*
*函数名: OneWire_Sendbyte(unsigned char byte)
*函数功能: 主机发送一个字节数据
*输入: byte:一个字节
*输出: 无
*/
void OneWire_Sendbyte(unsigned char byte)
{
unsigned char i=0;
for(i=0;i<8;i++)
{
OneWire_Sendbite(byte&(0x01<<i));
}
}
/*
*函数名: OneWire_Receivebyte()
*函数功能: 主机接收一个字节数据
*输入: 无
*输出: byte:接收的一个字节数据
*/
unsigned char OneWire_Receivebyte()
{
unsigned char i,byte=0;
for(i=0;i<8;i++)
{
if(OneWire_Receivebite()!=0)
{
byte|=0x01<<i;
};
}
return byte;
}
三、测试文件
#include <REGX52.H>
#include"LCD1602.h"
#include"DS18B20.h"
#include"Delay.h"
#include"Timer0_Init.h"
#include"AT24C02.h"
#include"IndependentKey.h"
char THigh=20;
char TLow=-1;
unsigned char set_mode;
unsigned char flash;
void set_T()
{
unsigned char keynum;
keynum=key();
if(keynum!=0)
{
if(keynum==1){set_mode=!set_mode;}
if(keynum==2)
{
if(set_mode==0){THigh++;}//加温度值
else{TLow++;}
}
if(keynum==3)
{
if(set_mode==0){THigh--;}//减温度值
else{TLow--;}
}
if(keynum==4)
{
AT24C02_Writebyte(0x01,THigh);
Delay(5);
AT24C02_Writebyte(0x02,TLow);
Delay(5);
}
}
LCD_ShowNum(2,16,set_mode+1,1);
}
void show_T()
{
LCD_ShowString(2,1,"TH:");
LCD_ShowString(2,8,"TL:");
if(THigh>0){LCD_ShowString(2,4,"+");LCD_ShowNum(2,5,THigh,2);}
else{LCD_ShowString(2,4,"-");LCD_ShowNum(2,5,0-THigh,2);}
if(TLow>0){LCD_ShowString(2,11,"+");LCD_ShowNum(2,12,TLow,2);}
else{LCD_ShowString(2,11,"-");LCD_ShowNum(2,12,0-TLow,2);}
}
int main()
{
float T=0;
LCD_Init();
LCD_ShowString(1,1,"T:");
DS18B20_Convert_T();
Delay(1000);
Timer0_Init();
AT24C02_Writebyte(0x03,THigh);
Delay(5);
AT24C02_Writebyte(0x04,TLow);
Delay(5);
THigh=AT24C02_Readbyte(0x01);
TLow=AT24C02_Readbyte(0x02);
while(1)
{
DS18B20_Convert_T();
T=DS18B20_ReadT();
if(T<0){LCD_ShowString(1,3,"-");}
if(T>0){LCD_ShowString(1,3,"+");}
LCD_ShowNum(1,4,T/1,3);
LCD_ShowString(1,7,".");
LCD_ShowNum(1,8,(unsigned long)(T*10000)%10000,4);
set_T();//设置上下限温度
show_T(); //显示上下限温度
if(flash==0)
{
if(T>=THigh){LCD_ShowString(1,13,"T:H ");} //闪烁警告
if(T<=TLow){LCD_ShowString(1,13,"T:L ");}
}
if(flash!=0)
{
LCD_ShowString(1,13," ");
}
if(T>TLow&&T<THigh){LCD_ShowString(1,13,"T:OK");}
}
}
void Timer0_ISR() interrupt 1
{
static int count1,count2=0;
count1++;
count2++;
TH0=0xfc;
TL0=0x18;
if(count1==20)
{
count1=0;
Key_Loop();
}
if(count2==500)
{
count2=0;
flash=!flash;
}
}
四、效果演示
低温度暂时测试不了。
DS18B20温度传感器