一. 基于STC89C52单片机的智能温度检测仪设计概述
1. 智能温度检测仪系统功能概述
智能温度检测仪是采用STC89C52为主控芯片,结合DS18B20温度传感器、LCD1602显示屏、LED显示模块、AT24C02存储单元、直流电机以及蜂鸣器等外设设计出了可以采集实时温度、显示最近一次采集的温度、显示最大采集温度、显示最近五次采集的平均温度、显示采集温度的次数、温度报警、智能风扇降温等功能。本设计通过DS18B20、矩阵键盘、EEPROM相结合以及设置出界面标志位,可以实现实时温度、采集次数、最高温度、平均温度、风扇挡位等界面的切换。并且通过LCD1602显示屏,可以完美的将各个模式界面显示在LCD1602上。本设计除了完成温度采集以及相关计算的功能,还实现了温度报警的功能、风扇调速、风扇强制关闭的功能。并且本设计中所有底层代码都通过模块化编程的方式来实现,使设计整体清晰直观。
1.1 整体功能框图
本设计由STC89C52为主控芯片。该设计通过DS18B20来进行外部温度的采集;通过矩阵键盘输入来控制各个模式的切换;通过AT24C02来进行温度数据的存储和数据的掉电保护;通过LCD1602显示屏来显示出各个界面;除此之外该设计还具有蜂鸣器报警功能,直流电机智能降温功能,LED闪烁功能。
1.2 界面控制和显示功能
本设计中采用LCD1602模块来实现显示功能,本设计一共有6个显示界面。六个界面分别为实时温度显示界面、最大温度显示界面、最近一次保存温度显示界面、平均温度显示界面、温度上限参数显示界面和电机挡位显示界面,各个界面详情如下。
- 界面1——实时温度
按下矩阵键盘的按键1为显示界面1,在此界面下会显示出温度传感器实时采集到的温度和温度保存次数。显示格式为LCD1602第一行从最左端开始显示“temp_rea:”,其后数据为温度传感器采集到的实时温度。LCD1602的第二行显示“count:”和温度的采集次数。具体显示情况如下。 - 界面2——最大温度
按下矩阵键盘的按键3为显示界面2,在此界面下会显示出近五次AT24C02所保存的温度中的最高温度和温度保存次数。显示格式为LCD1602第一行从最左端开始显示“temp_max:”,其后数据为近五次所保存的最高温度。LCD1602的第二行显示“count:”和温度的采集次数。具体显示情况如下。 - 界面3——最近温度
按下矩阵键盘的按键4为显示界面3,在此界面下会显示出AT24C02最近一次保存的温度和温度保存次数。显示格式为LCD1602第一行从最左端开始显示“temp_rec:”,其后数据为最近一次保存的温度。LCD1602的第二行显示“count:”和温度的采集次数。具体显示情况如下。
- 界面4——平均温度
按下矩阵键盘的按键5为显示界面4,在此界面下会显示出AT24C02近五次保存温度的平均温度,如果保存次数小于5次,则会显示出该保存次数下的平均温度和温度保存次数。显示格式为LCD1602第一行从最左端开始显示“temp_ave:”,其后数据为平均温度。LCD1602的第二行显示“count:”和温度的采集次数。具体显示情况如下。 - 界面5——温度参数
按下矩阵键盘的按键9为显示界面5,在此界面下会显示出温度上限参数和温度保存次数。显示格式为LCD1602第一行从最左端开始显示“temp_lim:”,其后数据为平均温度。LCD1602的第二行显示“count:”和温度的采集次数。具体显示情况如下。 - 界面6——电机挡位
按下矩阵键盘按键10将会实现电机转速的调整,一共有四种转速调整模式。按下按键11将是显示界面6电机挡位显示界面,将会LCD1602第一行将会显示原有内容,LCD1602的第二行将会显示出电机转速的挡位,格式为“Speed:”和当前的挡位。具体显示情况如下。
1.3 智能降温和报警功能
当温度高于温度上限参数时,蜂鸣器就会进行报警操作。在此状态下可以按下矩阵键盘的按键10来对直流电机进行调速。直流电机一共有四种挡位可供原则,四种挡位对应PWM的占空比分别为0、25、50、100四种状态。从而在温度高于温度上限参数时可以用直流电机操控风扇来达到降温的目的。除此之外,当温度低于温度上限参数时,直流电机会自动停止工作,在此状态下调节矩阵键盘按键10也不会改变直流电机挡位。
1.4 温度采集和记录功能
本设计可以通过DS18B20温度传感器进行温度的采集,在本设计中温度可以精确到小数点后一位。并且可以正确的辨别出温度是正数还是负数。除此之外,本设计还使用了AT24C02存储模块,并且通过程序运用了其十个字节的存储单元,以实现其可进行五次温度采集的掉电保存。此设计还进行了最大采集温度的计算以及最近一次采集温度的保存。
温度的采集以及计算功能与矩阵键盘相结合,当矩阵键盘按键2按下时就会对当前温度进行保存,当矩阵键盘的按键3按下时其会计算出近五次保存的温度中的最高温度,当矩阵键盘的按键4按下时其会计算出最近一次保存的温度,当矩阵键盘的按键5按下时其会计算出近五次保存的平均温度,如果保存次数小于五次,则会计算该保存次数的平均温度。
二. 智能温度检测仪硬件设计
2.1 温度传感器的选型
2.1.1 传感器说明
本设计采用DS18B20温度传感器作为温度采集模块的主元件。 DS18B20数字温度计提供9位温度读数。信息经过单线接口送入或送出DS18B20传感器,因此从中央处理器到DS18B20仅需要提供电源以及一根数据线,就可以工作。
2.1.2DS18B20的ROM指令表
指令 | 约定代码 | 功能 |
---|---|---|
读ROM | 33H | 读取DS18B20温度传感器ROM中的编码 |
符合ROM | 55H | 发送此命令之后,接着发送出64位ROM编码,访问单总线上与该编码相对应的DS18B20使用之作出相应,为下一步DS18B20的读写做出准备。 |
搜索ROM | F0H | 用于确定挂接在同一总线上DS18B20的个数和识别64位ROM地址,为操作各器件做好准备 |
跳过ROM | CCH | 在单点总线系统中,此命令通过允许总线主机不提供64位ROM编码而访问存储器来节省空间 |
告警搜索命令 | ECH | 此命令的流程与搜索ROM命令相同。但仅在最近一次温度测量出现告警的情况下,DS18B20才对此命令作出响应。告警的条件定义为温度高于TH 或低于TL。只要DS18B20一上电,告警条件就保持在设置状态,直到另一次温度测量显示出非告警值或者改变TH或TL的设置,使得测量值再一次位于允许的范围之内。贮存在EEPROM内的触发器值用于告警。 |
2.1.3 DS18B20的RAM指令表
指令 | 约定代码 | 功能 |
---|---|---|
温度变换 | 44H | 启动一次温度转换而无需其他数据。温度转换命令被执行,而后DS18B20保持等待状态。 |
读RAM | BEH | 这个命令读取暂存器的内容。读取将从字节0开始,一直进行下去,直到第9(字节8,CRC)字节读完。如果不想读完所有字节,控制器可以在任何时间发出复位命令来中止读取。 |
写RAM | 4EH | 这个命令向DS18B20的暂存器中写入数据,开始位置在地址2。接下来写入的两个字节将被存到暂存器中的地址位置2和3。可以在任何时刻发出复位命令来中止写入。 |
复制RAM | 48H | 这条命令把暂存器的内容拷贝到DS18B20的E2存储器里,即把温度报警触发字节存入非易失性存储器里。 |
重新调整E2 | B8H | 把贮存在E2中温度触发器的值重新调至暂存存储器。这种重新调出的操作在对DS18B20上电时也自动发生,因此只要器件一上电,暂存存储器内就有了有效的数据。 |
读电源 | B4H | 对于在此命令发送至DS18B20之后所发出的第一读数据的时间片,器件都会给出其电源方式的信号:“0”=寄生电源供电,“1”=外部电源供电。 |
2.1.4 单总线协议
- DS18B20初始化
(1) 数据线拉到低电平“0”。
(2) 延时480微妙(该时间的时间范围可以从480到960微妙)。
(3) 数据线拉到高电平“1”。
(4) 延时等待80微妙。如果初始化成功则在15到60微妙时间内产生一个由DS18B20所返回的低电平“0”.根据该状态可以来确定它的存在,但是应注意不能无限的进行等待,不然会使程序进入死循环,所以要进行超时判断。
(5) 若CPU读到了数据线上的低电平“0”后,还要做延时,其延时的时间从发出的高电平算起(第3步的时间算起)最少要480微妙。 - 读时序
(1)将数据线拉低“0”。
(2)延时1微妙。
(3)将数据线拉高“1”,释放总线准备读数据。
(4)延时10微妙。
(5)读数据线的状态得到1个状态位,并进行数据处理。
(6)延时45微妙。
(7)重复1~7步骤,直到读完一个字节。 - 写时序
(1)数据线先置低电平“0”
(2)延时15微妙。
(3)按从低位到高位的顺序发送数据(一次只发送一位)。
(4)延时60微妙。
(5)将数据线拉到高电平。
(6)重复1~5步骤,直到发送完整的字节。
(7)最后将数据线拉高。
2.1.5 硬件连接
2.1.6 程序例程
//onewire单总线代码例程
#include "onewire.h"
sbit DQ = P3^7; // 单总线接口
// 单总线延时函数
void Delay_OneWire(unsigned int t) // STC89C52RC
{
t = t;
while (t--);
}
// 通过单总线向DS18B20写一个字节
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for (i = 0; i < 8; i++)
{
DQ = 0;
DQ = dat & 0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1;
}
Delay_OneWire(5);
}
// 从DS18B20读取一个字节
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for (i = 0; i < 8; i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
if (DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
return dat;
}
// DS18B20设备初始化
bit init_ds18b20(void)
{
bit initflag = 0;
DQ = 1;
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);
DQ = 1;
Delay_OneWire(10);
initflag = DQ;
Delay_OneWire(5);
return initflag;
}
2.2 显示屏选型
2.2.1 LCD1602显示屏
1602液晶也叫1602字符型液晶,它是一种专门用来显示字母、数字、符号的点阵型液晶模块。它是由若干个5x7或者5x10的点阵字符位组成,每个点阵字符位都可以用以显示一个字符,每位之间有一个点距的间隔,每行之间也有间隔,起到了字符间距和行间距的作用,正因为如此,所以它不能很好的显示图片。
2.2.2 LCD1602引脚定义
编号 | 符号 | 引脚说明 |
---|---|---|
1 | VSS | 电源地 |
2 | VDD | 电源正极 |
3 | VL | 液晶显示偏压信号 |
4 | RS | 数据/命令选择端(H/L) |
5 | R/W | 读/写选择端(H/L) |
6 | E | 使能信号 |
7 | D0 | Data I / O |
8 | D1 | Data I / O |
9 | D2 | Data I / O |
10 | D3 | Data I / O |
11 | D4 | Data I / O |
12 | D5 | Data I / O |
13 | D6 | Data I / O |
14 | D7 | Data I / O |
15 | BLA | 背光源正极 |
16 | BLK | 背光源负极 |
2.2.3 LCD1602代码例程
#include "LCD1602.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=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602写数据
* @param Data 要写入的数据
* @retval 无
*/
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=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(0x0c);//显示开,光标关,闪烁关
LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x01);//光标复位,清屏
}
/**
* @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;
}
//第二行的清除函数
void LCD1602_Clear_2LINE()
{
int i=0;
LCD_WriteCommand(0x80+0x40);
for(i=0;i<16;i++)
{
LCD_WriteData(0x20); //无显示
}
// while(*str!='\0')
// {
// LCD1602_WriteData(*str);
// DELAY_US(500); //延时500us
// str++;
// }
}
/**
* @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');
}
}
void LCD_ShowSignedNum1(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');
}
}
三. 智能温度检测仪的代码思路
3.1 主控制器的程序思路
在系统上电后会先执行LCD1602显示屏的初始化,接下来系统会执行定时器0的初始化,然后系统将进入循环,在循环体内部系统将会调用温度读取函数Read_temp来进行环境温度的采集,接下来系统将调用按键处理函数key_pro,对不同按键按下产生不同的结果,接下来系统将执行数据处理函数,对采集到的温度数据进行处理,然后系统将执行LCD显示函数,在LCD1602显示屏上显示各个界面,最后系统将执行LED处理函数,根据情况判断LED是否按下。系统的整体流程图如下。
3.2 各个模块子程序思路
3.1 LCD1602显示函数
本设计中的LCD1602显示模块,先来判断实时温度与温度上限参数的大小,如果实时温度大于温度上限参数则LCD1602的第二行最后五个字节显示出error字样,然后会进行界面标志位state的判断,state对应6个界面,程序中用switch case选择语句与LCD1602底层驱动代码相结合使用来实现界面的切换。LCD1602选择界面流程图如下。
//LCD1602 界面切换部分代码
//uchar state = 1;//界面标志位1 即时温度 2 最高温度 3 平均温度 4 最近一次存储温度 5 最大温度限制 6 风扇挡位
void LCD1602_pro()
{
if(state == 1)
{
LCD_ShowString(2,1,"count:");
LCD_ShowSignedNum(2,7,count1,2);
LCD_ShowString(1,1,"temp_rea:");
if(temp > 99)
{
LCD_ShowSignedNum(1,11,temp/10,2);
LCD_ShowChar(1,14,'.');
LCD_ShowSignedNum1(1,15,temp%10,1);
}
else if(temp > 9)
{
LCD_ShowSignedNum(1,11,temp/10,1);
LCD_ShowChar(1,13,'.');
LCD_ShowSignedNum1(1,14,temp%10,1);
}
else
{
LCD_ShowSignedNum(1,11,0,1);
LCD_ShowChar(1,13,'.');
LCD_ShowSignedNum1(1,14,temp%10,1);
}
}
else if(state == 2)
{
LCD_ShowString(2,1,"count:");
LCD_ShowSignedNum(2,7,count1,2);
LCD_ShowString(1,1,"temp_max:");
if(temp_max > 99)
{
LCD_ShowSignedNum(1,11,temp_max/10,2);
LCD_ShowChar(1,14,'.');
LCD_ShowSignedNum1(1,15,temp_max%10,1);
}
else if(temp_max > 9)
{
LCD_ShowSignedNum(1,11,temp_max/10,1);
LCD_ShowChar(1,13,'.');
LCD_ShowSignedNum1(1,14,temp_max%10,1);
}
else
{
LCD_ShowSignedNum(1,11,0,1);
LCD_ShowChar(1,13,'.');
LCD_ShowSignedNum1(1,14,temp_max%10,1);
}
}
else if(state == 3)
{
LCD_ShowString(2,1,"count:");
LCD_ShowSignedNum(2,7,count1,2);
LCD_ShowString(1,1,"temp_rec:");
if(temp_rec > 99)
{
LCD_ShowSignedNum(1,11,temp_rec/10,2);
LCD_ShowChar(1,14,'.');
LCD_ShowSignedNum1(1,15,temp_rec%10,1);
}
else if(temp > 9)
{
LCD_ShowSignedNum(1,11,temp_rec/10,1);
LCD_ShowChar(1,13,'.');
LCD_ShowSignedNum1(1,14,temp_rec%10,1);
}
else
{
LCD_ShowSignedNum(1,11,0,1);
LCD_ShowChar(1,13,'.');
LCD_ShowSignedNum1(1,14,temp_rec%10,1);
}
}
else if(state == 4)
{
LCD_ShowString(2,1,"count:");
LCD_ShowSignedNum(2,7,count1,2);
LCD_ShowString(1,1,"temp_ave:");
if(temp_average > 99)
{
LCD_ShowSignedNum(1,11,temp_average/10,2);
LCD_ShowChar(1,14,'.');
LCD_ShowSignedNum1(1,15,temp_average%10,1);
}
else if(temp_average > 9)
{
LCD_ShowSignedNum(1,11,temp_average/10,1);
LCD_ShowChar(1,13,'.');
LCD_ShowSignedNum1(1,14,temp_average%10,1);
}
else
{
LCD_ShowSignedNum(1,11,0,1);
LCD_ShowChar(1,13,'.');
LCD_ShowSignedNum1(1,14,temp_average%10,1);
}
}
else if(state == 5)
{
LCD_ShowString(2,1,"count:");
LCD_ShowSignedNum(2,7,count1,2);
LCD_ShowString(1,1,"temp_lim:");
if(temp_limit > 99)
{
LCD_ShowSignedNum(1,11,temp_limit/10,2);
LCD_ShowChar(1,14,'.');
LCD_ShowSignedNum1(1,15,temp_limit%10,1);
}
else if(temp_average > 9)
{
LCD_ShowSignedNum(1,11,temp_limit/10,1);
LCD_ShowChar(1,13,'.');
LCD_ShowSignedNum1(1,14,temp_limit%10,1);
}
else
{
LCD_ShowSignedNum(1,11,0,1);
LCD_ShowChar(1,13,'.');
LCD_ShowSignedNum1(1,14,temp_limit%10,1);
}
}
else if(state == 6)
{
LCD_ShowString(2,1,"Speed:");
LCD_ShowSignedNum(2,7,Speed,2);
}
if(temp>temp_limit)
{
motor_flag = 1;
Buzzer = 1;
LCD_ShowString(2,12,"error");
}
else if(temp <= temp_limit)
{
motor_flag = 0;
Compare = 0;
Buzzer = 0;
LCD_ShowString(2,12," ");
}
}
3.2 DS18B20温度采集
在进行DS18B20采集温度时,首先关闭定时器0中断,避免定时器产生PWM波的过程与温度采集过程冲突。其次,初始化DS18B20,接下来在DS18B20中写入0xCC,其目的为跳过ROM指令,从而节省程序运行时间。然后在DS18B20的RAM中写入0x44,其目的启动温度转换命令。然后延时一段时间,等待温度转换完成。至此,DS18B20采集温度过程已完成,接下来只需从DS18B20中把温度读取出来即可。第一步还是初始化DS18B20,然后向DS18B20中写入0xCC,接着向DS18B20中写入0xBE,接下来调用DS18B20_Read函数,从DS18B20中一位一位的将温度的数字量读取出来,然后再根据分辨率对读取出来的温度进行数据处理即可。最后打开定时器0中断,使其正常产生PWM波。至此,此函数流程完全结束。函数流程图如下。
//DS18B20 采集温度
void Read_Temp()
{
uchar LSB,MSB;
EA = 0;
init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0x44);
Delay_us(15);
init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0xBE);
LSB = Read_DS18B20();
MSB = Read_DS18B20();
init_ds18b20();
temp = (MSB << 8) | LSB;
if((temp & 0xF800) == 0x0000)
{
temp >>= 4;
temp = temp * 10;
temp = temp + (LSB & 0x0F) * 0.0625 * 10;
}
EA = 1;
}
3.3 矩阵键盘模块
本设计的矩阵键盘,共可以实现6种界面切换,按键1为界面1、按键2保存当前温度、按键3为界面2、按键4显示最近保存温度、按键5显示近五次保存的平均温度、按键6实现LED闪烁、按键7和按键8可以来改变温度上限参数,且其只有在温度上限参数显示界面有效。按键9为温度参数显示界面、按键10可以实现电机速度的调整、按键11为电机挡位显示界面。按键部分具体流程图如下。
//矩阵键盘实现界面切换代码
void key_pro()
{
uint keynum;
keynum = key_scan();
switch(keynum)
{
case 1:state = 1;
break;
case 2:
AT24C02_WriteByte(0,temp%256);
Delay_ms(5);
AT24C02_WriteByte(1,temp/256);
Delay_ms(5);
AT24C02_WriteByte(0x10,AT24C02_ReadByte(0x06));
Delay_ms(5);
AT24C02_WriteByte(0x11,AT24C02_ReadByte(0x07));
Delay_ms(5);
AT24C02_WriteByte(0x06,AT24C02_ReadByte(0x04));
Delay_ms(5);
AT24C02_WriteByte(0x07,AT24C02_ReadByte(0x05));
Delay_ms(5);
AT24C02_WriteByte(0x04,AT24C02_ReadByte(0x02));
Delay_ms(5);
AT24C02_WriteByte(0x05,AT24C02_ReadByte(0x03));
Delay_ms(5);
AT24C02_WriteByte(0x02,AT24C02_ReadByte(0x00));
Delay_ms(5);
AT24C02_WriteByte(0x03,AT24C02_ReadByte(0x01));
Delay_ms(5);
count1++;
break;
case 3:state = 2;
break;
case 4:state = 3;
break;
case 5:state = 4;
break;
case 6:led_flag = 1 - led_flag;
break;
case 7:
if(state == 5)
{
temp_limit += 10;
}
break;
case 8:
if(state == 5)
{
temp_limit -= 10;
}
break;
case 9:state = 5;
break;
case 10:
if(motor_flag == 1)
{
Speed++;
Speed %= 4;
if(Speed == 4){Compare = 100;}
else if(Speed == 3){Compare = 75;}
else if(Speed == 2){Compare = 50;}
else if(Speed == 1){Compare = 25;}
else{Speed = 0;}
}
break;
case 11:
state = 6;
break;
}
}
//矩阵键盘获取键码代码
uint key_scan()
{
uchar key;
P1_4 = P1_5 = P1_6 = P1_7 = 0;
P1_3 = P1_2 = P1_1 = P1_0 = 1;
if(key_up == 1 && (P1_3 == 0 || P1_2 == 0 || P1_1 == 0 || P1_0 == 0))
{
Delay_ms(20);
if(P1_3 == 0) key = 1;
else if(P1_2 == 0) key = 2;
else if(P1_1 == 0) key = 3;
else if(P1_0 == 0) key = 4;
else return 0;
P1_4 = P1_5 = P1_6 = P1_7 = 1;
P1_3 = P1_2 = P1_1 = P1_0 = 0;
Delay_ms(20);
key_up = 0;
if(P1_7 == 0) return key;
else if(P1_6 == 0) return key + 4;
else if(P1_5 == 0) return key + 8;
else if(P1_4 == 0) return key + 12;
}
else if(P1_3 == 1 && P1_2 == 1 && P1_1 == 1 && P1_0 == 1)
key_up = 1;
return 0;
}
四. 代码中调试问题
4.1 LED与LCD1602部分引脚冲突问题
在本设计中LCD1602占用P2^7 P2^6 P2^5引脚,这部分引脚与LED的引脚相冲突。因此LED的闪烁部分有一定的问题。
4.2 定时器和温度采集冲突问题
问题具体描述:当使用定时器0产生PWM来控制电机挡位时温度采集数据一直为53.5不会发生改变。
解决方案:在进行温度采集时关闭定时器0的中断,温度采集完成后打开定时器0的中断。