目录
一、具体实现功能
1.测量温度范围在-55°-125°,精度为0.1度;
2.通过LCD1602动态显示温度值;
3.能通过独立按键设置温度上下限;
4.根据温度上下限设置,具备声光报警功能;
5.能在同一个IO中连接4个DS18b20,通过DS18B20中独特的序列号进行识别和检测温度;
6.能通过矩阵键盘切换DS18B20显示;
二、前期硬件准备
该系统设计需要准备51单片机(芯片这里用了STC89C52RC)、LCD1602、4个DS18B20、面包板、4.7KΩ电阻、杜邦线若干根。
三、电路设计
1.多个DS18B20并联在一起
可根据需要并联多个,原理相同,GND接GND,VDD接在单片机 5V或3.3V或VCC上,将VCC与DQ口用4.7KΩ电阻串联。(但是我之前按照数据手册就是下面这个图片的接口接结果DS18B20烧了,最后我按照DS18B20平的那一面面对自己,最左边接GND、最右边接5V,有懂的大佬吗欢迎评论区告诉我~)
其中DQ引脚我连在了单片机的P3^7口,可根据自身代码引脚定义接在其他io口。
![](https://img-blog.csdnimg.cn/img_convert/b6f8379aa288fb2485b2107f58cbf6c8.png)
![](https://img-blog.csdnimg.cn/3a4d5144b7ab4535b1196b3d7a876f61.jpeg)
2.DS18B20的供电方式
外部供电:即图2的连接方式,通过引脚由外部供电。
![](https://img-blog.csdnimg.cn/img_convert/148858abba5e0c2e7c3dc26af54bd7a8.png)
寄生供电:由DQ口拉高时向其供电。总线拉高的时候为内部电容(Cpp)充电,当总线拉低是由该电容向设备供电。当DS18B20为“寄生电源”供电模式时,该VDD引脚必须连接到地。
![](https://img-blog.csdnimg.cn/img_convert/a0b5a45bd588d72975c18ac4e382894d.png)
三、软件实现
1.DS18B20初始化
1-Wire 总线上的所有事件都必须以初始化为开始。初始化序列由总线上的主设备发出的复位脉冲以及紧跟着从设备回应的存在脉冲构成(单总线发送一位应答位)。该回应脉冲让总线上的主设备知道在该总线上有从设备(例如DS18B20),并且已经准备好进行操作。
2.单总线
初始化:主机将总线拉低至少480us,然后释放总线,等待15~60us后,存在的从机会拉低总线60~240us以响应主机,之后从机将释放总线。
![](https://img-blog.csdnimg.cn/4cc369eada174ebc8c7ea5f6868ce363.png)
发送一位:主机将总线拉低60~120us,然后释放总线,表示发送0;主机将总线拉低1~15us,然后释放总线,表示发送1。从机将在总线拉低30us后(典型值)读取电平,整个时间片应大于60us。
![](https://img-blog.csdnimg.cn/76c517f0f7c04ab28c38a64340161544.png)
接收一位:主机将总线拉低1~15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us的末尾),读取为低电平则为接收0,读取为高电平则为接收1 ,整个时间片应大于60us。
![](https://img-blog.csdnimg.cn/b83351754515489a8a97eb5846790758.png)
发送一个字节:连续调用8次发送一位的时序,依次发送一个字节的8位(低位在前)
接收一个字节:连续调用8次接收一位的时序,依次接收一个字节的8位(低位在前)
通过单总线发送字节实现DS18B20功能指令。
3.DS18B20 ROM操作以及功能指令
当总线上的主设备检测到了存在脉冲后,就可以执行ROM指令。这些指令是对每个设备独一无二的 64位 ROM编码进行操作的,当总线上连接有多个设备时,可以通过这些指令识别各个设备。这些命令同时也可以使主设备确定该总线上有多少个什么类型的设备或者有温度报警信号的设备。总共包含有5种 ROM 指令,每个指令的长度都是8Bit。主设备在执行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] |
四、代码测试
本系统设计代码采用模块化编程,分为OneWire(单总线)模块、DS18B20模块、LCD1602模块、Delay(延时函数)模块、Key(按键)模块、Timer0(定时器)模块以及主函数。
1.OneWire(单总线)模块
#include <REGX52.H>
// 引脚定义
sbit OneWire_DQ = P3^7;
/**
* @brief 单总线初始化
* @param 无
* @retval 从机响应位,0为响应,1为未响应
*/
unsigned char OneWire_Init(void)
{
unsigned char i;
unsigned char AckBit;
EA = 0;
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
EA = 1;
return AckBit;
}
/**
* @brief 单总线发送一位
* @param Bit 要发送的位
* @retval 无
*/
void OneWire_SendBit(unsigned char Bit)
{
unsigned char i;
EA = 0;
OneWire_DQ = 0;
i = 4;
while (--i); // Delay 10us
OneWire_DQ = Bit;
i = 24;
while (--i); // Delay 50us
OneWire_DQ = 1;
EA = 1;
}
/**
* @brief 单总线接收一位
* @param 无
* @retval 读取的位
*/
unsigned char OneWire_ReceiveBit(void)
{
unsigned char i;
unsigned char Bit;
EA = 0;
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
EA = 1;
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;
}
2.DS18B20模块
#include <REGX52.H>
#include "intrins.h"
#include "OneWire.h"
#include "Delay.h"
#include "NixieTube.h"
#include "LCD1602.h"
// DS18B20指令定义
#define DS18B20_SKIP_ROM 0xCC
#define DS18B20_READ_ROM 0x33
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xBE
#define DS18B20_MATCH_ROM 0x55
/**
* @brief DS18B20开始温度转换
* @param 无
* @retval 无
*/
void DS18B20_ConvertT(void)
{
OneWire_Init(); // 初始化OneWire总线
OneWire_SendByte(DS18B20_SKIP_ROM); // 跳过ROM匹配
OneWire_SendByte(DS18B20_CONVERT_T); // 发送温度转换命令
}
/**
* @brief DS18B20读取温度
* @param 无
* @retval 温度值
*/
float DS18B20_ReadT(void)
{
unsigned char TLSB, TMSB;
int Temp;
float T;
DS18B20_ConvertT(); // 启动温度转换
OneWire_SendByte(DS18B20_READ_SCRATCHPAD); // 读取温度数据
TLSB = OneWire_ReceiveByte(); // 读取温度值低字节
TMSB = OneWire_ReceiveByte(); // 读取温度值高字节
Temp = (TMSB << 8) | TLSB; // 合成温度值
T = Temp / 16.0; // 将温度值转换为浮点数
return T;
}
/**
* @brief DS18B20读取温度(根据ROM码)
* @param DS18B20的ROM码
* @retval 温度数值
*/
float DS18B20_ReadT_m(unsigned char* ROM)
{
unsigned char i;
unsigned char TLSB,TMSB;
int Temp;
float T;
OneWire_Init();
OneWire_SendByte(DS18B20_MATCH_ROM); // 发送匹配ROM命令
for(i = 0; i < 8; i++)
{
OneWire_SendByte(ROM[i]);
}
OneWire_SendByte(DS18B20_CONVERT_T);
Delay(800);
OneWire_Init();
OneWire_SendByte(DS18B20_MATCH_ROM); // 发送匹配ROM命令
for(i = 0; i < 8; i++)
{
OneWire_SendByte(ROM[i]);
}
OneWire_SendByte(DS18B20_READ_SCRATCHPAD); // 读取温度数据
TLSB = OneWire_ReceiveByte();
TMSB = OneWire_ReceiveByte();
Temp = (TMSB << 8) | TLSB;
T = Temp / 16.0;
return T;
}
/**
* @brief 读取ROM码
* @param 存储DS18B20 ROM码的数组
* @retval None
*/
void ReadRomCode(unsigned char* romcode)
{
bit ack;
unsigned char i;
ack = OneWire_Init();
if(ack == 0)
{
OneWire_SendByte(DS18B20_READ_ROM); // 发送读取ROM命令
Delay(1);
for(i = 0; i < 8; i++)
{
romcode[i] = OneWire_ReceiveByte(); // 读取64位ROM码
}
}
}
/**
* @brief 在LCD1602的第二行显示DS18B20的ROM码
* @param 一个DS18B20的ROM码
* @retval None
*/
void LCDShowRomCode(unsigned char romcode)
{
unsigned char high, low;
low = romcode & 0x0f; // 获取低4位
high = romcode & 0xf0; // 获取高4位
high >>= 4;
if(high >= 0x00 && high <= 0x09)
{
LCD_WriteData(high + 0x30); // 获取ASCII码(数字)
}
else
{
LCD_WriteData(high + 0x37); // 获取ASCII码(字母)
}
if(low >= 0x00 && low <= 0x09)
{
LCD_WriteData(low + 0x30); // 获取ASCII码(数字)
}
else
{
LCD_WriteData(low + 0x37); // 获取ASCII码(字母)
}
}
3.LCD1602模块
#include <REGX52.H>
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//函数定义:
/**
* @brief LCD1602延时函数,12MHz调用可延时1ms
* @param 无
* @retval 无
*/
void LCD_Delay()
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
/**
* @brief LCD1602写命令
* @param Command 要写入的命令
* @retval 无
*/
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_EN=0;
LCD_Delay();
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
}
/**
* @brief LCD1602写数据
* @param Data 要写入的数据
* @retval 无
*/
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_EN=0;
LCD_DataPort=Data;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602设置光标位置
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @retval 无
*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else if(Line==2)
{
LCD_WriteCommand(0x80|(Column-1+0x40));
}
}
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*/
void LCD_Init()
{
LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
LCD_WriteCommand(0x08);//关闭LCD显示
LCD_WriteCommand(0x01);//光标复位,清屏
LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
}
/**
* @brief 在LCD1602指定位置上显示一个字符
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @param Char 要显示的字符
* @retval 无
*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
/**
* @brief 在LCD1602指定位置开始显示所给字符串
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串
* @retval 无
*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
/**
* @brief 返回值=X的Y次方
*/
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
/**
* @brief 在LCD1602指定位置开始显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~65535
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以有符号十进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-32768~32767
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
unsigned char i;
unsigned int Number1;
LCD_SetCursor(Line,Column);
if(Number>=0)
{
LCD_WriteData('+');
Number1=Number;
}
else
{
LCD_WriteData('-');
Number1=-Number;
}
for(i=Length;i>0;i--)
{
LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以十六进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFF
* @param Length 要显示数字的长度,范围:1~4
* @retval 无
*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i,SingleNumber;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
SingleNumber=Number/LCD_Pow(16,i-1)%16;
if(SingleNumber<10)
{
LCD_WriteData(SingleNumber+'0');
}
else
{
LCD_WriteData(SingleNumber-10+'A');
}
}
}
/**
* @brief 在LCD1602指定位置开始以二进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
}
}
void LCD_DispalyT(float T)
{
float TShow;
LCD_Init();
LCD_ShowString(1,1,"T:");
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);//显示温度小数部分
}
4.Delay(延时函数)模块
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
5.Key(按键)模块
#include <REGX52.H>
#include "Delay.h"
unsigned char Key_KeyNumber;
/**
* @brief 获取按键键码
* @param 无
* @retval 按下按键的键码,范围:0,1~4,0表示无按键按下
*/
unsigned char Key(void)
{
unsigned char Temp = 0;
Temp = Key_KeyNumber;
Key_KeyNumber = 0;
return Temp;
}
/**
* @brief 获取当前按键的状态,无消抖及松手检测
* @param 无
* @retval 按下按键的键码,范围:0,1~4,0表示无按键按下
*/
unsigned char Key_GetState()
{
unsigned char KeyNumber = 0;
// 矩阵键盘 (设置温度阈值)
P1 = 0xFF;
P1_3 = 0;
if (P1_7 == 0 && P1_3 == 0) {
KeyNumber = 1;
}
P1 = 0xFF;
P1_2 = 0;
if (P1_7 == 0 && P1_2 == 0) {
KeyNumber = 2;
}
P1 = 0xFF;
P1_1 = 0;
if (P1_7 == 0 && P1_1 == 0) {
KeyNumber = 3;
}
P1 = 0xFF;
P1_0 = 0;
if (P1_7 == 0 && P1_0 == 0) {
KeyNumber = 4;
}
return KeyNumber;
}
/**
* @brief 按键驱动函数,在中断中调用
* @param 无
* @retval 无
*/
void Key_Loop(void)
{
static unsigned char NowState, LastState;
LastState = NowState; // 按键状态更新
NowState = Key_GetState(); // 获取当前按键状态
// 如果上个时间点按键按下,这个时间点未按下,则是松手瞬间,以此避免消抖和松手检测
if (LastState == 1 && NowState == 0) {
Key_KeyNumber = 1;
}
if (LastState == 2 && NowState == 0) {
Key_KeyNumber = 2;
}
if (LastState == 3 && NowState == 0) {
Key_KeyNumber = 3;
}
if (LastState == 4 && NowState == 0) {
Key_KeyNumber = 4;
}
}
6.Timer0(定时器)模块
#include <REGX52.H>
/**
* @brief 定时器0初始化,1毫秒@12.000MHz
* @param 无
* @retval 无
*/
void Timer0_Init(void)
{
TMOD &= 0xF0; // 设置定时器模式
TMOD |= 0x01; // 设置定时器模式
TL0 = 0x18; // 设置定时初值
TH0 = 0xFC; // 设置定时初值
TF0 = 0; // 清除TF0标志
TR0 = 1; // 定时器0开始计时
ET0 = 1;
EA = 1;
PT0 = 0;
}
7.读取多个DS18B20的ROM码
为了实现一根单总线挂多个DS18B20读取温度,需要识别每个DS18B20独特的64位ROM码,故在系统设计前需要读取要使用到的每个DS18B20的ROM码,并将其以16进制(低位在前,高位在后)在LCD1602上显示出来(不显示0X前缀),并将显示在LCD1602上的ROM码倒序以记录在数组中(记得加上0X前缀)。
ps:一次只可以读取一个DS18B20的ROM码,不可同时读取多个(读一个插一个,记录完拔下换另一个);如果LCD1602显示很闪,可以调一下延时函数,个人感觉没什么影响其实。
#include <REGX52.H>
#include "intrins.h"
#include "LCD1602.h"
#include "DS18B20.h"
#include "Delay.h"
#include "AT24C02.h"
#include "Key.h"
#include "Timer0.h"
unsigned int i;
unsigned char ROM_data[8];
void main()
{
/*****************初始化LCD1602并显示DS18B20的ROM码并发送数据给串口********************/
LCD_Init(); // 初始化LCD1602
Delay(2);
LCD_SetCursor(2,0); // 将LCD1602的光标设置在第二行第一列
ReadRomCode(ROM_data); // 读取DS18B20的ROM码
for(i = 0; i < 8; i++)
{
LCDShowRomCode(ROM_data[8-i-1]); // 在LCD上显示ROM码
}
}
![](https://img-blog.csdnimg.cn/23e78737c1cb4f4883cba77194113553.png)
8.主函数
在主函数中实现了显示温度数据、调节温度上下限以及声光报警功能。
在主函数中同时调用了AT24C02模块和UART串口模块,分别起到关机保存上一次调节的温度阈值数据和将DS18B20的ROM码数据发送给串口。(也可去掉)
#include <REGX52.H>
#include "intrins.h"
#include "LCD1602.h"
#include "DS18B20.h"
#include "Delay.h"
#include "AT24C02.h"
#include "Key.h"
#include "Timer0.h"
#include "UART.h"
sbit ROM_1 = P3^1;
sbit ROM_2 = P3^0;
sbit ROM_3 = P3^2;
sbit ROM_4 = P3^3;
sbit beep = P2^5;
float T;
float TShow;
char TLow,THigh;
unsigned char KeyNum;
char flag = 0XFE;
unsigned int i;
unsigned char num = '1';
unsigned char code romcode1[] = {0x28, 0xC1, 0x50, 0x79, 0xA2, 0x00, 0x03, 0x43};
unsigned char code romcode2[] = {0x28, 0x61, 0x64, 0x08, 0xD5, 0x73, 0x3A, 0xF7};
unsigned char code romcode3[] = {0x28, 0x61, 0x64, 0x08, 0xA1, 0xC3, 0x52, 0x23};
unsigned char code romcode4[] = {0x28, 0x61, 0x64, 0x08, 0xA1, 0xCF, 0x5C, 0x71};
static unsigned char *ROM_data;
void main()
{
/****************LCD1602显示DS18B20的ROM码并发送数据给串口***********************/
// LCD_Init();
// UART_Init();
// Delay(1);
// LCD_SetCursor(1,0); //LCD1602设置光标在第一行
// ReadRomCode(ROM_data);
// for(i = 0;i < 8;i++)
// {
// LCDShowRomCode(ROM_data[8-i-1]);
// UART_SendByte(ROM_data[8-i-1]);
// }
/***********************************************************************/
THigh=AT24C02_ReadByte(0); //读取当前温度上限数据
TLow=AT24C02_ReadByte(1);
if(THigh>125 || TLow<-55 || THigh<=TLow)
{
THigh=30; //如果温度数据不合法, 则设为默认值
TLow=15;
}
LCD_Init();
LCD_ShowString(1,1,"T");
LCD_ShowString(1,3,":");
LCD_ShowString(2,1,"TH:");
LCD_ShowString(2,9,"TL:");
LCD_ShowSignedNum(2,4,THigh,3);
LCD_ShowSignedNum(2,12,TLow,3);
ROM_data = romcode1;
Timer0_Init();
while(1)
{
KeyNum=Key();
/*温度读取和显示*/
T = DS18B20_ReadT_m(ROM_data);
LCD_ShowChar(1,2,num); //显示传感器编号
if(T<0) //如果温度小于0
{
LCD_ShowChar(1,4,'-'); //显示负号
TShow=-T; //将温度取反
}
else //如果温度大于等于0
{
LCD_ShowChar(1,4,'+'); //显示正号
TShow=T;
}
LCD_ShowNum(1,5,TShow,3); //显示温度整数部分
LCD_ShowChar(1,8,'.'); //显示小数点
LCD_ShowNum(1,9,(unsigned long)(TShow*100)%100,2);//显示温度小数部分
if(0 == ROM_1) //K1按键,切换THigh参考温度
{
num = '1';
ROM_data = romcode1;
}
if(0 == ROM_2) //K2按键,切换THigh参考温度
{
num = '2';
ROM_data = romcode2;
}
if(0 == ROM_3) //K3按键,切换THigh参考温度
{
num = '3';
ROM_data = romcode3;
}
if(0 == ROM_4) //K4按键,切换THigh参考温度
{
num = '4';
ROM_data = romcode4;
}
/* 阈值判断和显示 */
if (KeyNum) {
if (KeyNum == 1) { // K1键,THigh增加
THigh++;
if (THigh > 125) {
THigh = 125;
}
LCD_ShowString(1, 1, "T1:");
}
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");
for (i = 0; i < 200; i++) {
beep = ~beep;
Delay(1);
P2 = flag;
flag = _crol_(flag, 1);
}
} else if (T < TLow) {
LCD_ShowString(1, 13, "OV:L");
for (i = 0; i < 200; i++) {
beep = ~beep;
Delay(1);
P2 = flag;
flag = _crol_(flag, 1);
}
} else {
LCD_ShowString(1, 13, " ");
beep = 0;
flag = 0XFE;
}
}
}
/**********定时器0中断************/
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count; // 定义一个静态变量T0Count
TL0 = 0x18; // 设置定时器初值
TH0 = 0xFC; // 设置定时器初值
T0Count++; // T0Count自增
if(T0Count>=20) // 如果T0Count达到20
{
T0Count=0; // 将T0Count重置为0
Key_Loop(); // 每20ms调用一次按键处理函数
}
}
五、总结
因为暑假前课设的要求做了个温度报警系统,还是有很多的不足和小问题的,我当时卡在了匹配ROM码读取温度那里,也查过了很多资料,最后我在DS18B20模块那里添加了带ROM码参数的读取温度的函数,根据传递的ROM码进行匹配并读取到了对应的温度传感器的温度。还有独立按键和矩阵键盘切换DS18B20和调节温度上下限,这个地方按键需要按下时间有点长,还没有找到什么解决方法哈哈哈哈哈。
课设验收的时候,老师也提出了不同的DS18B20保存不同的温度上下限(当然我没做哈哈哈哈),宝子们也都可以试试哈哈哈哈哈。还有将采集到的温度数据上传到云端(可以试试esp8266、小熊派...)、能够使用PID等算法进行温度调节(可能会用到继电器、加热膜、小风扇等),以后可能会试一下。还是希望有什么不足大家可以提一提,希望能帮助到有需要的人~~~