一、DS18B20温度传感器
1.DS18B20介绍
· DS18B20是一种常见的数字温度传感器,其控制命令和数据都是以数字信号的方式输入输出,相比较于模拟温度传感器,具有功能强大、硬件简单、易扩展、抗干扰性强等特点
· 测温范围 :- 55℃到+125℃
· 通信接口:1-Wire(单总线)
· 其它特征:可形成总线结构、内置温度报警功能、可寄生供电
2.引脚及应用电路
本开发板原理图如图:
本开发板的IO口已经外接了一个电阻了(与AT24C02类似),所以这个就没有再单独接一个电阻
3.内部结构框图
parasite power circuit:是寄生电路部分,也就是说DS18B20可以不用通过VDD来进行供电,通过DQ口接电源也可以进行供电。DQ为高电平时,会对电容CPP进行充电,DQ为低电平时,电容会进行供电。
64-BIT ROM:作为器件地址,用于总线通信的寻址
MEMORY CONTROL LOGIC: 存储控制逻辑,用于控制数据的传输
SCRATCHPAD(暂存器):用于总线的数据交互(RAM)
TEMPERATURE SENSOR:模拟温度传感器
ALARM HIGH TRIGGER REGISTER:报警高位触发寄存器,存储温度上限阈值
ALARM LOW TRIGGER REGISTER: 报警低位触发寄存器,存储温度下限阈值
CONFIGURATION REGISTER:配置寄存器,用于设置分辨率(精度)
EEPROM: 用于保存温度触发阈值和配置参数
8-BIT CRC GENERATOR: 8位CRC生成器(CRC,循环冗余码,一种校验码)
存储器结构:
Byte0和Byte1分别存储温度的低位和高位,默认为85
不能直接读写EEPROM,需要先将数据写入存储器,然后通过通信,将数据在写入EEPROM,读出数据同理
Byte5、Byte6和Byte7为保留字段
4.单总线介绍
·单总线(1-Wire BUS)是由Dallas公司开发的一种通用数据总线
·一根通信线:DQ
·异步、半双工
·单总线只需要一根通信线即可实现数据的双向传输,当采用寄生供电时,还可以省去设备的VDD线路,此时,供电加通信只需要DQ和GND两根线
单总线电路规范
·设备的DQ均要配置成开漏输出模式(类似于IIC)
·DQ添加一个上拉电阻,阻值一般为4.7KΩ左右(类似于IIC)
·若此总线的从机采取寄生供电,则主机还应配一个强上拉输出电路(像读EEPROM等耗电操作仅仅靠弱上拉是不行的)
单总线时序结构
·初始化:主机将总线拉低至少480us,然后释放总线,等待15 ~ 60us后,存在的从机会拉低总线60 ~ 240us以响应主机,之后从机将释放总线(实际写程序中,一般取中间值)
·发送一位:主机将总线拉低60 ~ 120us,然后释放总线,表示发送0;主机将总线拉低1 ~ 15us,然后释放总线,表示发送1。从机将在总
线拉低30us后(典型值)读取电平,整个时间片应大于60us
也就是说,在30us这个时间点,读取总线的电平状态,如果是高电平,则表示发送1,如果是低电平,则表示发送0
· 接收一位:主机将总线拉低1~15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us的末尾),读取为低电平则为接收0,读取为高电平则为接收1,整个时间片应大于60us
5.DS18B20操作流程
·初始化:从机复位,主机判断从机是否响应
·ROM操作:ROM指令+本指令需要的读写操作
·功能操作:功能指令+本指令需要的读写操作
ROM操作就是访问64位ROM,功能操作就是访问暂存器
本实验使用SKIP ROM,因为本开发板从机只有一个DS18B20,不需要ROM来进行寻址
6.DS18B20数据帧
· 温度变换:初始化→跳过ROM→开始温度变换
· 温度读取:初始化→跳过ROM→读暂存器→连续的读操作
温度存储格式
其中S是符号位,温度为负,则BIT11-15全为1,温度为正,则BIT11-15全为0
BITO-10为温度数据(补码),举例如下:
7.DS18B20温度读取
现象:利用DS18B20读取温度并将温度显示在LCD1602上
OneWire.c
引脚定义
#include <REGX52.H>
//引脚定义
sbit OneWire_DQ=P3^7;
初始化(温度传感器插上显示0,拔下显示1)
因为延时函数将nop删了,删了之后,生成的480us就没有480us了,为了保证延时不少于480us,故延时了500us
/**
* @brief 单总线初始化
* @param 无
* @retval 从机响应位,0为响应,1为未响应
*/
unsigned char OneWire_Init(void)
{
unsigned char i;
unsigned char AckBit;
OneWire_DQ=1;
OneWire_DQ=0;
i = 247;while (--i); //Delay 500us
OneWire_DQ=1;
i = 32;while (--i); //Delay 70us
AckBit=OneWire_DQ;
i = 247;while (--i); //Delay 500us
return AckBit;
}
发送一位
/**
* @brief 单总线发送一位
* @param Bit 要发送的位
* @retval 无
*/
void OneWire_SendBit(unsigned char Bit)
{
unsigned char i;
OneWire_DQ=0;
i = 4;while (--i); //Delay 10us
OneWire_DQ=Bit;
i = 24;while (--i); //Delay 50us
OneWire_DQ=1;
}
接收一位
/**
* @brief 单总线接收一位
* @param 无
* @retval 读取的位
*/
unsigned char OneWire_ReceiveBit(void)
{
unsigned char i;
unsigned char Bit;
OneWire_DQ=0;
i = 2;while (--i); //Delay 5us
OneWire_DQ=1;
i = 2;while (--i); //Delay 5us
Bit=OneWire_DQ;
i = 24;while (--i); //Delay 50us
return Bit;
}
发送一个字节
/**
* @brief 单总线发送一个字节
* @param Byte 要发送的字节
* @retval 无
*/
void OneWire_SendByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
OneWire_SendBit(Byte&(0x01<<i));
}
}
接收一个字节
/**
* @brief 单总线接收一个字节
* @param 无
* @retval 接收的一个字节
*/
unsigned char OneWire_ReceiveByte(void)
{
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);
void OneWire_SendBit(unsigned char Bit);
unsigned char OneWire_ReceiveBit(void);
void OneWire_SendByte(unsigned char Byte);
unsigned char OneWire_ReceiveByte(void);
#endif
DS18B20.c
宏定义指令
0xCC进行设备寻址(只有一个从机时使用)
0x44温度变换(读取温度前要执行温度变换,将温度值放到暂存器里)
0xBE温度读取(将暂存器里的温度读出)
#include <REGX52.H>
#include "OneWire.h"
//DS18B20指令
#define DS18B20_SKIP_ROM 0xCC
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xBE
DS18B20开始温度变换
/**
* @brief DS18B20开始温度变换
* @param 无
* @retval 无
*/
void DS18B20_ConvertT(void)
{
OneWire_Init();
OneWire_SendByte(DS18B20_SKIP_ROM);
OneWire_SendByte(DS18B20_CONVERT_T);
}
DS18B20读取温度
只需读取暂存器的前两个即可(TLSB)(TMSB)
温度存储格式为4为整数,4为小数。2的4次方是16,且要保持精度,所以除以16.0
/**
* @brief DS18B20读取温度
* @param 无
* @retval 温度数值
*/
float DS18B20_ReadT(void)
{
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;
}
DS18B20.h
#ifndef __DS18B20_H__
#define __DS18B20_H__
void DS18B20_ConvertT(void);
float DS18B20_ReadT(void);
#endif
main.c
(unsigned long)(T*10000)%10000,4):因为小数不能取余再取整unsigned int超过,所以用unsigned long
#include <REGX52.H>
#include "LCD1602.h"
#include "DS18B20.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) //如果温度小于0
{
LCD_ShowChar(2,1,'-'); //显示负号
T=-T; //将温度变为正数
}
else //如果温度大于等于0
{
LCD_ShowChar(2,1,'+'); //显示正号
}
LCD_ShowNum(2,2,T,3); //显示温度整数部分
LCD_ShowChar(2,5,'.'); //显示小数点
LCD_ShowNum(2,6,(unsigned long)(T*10000)%10000,4);//显示温度小数部分
}
}
8.温度报警器
main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "DS18B20.h"
#include "Delay.h"
#include "AT24C02.h"
#include "Key.h"
#include "Timer0.h"
float T,TShow;
char TLow,THigh;
unsigned char KeyNum;
void main()
{
DS18B20_ConvertT(); //上电先转换一次温度,防止第一次读数据错误
Delay(1000); //等待转换完成
THigh=AT24C02_ReadByte(0); //读取温度阈值数据
TLow=AT24C02_ReadByte(1);
if(THigh>125 || TLow<-55 || THigh<=TLow)
{
THigh=20; //如果阈值非法,则设为默认值
TLow=15;
}
LCD_Init();
LCD_ShowString(1,1,"T:");
LCD_ShowString(2,1,"TH:");
LCD_ShowString(2,9,"TL:");
LCD_ShowSignedNum(2,4,THigh,3);
LCD_ShowSignedNum(2,12,TLow,3);
Timer0_Init();
while(1)
{
KeyNum=Key();
/*温度读取及显示*/
DS18B20_ConvertT(); //转换温度
T=DS18B20_ReadT(); //读取温度
if(T<0) //如果温度小于0
{
LCD_ShowChar(1,3,'-'); //显示负号
TShow=-T; //将温度变为正数
}
else //如果温度大于等于0
{
LCD_ShowChar(1,3,'+'); //显示正号
TShow=T;
}
LCD_ShowNum(1,4,TShow,3); //显示温度整数部分
LCD_ShowChar(1,7,'.'); //显示小数点
LCD_ShowNum(1,8,(unsigned long)(TShow*100)%100,2);//显示温度小数部分
/*阈值判断及显示*/
if(KeyNum)
{
if(KeyNum==1) //K1按键,THigh自增
{
THigh++;
if(THigh>125){THigh=125;}
}
if(KeyNum==2) //K2按键,THigh自减
{
THigh--;
if(THigh<=TLow){THigh++;}
}
if(KeyNum==3) //K3按键,TLow自增
{
TLow++;
if(TLow>=THigh){TLow--;}
}
if(KeyNum==4) //K4按键,TLow自减
{
TLow--;
if(TLow<-55){TLow=-55;}
}
LCD_ShowSignedNum(2,4,THigh,3); //显示阈值数据
LCD_ShowSignedNum(2,12,TLow,3);
AT24C02_WriteByte(0,THigh); //写入到At24C02中保存
Delay(5);
AT24C02_WriteByte(1,TLow);
Delay(5);
}
if(T>THigh) //越界判断
{
LCD_ShowString(1,13,"OV:H");
}
else if(T<TLow)
{
LCD_ShowString(1,13,"OV:L");
}
else
{
LCD_ShowString(1,13," ");
}
}
}
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++;
if(T0Count>=20)
{
T0Count=0;
Key_Loop(); //每20ms调用一次按键驱动函数
}
}