基于51单片机的智能温度检测仪

一. 基于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为显示界面1,在此界面下会显示出温度传感器实时采集到的温度和温度保存次数。显示格式为LCD1602第一行从最左端开始显示“temp_rea:”,其后数据为温度传感器采集到的实时温度。LCD1602的第二行显示“count:”和温度的采集次数。具体显示情况如下。实时温度显示界面
  2. 界面2——最大温度
    按下矩阵键盘的按键3为显示界面2,在此界面下会显示出近五次AT24C02所保存的温度中的最高温度和温度保存次数。显示格式为LCD1602第一行从最左端开始显示“temp_max:”,其后数据为近五次所保存的最高温度。LCD1602的第二行显示“count:”和温度的采集次数。具体显示情况如下。最大温度显示界面
  3. 界面3——最近温度
    按下矩阵键盘的按键4为显示界面3,在此界面下会显示出AT24C02最近一次保存的温度和温度保存次数。显示格式为LCD1602第一行从最左端开始显示“temp_rec:”,其后数据为最近一次保存的温度。LCD1602的第二行显示“count:”和温度的采集次数。具体显示情况如下。
    最近温度显示界面
  4. 界面4——平均温度
    按下矩阵键盘的按键5为显示界面4,在此界面下会显示出AT24C02近五次保存温度的平均温度,如果保存次数小于5次,则会显示出该保存次数下的平均温度和温度保存次数。显示格式为LCD1602第一行从最左端开始显示“temp_ave:”,其后数据为平均温度。LCD1602的第二行显示“count:”和温度的采集次数。具体显示情况如下。平均温度显示界面
  5. 界面5——温度参数
    按下矩阵键盘的按键9为显示界面5,在此界面下会显示出温度上限参数和温度保存次数。显示格式为LCD1602第一行从最左端开始显示“temp_lim:”,其后数据为平均温度。LCD1602的第二行显示“count:”和温度的采集次数。具体显示情况如下。温度参数显示界面
  6. 界面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指令表

指令约定代码功能
读ROM33H读取DS18B20温度传感器ROM中的编码
符合ROM55H发送此命令之后,接着发送出64位ROM编码,访问单总线上与该编码相对应的DS18B20使用之作出相应,为下一步DS18B20的读写做出准备。
搜索ROMF0H用于确定挂接在同一总线上DS18B20的个数和识别64位ROM地址,为操作各器件做好准备
跳过ROMCCH在单点总线系统中,此命令通过允许总线主机不提供64位ROM编码而访问存储器来节省空间
告警搜索命令ECH此命令的流程与搜索ROM命令相同。但仅在最近一次温度测量出现告警的情况下,DS18B20才对此命令作出响应。告警的条件定义为温度高于TH 或低于TL。只要DS18B20一上电,告警条件就保持在设置状态,直到另一次温度测量显示出非告警值或者改变TH或TL的设置,使得测量值再一次位于允许的范围之内。贮存在EEPROM内的触发器值用于告警。

2.1.3 DS18B20的RAM指令表

指令约定代码功能
温度变换44H启动一次温度转换而无需其他数据。温度转换命令被执行,而后DS18B20保持等待状态。
读RAMBEH这个命令读取暂存器的内容。读取将从字节0开始,一直进行下去,直到第9(字节8,CRC)字节读完。如果不想读完所有字节,控制器可以在任何时间发出复位命令来中止读取。
写RAM4EH这个命令向DS18B20的暂存器中写入数据,开始位置在地址2。接下来写入的两个字节将被存到暂存器中的地址位置2和3。可以在任何时刻发出复位命令来中止写入。
复制RAM48H这条命令把暂存器的内容拷贝到DS18B20的E2存储器里,即把温度报警触发字节存入非易失性存储器里。
重新调整E2B8H把贮存在E2中温度触发器的值重新调至暂存存储器。这种重新调出的操作在对DS18B20上电时也自动发生,因此只要器件一上电,暂存存储器内就有了有效的数据。
读电源B4H对于在此命令发送至DS18B20之后所发出的第一读数据的时间片,器件都会给出其电源方式的信号:“0”=寄生电源供电,“1”=外部电源供电。

2.1.4 单总线协议

  1. DS18B20初始化
    在这里插入图片描述
    (1) 数据线拉到低电平“0”。
    (2) 延时480微妙(该时间的时间范围可以从480到960微妙)。
    (3) 数据线拉到高电平“1”。
    (4) 延时等待80微妙。如果初始化成功则在15到60微妙时间内产生一个由DS18B20所返回的低电平“0”.根据该状态可以来确定它的存在,但是应注意不能无限的进行等待,不然会使程序进入死循环,所以要进行超时判断。
    (5) 若CPU读到了数据线上的低电平“0”后,还要做延时,其延时的时间从发出的高电平算起(第3步的时间算起)最少要480微妙。
  2. 读时序
    DS18B20读时序
    (1)将数据线拉低“0”。
    (2)延时1微妙。
    (3)将数据线拉高“1”,释放总线准备读数据。
    (4)延时10微妙。
    (5)读数据线的状态得到1个状态位,并进行数据处理。
    (6)延时45微妙。
    (7)重复1~7步骤,直到读完一个字节。
  3. 写时序

DS18B20写时序
(1)数据线先置低电平“0”
(2)延时15微妙。
(3)按从低位到高位的顺序发送数据(一次只发送一位)。
(4)延时60微妙。
(5)将数据线拉到高电平。
(6)重复1~5步骤,直到发送完整的字节。
(7)最后将数据线拉高。

2.1.5 硬件连接

DS18B20硬件连接图

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引脚定义

编号符号引脚说明
1VSS电源地
2VDD电源正极
3VL液晶显示偏压信号
4RS数据/命令选择端(H/L)
5R/W读/写选择端(H/L)
6E使能信号
7D0Data I / O
8D1Data I / O
9D2Data I / O
10D3Data I / O
11D4Data I / O
12D5Data I / O
13D6Data I / O
14D7Data I / O
15BLA背光源正极
16BLK背光源负极

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选择界面流程图

//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采集温度

//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的中断。

  • 14
    点赞
  • 90
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
目前关于温度传感器的误差研究大多从温度传感器硬件自身角度出发进行分析,单片机控制的温度传感器测温系统,绝大多数采用独立的片外温度传感器进行设计,成本较高,对测量误差的分析针对的是片外温度传感器,而单片机片内温度传感器虽然成本较低:但其检测到的是单片机的芯片温度,如用其测量外部环境温度,误差较大。C8051F系列单片机价格低廉,开发简单,因其自带片内温度传感器,如用于测量外部环境温度,可以实现单片机开发的低成本化。本文对C8051F系列单片机的片内温度传感器测量环境温度的误差进行了分析,对补偿方法作了探讨,提出了低功耗的硬件与软件相结合的设计方法,并在此基础上力求提高ADC测量精度,为测量外部环境温度提供减小误差的方法以供参考。文章针对单片机自热效应及其它原因引起的误差进行了分析,对于自热效应,主要从功耗方面入手对供电电源、频率、高功耗的模拟设备、数字设备等进行了分析,并对ADC转换的误差进行了分析,继而从供电设计、硬件系统的低功耗设计、硬件系统提高测量精度的设计、软件程序设计、补偿算法等方面提出了综合的解决方案,探讨用单片机片内温度传感器的更为精确的测量环境温度的方法,对于实现用C8051F等系列的片内温度传感器测量环境温度的低成本化,有一定启发意义。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值