写在前面:
在单片机的学习中,传感器作为一种最基本的外设,是初学者必须掌握的。特别是在后面做相关项目“智慧农业、智能家居等”,都会用到相关的传感器;而温度传感器是所有传感器中运用的最多的一种传感器,因此,学好温度传感器也是为后面其他的学习打好基础。
温度传感器相关内容,包括单总线的学习,相关时序的理解以及代码的实现,成功的在LCD1602液晶屏幕上显示了温度数据,并进行了相关操作,包括:阈值的判断、阈值的调节、将其DS18B20同LCD1602、独立按键、I2C总线、EEPROM进行来联合使用,做出相应的操作;
如果需要查看实验现象,或使用源码直接放在文末,百度网盘中!!!
本次我们学习如何使用精度较高的外设DS18B20温度传感器,由于此传感器是单总线接口,因此我们会先介绍单总线的使用;利用51单片机的一个IO口模拟单总线时序与DS18B20通信,将检测的环境温度读取出来;
本节要实现的功能为:
1、系统运行时,LCD1602液晶显示屏显示检测的温度值;
2、系统运行时,判断检测的温度值与设置阈值范围的比较,手动调节阈值范围,并且将设置的阈值同AT24C02联系起来,做到断电保存。
目录
一、DS18B20介绍
DS18B20是一种常见的数字温度传感器,与传统的热敏电阻等测温元器件相比,它的优点是:体积小,适用电压宽、功能强大、硬件简单,抗干扰性强。
DS18B20特点:
1、适应电压范围更宽,电压范围:3.0~5.5V,在寄生电源方式下可由数据线供电。
2、独特的单线接口方式,DS18B20 在与微处理器连接时仅需要一条口线即可实现微处理器与 DS18B20 的双向通信。
3、DS18B20 在使用中不需要任何外围元件,全部传感元件及转换电路集成在形如一只三极管的集成电路内。
4、温范围-55℃~+125℃;
下图为DS18B20的引脚图:
其中,三根接线分别为VDD电源线、GND接地线以及DQ数据线;当使用寄生电源时,将GND与VDD相连,数据线提供电压,此时只需要两个数据线即可满足传感器要求;寄生电源的优点有:1、利用此引脚,远程温度检测无需本地电源;2、缺少电源的情况也可以读ROM;
上图为DS18B20传感器内部框图,下面将逐块介绍:
1、寄生电路(电源)
在上图的左侧为内部供电电路,供电方式共有两种:
1、直接采用5V电源供电:通过VPU与上拉电阻作用,直接同寄生电源电路相连;
2、通信线供电:器件从单总线上取得电源,在信号线为高电平器件,把能量存储在内部电容中,在信号线为低电平时断开此电源,直到信号重信接上寄生电源为止;
2、64位ROM(身份证号)
ROM中64位序列号时出厂前被光刻好的,它可以看作是DS18B20的地址序列。64位中,前8位是产品的类型标号,中间的48位是该DS18B20自身的序列号,最后8位是前面56位的循环冗余校验码(如同身份证最后一位),光刻ROM的作用是使买到的DS18B20各不相同,这样一根总线上可以挂接多个DS18B20。
3、暂存器
暂存器的主要作用为:保证单线通信时数据的完整性,当温度传感器完成温度检测后将该测量的结果暂存于存储器中,通过发现出读暂存器内容的存储器操作命令可以获取到测量的结果。
寄存器内容 | 地址 |
温度值低位 | 0 |
温度值高位 | 1 |
高温限值(TH) | 2 |
低温限值(TL) | 3 |
配置寄存器 | 4 |
保留 | 5 |
保留 | 6 |
保留 | 7 |
CRC校验值 | 8 |
后面是对暂存器中的内容做出说明:
温度传感器(读取温度值)
高低温限值
在DS18B20完成温度转化后,温度值与存储在TH和TL的触发值进行比较,TH与TL的最高位直接对应16为数据的最高位,如果温度测试的值高于TH或低于TL,那么器件内告警标志位将置位。
配置寄存器(确定精度)
配置寄存器是配置不同的位数来确定温度和数字的转化,其结构为:
TM R1 R0 1 1 1 1 1 低5位一直都是“1”,TM为出厂设置。R1 和 R0 用来设置 DS18B20 的精度(分辨率),可设置为 9,10,11 或 12 位,(00.01.10.11)对应的分辨率温度是 0.5℃,0.25℃,0.125℃和 0.0625℃.
CRC
用于产生校验码,同主机进行确认数据字节的传送。
二、单总线介绍
单总线通信,顾名思义即只需要一根通信线即可实现数据的双向传输,采用实时供电时,还可以省去VDD。如何通过一根通信线实现双方通信,这就对时序的要求十分严格,同I2C相同。
电路规范:
1、设备DQ均要配置成开漏输出模式(达到各个设备的相互干扰最小);
2、DQ添加一个上拉电阻;当所有的设备都不工作时,总线被上拉置高电平;
3、若此总线的从机采用寄生供电,则主机还应该配备一个强上拉的输出电路(增强IO口的驱动能力);
电路相关部分,建议参考之前的I2C部分,两者有异曲同工之妙;
CSDNhttps://mp.csdn.net/mp_blog/creation/editor/134191210单总线时序:
1、初始化
单总线上的所有通信都是以初始化序列开始。主机输出低电平,保持低电平 时间至少 480us(该时间的时间范围可以从 480 到 960 微妙),以产生复位脉冲。接着主机释放总线,外部的上拉电阻将单总线拉高,延时 15~60 us,并进入接收模式。接着 DS18B20 拉低总线 60~240 us,以产生低电平应答脉冲,若为低电平,还要做延时,其延时的时间从外部上拉电阻将单总线拉高算起最少要 480 微妙。
unsigned char OneWire_Init()//初始化DS1802
{
unsigned char i;//定义延时函数需要的参数;
unsigned char ACK;//定义从机产生的答应脉冲
OneWire_IO=1;//先将总线拉高;
OneWire_IO=0;//主机输出低电平
i = 227;while (--i);//延时500us
OneWire_IO=1;//主机释放总线,外部上拉电阻将总线拉高;
i = 20;while (--i);//延时50us;
ACK=OneWire_IO;//从机对主机进行应答;
i = 227;while (--i);//延时500us;
return ACK;//返回从机对主机的应答;
}
2、写时序
写时序包括写 0 时序和写 1 时序。所有写时序至少需要 60us,且在 2 次 独立的写时序之间至少需要 1us 的恢复时间,两种写时序均起始于主机拉低总线。
写 1 时序:主机输出低电平,延时 2us,然后释放总线,延时 60us。
写 0 时序:主机输出低电平,延时 60us,然后释放总线,延时 2us。
void OneWire_Sendbit(unsigned char Bit)//写一个位数据;
{
unsigned char i;//定义延时函数需要的参数
OneWire_IO=0; //起始主机拉低总线;
i = 3;while (--i);// 延时10us;
OneWire_IO= Bit;//主机输出低电平或高电平;
i = 22;while (--i);// 延时50us;
OneWire_IO=1;//主机释放总线;
}
3、读时序
单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令后,必须马上产生读时序,以便从机能够传输数据。所有读时序至少需要 60us,且在 2 次独立的读时序之间至少需要 1us 的恢复时间。每个读时序都由主机发起,至少拉低总线 1us。主机在读时序期间必须释放总线,并且在时序起始后的 15us 之内采样总线状态。
unsigned char OneWire_ReceiveBit()//读取一位数据
{
unsigned char Bit;//定义读取的返回值;
unsigned char i;//定义延时所需要的变量;
OneWire_IO=0; //主机发起拉低总线信号;
i = 2;while (--i);//延迟5us
OneWire_IO=1;//主机释放总线;
i = 2;while (--i);//延迟5us;
Bit=OneWire_IO;//主机读取总线的值;
i = 22;while (--i);// 延时50us;
return Bit;//返回主机读取的值;
}
4、发送一个字节
连续调用8次发送一位的时序,依次发送一个字节(低位在前,高位在后);
void OneWire_SendByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
OneWire_Sendbit(Byte&(0x01<<i));//依次发送一个字节的每一位,低位在前;
}
5、接收一个字节
连续调用8次接收一位的时序,依次接收一个字节(低位在前,后位在后);
unsigned char OneWire_ReceiveByte()
{
unsigned char Byte=0x00;
unsigned char i;
for(i=0;i<8;i++)
{
if(OneWire_ReceiveBit()) {Byte|=(0x01<<i);}//依次接收一个字节的每一位,低位在前;
}
return Byte;
}
三、DS18B20的使用
一般通过初始化+ROM指令+功能指令来实现;
初始化:从机复位,主机判断,从机是否响应;
ROM操作:ROM指令+本指令需要的读写操作;
功能操作:功能指令+本指令需要的读写操作;
其中:
SKIP ROM [CCh]
允许总线控制器不用提供 64 位 ROM 编码就使用功能指令;
CONVERT T [44h] (温度转换指令)
用于启动一次温度转换。温度转换指令被执行,产生的温度转换结果数据以 2 个字节的形式被存储在高速暂存器中。
READ SCRATCHPAD [BEh] (读暂存器指令)
这条命令读取暂存器的内容。
温度变化:初始化——跳过ROM——开始温度变化;
void DS18B20_ConverT()//温度转化;
{
OneWire_Init();//初始化
OneWire_SendByte(0xcc);//跳过ROM;
OneWire_SendByte(0x44);//温度变化;
}
温度读取: 初始化——跳过ROM——读暂存器——连续的读操作;
float DS18B20_ReadT()//温度读取
{
unsigned char TLSB,TMSB;//定义相关变量
int Temp; float T;
OneWire_Init();//初始化;
OneWire_SendByte(0xcc);//跳过ROM;
OneWire_SendByte(0xBE);//读暂存器
TLSB=OneWire_ReceiveByte();//读取温度第一个字节;
TMSB=OneWire_ReceiveByte();//读取温度第二个字节;
Temp=(TMSB<<8)|TLSB;//整和温度值;
T=Temp/16.0;//温度换算;
return T;//返回温度值;
}
四、硬件电路
本次实验用到的硬件电路如下:
1、LCD1602液晶显示屏(实验1)
2、DS18B20(实验1)
3、独立按键(实验1、2)
4、EEPROM(实验1、2)
其他电路在之前的博客中都有介绍,大家可以去学习使用:
传感器接口的单总线管脚接至单片机 P3.7 IO 口上,在介 绍单总线的时候我们说过,为了让单总线默认为高电平,通常会在单总线上接上 拉电阻,在图中并没有看到有上拉电阻,这是因为单片机 IO 都外接了 10K 上拉 电阻,当单片机 IO 口连接到传感器的总线管脚时即相当于它们外接上拉电阻, 所以此处可以省去。
五、软件设计
5.1 LCD1602液晶显示屏显示检测的温度值
实验要求:利用DS18B20进行温度检测,并将检测的值进行显示;
源码:
main.c
#include <REGX52.H>//包含51头文件
#include "LCD1602.h"//包含LCD1602头文件
#include "DS18B20.h"//包含DS18B20头文件
float T;//定义浮点数温度
void main()
{
LCD_Init(); //LCD1602初始化
LCD_ShowString(1,1,"Temperature: ");//显示格式
while(1)
{
DS18B20_ConverT();//DS18B20温度转变函数
T= DS18B20_ReadT();//DS18B20温度读取函数
if(T<0)//判断温度的正负(-55至125℃)
{
LCD_ShowString(2,1,"-");
T=-T;
LCD_ShowNumber(2,2,T,3);//进行数据显示整数部分
LCD_ShowString(2,5,".");
LCD_ShowNumber(2,6,(unsigned long)(T*10000)%10000,4);//进行数据显示,小数部分,先乘再取余
}
else
{
LCD_ShowString(2,1,"+");
LCD_ShowNumber(2,2,T,3);/进行数据显示整数部分
LCD_ShowString(2,5,".");
LCD_ShowNumber(2,6,(unsigned long)(T*10000)%10000,4);//进行数据显示,小数部分,先乘再取余
}
}
}
DS18B20.c
#include <REGX52.H>//包含51头文件
#include "one_wire.h"//包含单总线头文件
/**
*@breaf
*@param 无
*@retval 无
*/
void DS18B20_ConverT()
{
OneWire_Init();
OneWire_SendByte(0xcc);
OneWire_SendByte(0x44);
}
/**
*@breaf
*@param 无
*@retval T 返回测量的温度
*/
float DS18B20_ReadT()
{
unsigned char TLSB,TMSB;
int Temp;
float T;
OneWire_Init();
OneWire_SendByte(0xcc);
OneWire_SendByte(0xBE);
TLSB =OneWire_ReceiveByte();
TMSB=OneWire_ReceiveByte();
Temp=(TMSB<<8)|TLSB;
T=Temp/16.0;
return T;
}
one_wire.c
#include <REGX52.H>
//定义端口
sbit OneWire_IO=P3^7;
/**
*@breaf 单总线初始化函数
*@param 无
*@retval ACK 从机响应主机
*/
unsigned char OneWire_Init()
{
unsigned char i;
unsigned char ACK;
OneWire_IO=1;
OneWire_IO=0;//主机将总线拉低:至少480us;用来产生复位脉冲;
i = 227;while (--i);//延时500us;
OneWire_IO=1;//主机释放总线;外部上拉电阻将单总线拉高;
i = 20;while (--i);//延时50us;
ACK=OneWire_IO;//从机对主机的响应;
i = 227;while (--i);//延时500us;
return ACK;
}
/**
*@breaf 单总线发送一位数据
*@param Bit发送的数据
*@retval 无
*/
void OneWire_Sendbit(unsigned char Bit)
{
unsigned char i;
OneWire_IO=0;
i = 3;while (--i);// 延迟10us;
OneWire_IO= Bit;
i = 22;while (--i);// 延迟50us;
OneWire_IO=1;
}
/**
*@breaf 单总线接收一位数据
*@param 无
*@retval Bit总线接受到的数据
*/
unsigned char OneWire_ReceiveBit()
{
unsigned char Bit;
unsigned char i;
OneWire_IO=0; //主机拉低总线;
i = 2;while (--i);//延迟5us;
OneWire_IO=1;//主机释放总线;
i = 2;while (--i);//延迟5us;
Bit=OneWire_IO;//主机读取总线的值;
i = 22;while (--i);// 延迟50us;
return Bit;
}
/**
*@breaf 单总线发送一个字节数据
*@param Byte发送的数据
*@retval 无
*/
void OneWire_SendByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
OneWire_Sendbit(Byte&(0x01<<i));//依次将一个字节的每一位发送给内部;低位在前,高位在后;
}
}
/**
*@breaf 单总线接收一个字节的数据
*@param 无
*@retval Byte接收到的数据
*/
unsigned char OneWire_ReceiveByte()
{
unsigned char Byte=0x00;
unsigned char i;
for(i=0;i<8;i++)
{
if(OneWire_ReceiveBit()) {Byte|=(0x01<<i);}
}
return Byte;
}
实验现象:
温度显示
实验代码:
链接:https://pan.baidu.com/s/1akFSmSJllPG90rdqdka9oQ
提取码:1022
5.2温度传感器相关操作
实验要求:
1、显示温度;
2、显示阈值(高阈值、低阈值);
3、判断温度与阈值的关系,并将判断结果显示;
4、按键控制阈值(K1:高阈值+1;K2:高阈值-1;K3:低阈值+1;K4:低阈值-1);
5、将阈值存于EEPROM,做到断电不丢失;
实验源码:
main.c
#include <REGX52.H>//包含对应头文件
#include "DS18B20.h"
#include "LCD1602.h"
#include "I2C.h"
#include "key.h"
#include "delay.h"
#include "AT24C02.h"
char THigh,Tlow;
unsigned char keynumber;
float Temp,T;
void main()
{
THigh=AT24C02_ReadByte(0);
Tlow=AT24C02_ReadByte(1);
if(THigh>125|| Tlow<-55||THigh<=Tlow)
{THigh=25;Tlow=10;}
LCD_Init();
LCD_ShowString(1,1,"T:");
LCD_ShowString(2,1,"TH:");
LCD_ShowString(2,9,"TL:");
LCD_ShowNumber(2,4,THigh,4);
LCD_ShowNumber(2,12,Tlow,4);
while(1)
{
keynumber=key_scan();
//温度读取及显示;
DS18B20_ConverT();
T=DS18B20_ReadT();
if(T<0)
{
LCD_ShowString(1,3,"-");
T=-T;
LCD_ShowNumber(1,4,T,3);
LCD_ShowString(1,7,".");
LCD_ShowNumber(1,8,(unsigned long)(T*100)%100,3);
}
else
{
LCD_ShowString(1,3,"+");
LCD_ShowNumber(1,4,T,3);
LCD_ShowString(1,7,".");
LCD_ShowNumber(1,8,(unsigned long)(T*100)%100,3);
}
//阈值判断及显示;
if(keynumber)
{
if(keynumber==1)
{
THigh++;
if(THigh>125)
{THigh=125;}
}
if(keynumber==2)
{
THigh--;
if(THigh<Tlow)
{THigh++;}
}
if(keynumber==3)
{
Tlow++;
if(Tlow>THigh)
{Tlow--;}
}
if(keynumber==4)
{
Tlow--;
if(Tlow<-55)
{Tlow=-55;}
}
LCD_ShowSignedNum(2,4,THigh,3);
LCD_ShowSignedNum(2,12,Tlow,3);
AT24C02_WriteByte(0,THigh);
Delay1ms(5);
AT24C02_WriteByte(1,Tlow);
Delay1ms(5);
}
//阈值判断;
Temp=DS18B20_ReadT();
if(Temp>THigh)
LCD_ShowString(1,13,"H ");
else if(Temp<Tlow)
LCD_ShowString(1,13,"L ");
else
LCD_ShowString(1,13,"OK");
}
}
此处,仅有main.c文件同之前不同,其余文件均与前方相同,故不在此进行讲解,源码如下方网盘;
实验现象:
温度传感器相关操作
实验源码:
链接:https://pan.baidu.com/s/1g2cty1YVV3cndDq9Ifm8BA
提取码:1022
总结: 本节我们学习了温度传感器相关内容,包括单总线的学习,相关时序的理解以及代码的实现,成功的在LCD1602液晶屏幕上显示了温度数据,并进行了相关操作,包括:阈值的判断、阈值的调节、将其DS18B20同LCD1602、独立按键、I2C总线、EEPROM进行来联合使用,做出相应的操作;大家看完之后,一定需要自己练习练习,这样才能更好的接收;
最后,创作不易,还请大家多多点赞支持!!!
能力有限,如有错误,欢迎留言讨论!!!