十二、温度传感器
1.前言知识
1.1DS18B20
1.2单总线
![](https://i-blog.csdnimg.cn/blog_migrate/4debc2e378fcbc44f4c75b9b0ae6e414.png)
![](https://i-blog.csdnimg.cn/blog_migrate/08acbcd7fbe3cca8c57f50019a72ecc7.png)
![](https://i-blog.csdnimg.cn/blog_migrate/614f9728befd706d24839f5c0ec05b0c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6b211c90ceb4fa2426de772411620e91.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a8b5350527c0d7ee8aa006df8eba9043.png)
![](https://i-blog.csdnimg.cn/blog_migrate/301b4d021b7504ee421678fe96a58fe3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5d9ae04068c50585cf41662fc629c087.png)
![](https://i-blog.csdnimg.cn/blog_migrate/db8f82402cf9218fbade6b3c2c2b4ada.png)
2.温度读取程序
2.1要求
能看懂onewire.c文件涉及:初始化,发送接收一位,发送接受一个字节。看懂DS.c涉及:读取温度,开始温度。最后读懂用main.c读取DS.c。
2.2代码
(1)main.c
#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函数局限性)
LCD_ShowChar(2,5,'.'); //显示小数点
LCD_ShowNum(2,6,(unsigned long)(T*10000)%10000,4);//显示温度小数部分
}
}
(2)DS.c
#include <REGX52.H>
#include "OneWire.h"
//DS18B20指令宏定义
#define DS18B20_SKIP_ROM 0xCC
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xBE
/**
* @brief DS18B20开始温度变换
* @param 无
* @retval 无
*/
void DS18B20_ConvertT(void)
{
OneWire_Init();
OneWire_SendByte(DS18B20_SKIP_ROM);
OneWire_SendByte(DS18B20_CONVERT_T);
}
/**
* @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;
}
(3)onewire.c
#include <REGX52.H>
//引脚定义
sbit OneWire_DQ=P3^7;
/**
* @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;//io口电瓶读出放入变量中
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,由于考虑调用函数要4微妙故 生成14微秒
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));
}
}
3.温度预警程序
3.1要求
能看懂main.c,并理解通过定时器扫描按键,在按键使用时不影响主函数(测温度)。了解单总线(OneWire.c)的延时要求会受定时器中断影响,若在其运行中关闭定时器,可以有效解决,但会影响定时精度
3.2代码
(1)main .c
#include <REGX52.H>
#include "LCD1602.h"
#include "DS18B20.h"
#include "Delay.h"
#include "AT24C02.h"
#include "Key.h"
#include "Timer0.h"
#include "Buzzer.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");
Buzzer_Time(100);
}
else if(T<TLow)
{
LCD_ShowString(1,13,"OV:L");
Buzzer_Time(1000);
}
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调用一次按键驱动函数
}
}