29. DS18B20温度传感器
-
29.1. DS18B20介绍
•DS18B20是一种常见的数字温度传感器,其控制命令和数据都是以数字信号的方式输入输出,相比较于模拟温度传感器,具有功能强大、硬件简单、易扩展、抗干扰性强等特点
•测温范围:-55°C 到 +125°C
•通信接口:1-Wire(单总线)
•其它特征:可形成总线结构、内置温度报警功能、可寄生供电(2个供电引脚,1个数据引脚,一个数据线+一个GND也可以实现工作)
•热敏电阻就是模拟温度传感器,通过AD采集芯片,将模拟的电压值转换为温度,需要AD转换读取温度;
•DS18B20集成了上述所有模拟温度传感器和电路,以及AD转换,应用更简单;
-
29.2. 引脚及应用电路
-
29.3. 内部结构框图
•PARASITE POWER CIRCUIT 寄生供电电路,可以省Vdd,如果用寄生供电,需要给Vpu给一个强上拉,以达到内部供电稳定,开发板不涉及这部分内容;
•64-BIT ROM:作为器件地址,用于总线通信的寻址
•SCRATCHPAD(暂存器RAM):用于总线的数据交互
•EEPROM:用于保存温度触发阈值和配置参数
•TEMPERATURE SENSOR温度传感器
•ALARM HIGH TRIGGER高温报警
•ALARM LOW TRIGGER低温报警
•CONFIGURATION REGISTER配置寄存器,掉电不丢失,精度修改等
•8BIT CRC GENERATOR校验码生成器
- 29.4. 存储器结构
- 下图与上图对应
-
29.5. 单总线介绍
•单总线(1-Wire BUS)是由Dallas公司开发的一种通用数据总线
•一根通信线:DQ
•异步、半双工
•单总线只需要一根通信线即可实现数据的双向传输,当采用寄生供电时,还可以省去设备的VDD线路,此时,供电加通信只需要DQ和GND两根线
•以下照片是单总线的传感器,右侧为温湿度传感器;
-
29.6. 单总线电路规范
•设备的DQ均要配置成开漏输出模式,根据经验判断,同I2C
•DQ添加一个上拉电阻,阻值一般为4.7KΩ左右,同I2C
•若此总线的从机采取寄生供电,则主机还应配一个强上拉输出电路
-
29.7. 单总线时序结构
•初始化:主机将总线拉低至少480us,然后释放总线,等待1560us后,存在的从机会拉低总线60240us以响应主机,之后从机将释放总线
•黑色粗线表示主机将总线拉低的时间480us,弯曲的细黑线表示电阻弱上拉,表示拉高有时间的,不是立即拉高,浅灰色表示从机拉低;
•在从机拉低的时候需要读取拉低的状态,判断从机响应;
-
29.8. 单总线时序结构
•发送一位:主机将总线拉低60120us,然后释放总线,表示发送0;主机将总线拉低115us,然后释放总线,表示发送1。从机将在总线拉低30us后(典型值)读取电平,整个时间片应大于60us
-
29.9. 单总线时序结构
•接收一位:主机将总线拉低1~15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us的末尾),读取为低电平则为接收0,读取为高电平则为接收1 ,整个时间片应大于60us
-
29.10. 单总线时序结构
-
I2C总线高位在前;
-
29.11. DS18B20操作流程
•初始化:从机复位,主机判断从机是否响应
•ROM操作:ROM指令+本指令需要的读写操作
•功能操作:功能指令+本指令需要的读写操作
ROM指令 | 功能指令 |
---|---|
SEARCH ROM [F0h] | CONVERT T [44h] |
READ ROM [33h] | WRITE SCRATCHPAD [4Eh] |
MATCH ROM [55h] | READ SCRATCHPAD [BEh] |
SKIP ROM [CCh] | COPY SCRATCHPAD [48h] |
ALARM SEARCH [ECh] | RECALL E2 [B8h] |
READ POWER SUPPLY [B4h] |
-
29.12. DS18B20数据帧
-
29.13. 温度存储格式
30. DS18B20温度读取&温度报警器
-
30.1. Proteus中建立仿真环境(依旧基于之前的版本基础上改为2.11版)
在元器件库中搜索DS18B20并加入到图纸中,DQ接P37口与开发板一致
此处的开关需要拨动到ON,因为需要LCD1602进行温度显示;
-
30.2. 大体的编程思路
需要写2个模块,一个OneWire单总线模块;
单总线模块包括初始化,写一个位,读一个位,写一个字节,读一个字节
一个DS18B20模块;
包括温度转换模块,温度读取模块 -
30.3. OneWire.c单总线模块程序如下:
#include <REGX52.H>
sbit OneWire_DQ=P3^7;
/*温度初始化*/
unsigned char OneWire_Init()
{
unsigned char i,AckBit;
OneWire_DQ=1; //不确定DQ状态,所以先拉高
OneWire_DQ=0; //再拉低DQ
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; //返回AckBit,初始化结束
}
/*发送一位*/
void OneWire_SendBit(unsigned char Bit) //发送一位数据,省去if判断
{
unsigned char i;
OneWire_DQ=0;
i = 4;while (--i); //delay 10us,数据源自STC ISP
OneWire_DQ=Bit;
i = 24;while (--i); //delay 50us
OneWire_DQ=1; //上述所有delay时间合计60us
}
/*读取或接收一位*/
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; //上述所有delay时间合计60us
}
/*发送一个字节*/
void OneWire_SendByte(unsigned char Byte) //发送一个字节
{
unsigned char i;
for(i=0;i<8;i++)
{
OneWire_SendBit(Byte&(0x01<<i));
}
}
/*读取或接收一个字节*/
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); //发送一位数据,省去if判断
unsigned char OneWire_ReceiveBit(void); //接收一位数据
void OneWire_SendByte(unsigned char Byte); //发送一个字节
unsigned char OneWire_ReceiveByte(void); //接收一个字节
#endif
- 30.4. DS18B20.c与DS18B20.h程序:
#include <REGX52.H>
#include "OneWire.h"
#include "LCD1602.h"
#define DS18B20_SKIP_ROM 0xcc //跳过ROM地址
#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(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(); //上限温度
LCD_ShowBinNum(1,1,TMSB,8); //测试用
LCD_ShowBinNum(1,9,TLSB,8); //测试用
Temp=(TMSB<<8)|TLSB; //高低8位合并
T=Temp/16.0;
return T;
}
#ifndef _DS18B20_H_
#define _DS18B20_H_
void DS18B20_ConvertT(void);
float DS18B20_ReadT(void);
#endif
主程序main.c为:
#include <REGX52.h>
#include "LCD1602.h"
#include "delay_xms.h"
#include "DS18B20.h"
float T;
void main()
{
DS18B20_ConvertT(); //先转化温度
delay_xms(1000); //延时1s,防止第一次出现默认值,给读取温度留时间
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);
}
}
Proteus测试没有问题
-
30.5. 需要在上述程序基础上衍生一个程序,要求如下
- 可以设定最高&最低温度,温度的上下限;
- 需用用独立按键对最高和最低温度的上下限进行调节;
- S1温度上限提高
- S2温度上限降低
- S3温度下限提高
- S4温度下限降低
- 当温度超过上限时,LCD1602右上角显示OV:H
- 当温度低于下线时,LCD1602右上角显示OV:L
- 当温度在设定上下限温度中间位置,显示OV:
- 温度的上下限不超过传感器测温范围-40~125摄氏度
- 同时应考虑独立按键的扫描使用之前修改的中断扫描方式,同时在单总线操作的时候应关闭中断(否则中断干扰了单总线数据传输),温度出错;
- 独立按键用delay扫描同样的问题;
- 上下限温度判断涉及逻辑判断,首先是上限温度高于125,则一直置于125,下限低于-40,则置于-40,如果上限温度低于等于下限温度,则上限温度不可减小了,对于下限温度反之一样;
- 总结:中断扫描用于按键这种实际要求精度不高的场合可以应用,如果是秒表的计时中断+按键中断扫描会影响计时精度,需要注意;
- 设定好的上下限温度存储于AT24C02中,开机直接调取;
-
30.6. 基于上述判断,主程序如下:
#include <REGX52.h>
#include "DS18B20.h"
#include "LCD1602.h"
#include "delay_xms.h"
#include "AT24C02.h"
#include "Key.h"
#include "Timer0.h"
float T,TShow;
char TLow,THigh;
unsigned char KeyNum;
void main()
{
DS18B20_ConvertT();
delay_xms(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:");
Timer0_Init();
while(1)
{
KeyNum=Key();
/*温度读取及显示*/
DS18B20_ConvertT();
T=DS18B20_ReadT();
if(T<0)
{
LCD_ShowChar(1,3,'-');
TShow=-T;
}
else
{
LCD_ShowChar(1,3,'+');
TShow=T; //如果没有这句开机温度为零,TShow没赋值
}
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)
{
THigh++;
if(THigh>125){THigh=125;}
}
if(KeyNum==2)
{
THigh--;
if(THigh<=TLow){THigh++;}
}
if(KeyNum==3)
{
TLow++;
if(TLow>=THigh){TLow--;}
}
if(KeyNum==4)
{
TLow--;
if(TLow<-55){THigh=-55;}
}
}
LCD_ShowSignedNum(2,4,THigh,3);
LCD_ShowSignedNum(2,12,TLow,3);
AT24C02_WriteByte(0,THigh);
delay_xms(5);
AT24C02_WriteByte(1,TLow);
delay_xms(5);
/*OVH,OVL显示*/
if(T>THigh)
{
LCD_ShowString(1,13,"OV:H");
}
else if(T<TLow) //注意T<TLow负数情况出问题
{
LCD_ShowString(1,13,"OV:L");
}
else
{
LCD_ShowString(1,13,"OV: ");
}
}
}
/*会打断主循环的延时进程*/
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++;
if(T0Count>=20) //定时器扫描按键还行,秒表的话会受影响,单总线的问题
{
T0Count=0;
Key_Loop();
}
}
-
需要调用的函数如左侧图片,同时其他函数模块参考之前项目
-
proteus测试如下
Proteus文件百度网盘分享:
链接:https://pan.baidu.com/s/1Brq-m9xH_PJgocxsRE5mPw
提取码:6666