基于51单片机的测温报警系统

软件开发大郭
0 评论
/
31 阅读
/
13322 字
25 2022-05

一、电路原理图

基于单片机的数据采集系统方案,该方案根据热敏电阻随温度变化而变化的特性,采用串联分压电路。
单片机采集热敏电阻的电压,通过A/D转换将模拟量电压信号转换成数字量电压信号,
经过查表转换得到温度值,控制LCD实时显示温度值。
51单片机NTC测温原理图

二、源码

1.main

代码如下:

#include<reg52.h>                 
#include<intrins.h>
#include"eeprom52.h"          //调用STC89C52单片机的EEPROM控制程序
#include "math.h"
#include"ADC0832.H"
#define uchar unsigned char         
#define uint unsigned int         
#define LCD1602_dat P0             //LCD1602的数据传输IO口

sbit LCD1602_rs=P2^5;
sbit LCD1602_rw=P2^6; 
sbit LCD1602_e=P2^7;  
sbit beep=P2^0;     //蜂鸣器IO
sbit led_1=P1^5; //超上限指示灯
sbit led_2=P1^6;//超下限指示灯

sbit key_1=P3^5;  //设置按键
sbit key_2=P3^6;//加按键
sbit key_3=P3^7;//减按键

uchar dat=0;       //AD值
uint c,d;
uint DY_dat=0;    
uchar ad_dat1=0;   //读取滤波后的AD值

float adc_value;    //暂存读取的输入变量

int temp;    //读取
char temp_h,temp_l;    //温度上下限制存储变量
uchar state,ms;       //系统设置项变量、50ms定时变量

bit s1,beep1;    //设置闪烁标志位、报警标志位

void delay(uint T)        //系统延时程序
{
    while(T--);
}

//延时函数,i=1时,大约延时10us
void delay10us(uint c)
{
    uchar a,b;
    for (; c>0; c--)
    {
         for (b=199;b>0;b--)
         {
              for(a=1;a>0;a--);
         }      
    }    
}

void Read_ad()
{

  static uint AD_DAT=0;         
    float ad_buf=0;                
    static uchar i=0;            //定义三个变量
    if(i<5)                 //累加,然后除以5,求取5次的平均值
    {
        i++;
        AD_DAT+=A_D();        // 波
    }
    else                    
    {
        i=0;                //清零,方便下次计算 
        ad_dat1=(uchar)(AD_DAT/5);         //求取平均值
        ad_buf=(float)(ad_dat1/0.51);     
        DY_dat=(uint)(ad_buf);             
        AD_DAT=0;                         //清零
    }
}
//1602写如数据函数
void LCD1602_write(uchar order,dat)                  //1602 一个字节  
{
    LCD1602_e=0;
    LCD1602_rs=order;
    LCD1602_dat=dat;
    LCD1602_rw=0;
    LCD1602_e=1;
    delay(1);
    LCD1602_e=0;                                                                                                     
}

//1602写入数据函数  指针式
void LCD1602_writebyte(uchar *prointer)                
{
    while(*prointer!='\0')
    {
        LCD1602_write(1,*prointer);
        prointer++;
    }
}

//初始化1602液晶 
void LCD1602_cls()                
{
    LCD1602_write(0,0x01);     //1602 清屏 指令
    delay(1500);
    LCD1602_write(0,0x38);     // 功能设置 8位、5*7点阵
    delay(1500);
    LCD1602_write(0,0x0c);     //设置 光标   
    LCD1602_write(0,0x06);
    LCD1602_write(0,0xd0);
    delay(1500);
}

//LCD1602液晶显示程序 
void show()            //显示数据
{
        LCD1602_write(0,0x80);           //显示位置设定
        LCD1602_writebyte("Temp:");        //显示当前温度值
        if(temp>=0)             //显示温度整数部分
        {
          if(temp>999)LCD1602_write(1,0x30+temp/1000%10);       
            else LCD1602_writebyte(" ");
            if(temp>99)LCD1602_write(1,0x30+temp/100%10);
            else LCD1602_writebyte(" ");
            LCD1602_write(1,0x30+temp/10%10);
            LCD1602_writebyte(".");
               LCD1602_write(1,0x30+temp%10);
        }else
        {
            LCD1602_writebyte("-");      //显示温度负数部分
            if(temp*-1>99)LCD1602_write(1,0x30+(temp*-1)/100%10);
            else LCD1602_writebyte(" ");
            LCD1602_write(1,0x30+(temp*-1)/10%10);    
            LCD1602_writebyte(".");
               LCD1602_write(1,0x30+(temp*-1)%10);
        }
        LCD1602_write(1,0xdf);
        LCD1602_writebyte("C  ");
        
         LCD1602_write(0,0xC0);     //第二行显示温度上下限值
        LCD1602_writebyte("H:");
        if(state==1&&s1==1)          //对应设置项设置时闪烁控制   //上限
        {
            LCD1602_writebyte("   ");
        }else                       //显示上限值
        {
            if(temp_h>=0)
            {
                   if(temp_h>99)LCD1602_write(1,0x30+temp_h/100%10);
                else LCD1602_writebyte(" ");
                if(temp_h>9)LCD1602_write(1,0x30+temp_h/10%10);
                else LCD1602_writebyte(" ");
                LCD1602_write(1,0x30+temp_h%10);
            }else
            {
                LCD1602_writebyte("-");
                if(temp_h*-1>9)LCD1602_write(1,0x30+(temp_h*-1)/10%10);
                else LCD1602_writebyte(" ");
                LCD1602_write(1,0x30+(temp_h*-1)%10);    
            }
        }
        LCD1602_write(1,0xdf);
        LCD1602_writebyte("C L:");
        if(state==2&&s1==1)      //对应设置项设置时闪烁控制   //下限
        {
            LCD1602_writebyte("   ");
        }else
        {
            if(temp_l>=0)          //显示下限值
            {
                   if(temp_l>99)LCD1602_write(1,0x30+temp_l/100%10);
                else LCD1602_writebyte(" ");
                if(temp_l>9)LCD1602_write(1,0x30+temp_l/10%10);
                else LCD1602_writebyte(" ");
                LCD1602_write(1,0x30+temp_l%10);
            }else
            {
                LCD1602_writebyte("-");
                if(temp_l*-1>9)LCD1602_write(1,0x30+(temp_l*-1)/10%10);
                else LCD1602_writebyte(" ");
                LCD1602_write(1,0x30+(temp_l*-1)%10);    
            }
        }
        LCD1602_write(1,0xdf);
        LCD1602_writebyte("C");
    
}

//系统指示灯与报警处理
void proc()
{
    if(temp>temp_h*10)     //如果当前温度高于上限温度
    {
        led_1=0;      
    }else            
    {
        led_1=1;     
    }
    if(temp<temp_l*10)    //如果当前温度低于下限温度 
    {
        led_2=0;
    }else             
    {
        led_2=1;    
    }
    if(temp>temp_h*10||temp<temp_l*10)    
    {
        beep1=1;  //开始报警
    }else
    {
        beep1=0;  //停止报警
    }
}
// 系统控制按键控制检测程序
void key()
{

    if(!key_1)    //判断按键是否按下
    {
        delay(888);     //延时去抖
        if(!key_1)    
        {
            state=(state+1)%3;        //切换设置项
            while(!key_1);           //按键判断释放
        }
    }
    if(state!=0)        //如果系统非设置状态
    {
        if(!key_2)         
        {
            delay(888);       
            if(!key_2)    
            {
                while(!key_2) show();  
                switch(state) 
                {
                    case 1:                  //如果是上限设置
                    if(temp_h<99)temp_h++;
                    SectorErase(0x2000);     //保存上限值
                    byte_write(0x2000,temp_h);
                    break;
                    case 2:             //如果是下限设置
                    if(temp_h>temp_l+1)temp_l++;    
                    SectorErase(0x2200);     //保存上限值
                    byte_write(0x2200,temp_l);
                    break;
                }
            }
        }
        if(!key_3)    
        {
            delay(888);     
            if(!key_3)    
            {
                while(!key_3) show();  
                switch(state)     //判断当前设置项
                {
                    case 1:                 //如果是上限设置
                    if(temp_h>temp_l+1)temp_h--;
                    SectorErase(0x2000);     //保存上限值
                    byte_write(0x2000,temp_h);
                    break;
                    case 2:               //如果是下限设置
                    if(temp_l>-40)temp_l--;
                    SectorErase(0x2200);     //保存上限值
                    byte_write(0x2200,temp_l);
                    break;

                }
            }
        }
    }
}
float TemperatureCalculate(float Rx,float B,float Revise,float BasicRx){
/*
Rx:  热敏电阻当前阻值
B:   热敏电阻参数B值
Revise:  校正温度
BasicRx:  热敏电阻25度时电阻(标称电阻数值)
*/  
    Rx = Rx / BasicRx;
    Rx = log(Rx);
    Rx = (Rx) / B;
    Rx = Rx + 0.003356;
    Rx = 1 / Rx;
    Rx = Rx - 273.13;  
    Rx = Rx + Revise;
    return Rx;
  
}

#define B 3950.0//温度系数
#define TN 298.15//额定温度(绝对温度加常温:273.15+25)
#define RN 10// 额定阻值(绝对温度时的电阻值10k)
#define BaseVcc 5.04 //ADC基准电压
float GetTemperature(uint adc)
{
        float RV,RT,Tmp;
        RV=BaseVcc/256.0*(float)adc;//ADC为10位ADC,求出NTC电压:RV=ADCValu/1024*BaseVcctag
        RT=RV*10/(BaseVcc-RV);//求出当前温度阻值 (BaseVcctage-RV)/R16=RV/RT;
        Tmp=1/(1/TN+(log(RT/RN)/B))-273.15;//%RT = RN exp*B(1/T-1/TN)%
         return Tmp;
}

void main()
{
    float Rad;     //生成运算暂存变量
    LCD1602_cls();
    TMOD=0x01;     
    TH0=0x4c;
    TL0=0x00;     
    ET0=1;        
    TR0=1;      
    EA=1;        
    temp_h=byte_read(0x2000);  //读取EEPROM中存储的温度上下限值
    temp_l=byte_read(0x2200);
    if(temp_h>99||temp_l>99||temp_l>=temp_h) //判断读取的温度上下限值是否错
    {
        temp_h=30;
        temp_l=10;
    }
    while(1)
    {
        Read_ad();       //读取AD数据
        adc_value=(float)A_D();     
        //Rad=adc_value/((5.0-adc_value)/10000.0);   //计算热敏电阻当前阻值
        //temp=(int)((temp * 0.5) + (TemperatureCalculate(Rad,3950.0,0,10000.0) * 0.5)*10);//求出当前系统的当前温度
        temp=GetTemperature(adc_value);
        show();         //显示函数
        delay10us(100);
        key();         //按键处理程序
        proc();         //超限处理程序
    }
}
//定时器0中断服务程序
void UART_1() interrupt 1 
{
    TH0=0x4c;    //重新赋值
    TL0=0x00;
    ms++;      //50ms定时变量计时
    if(ms%5==0)      //区分250ms定时
    {
        s1=!s1;        
        if(beep1==1) beep=!beep;  //判断报警标志位是否报警   
        else    beep=1;
    }
    if(ms>19)    //1s定时
    {
        ms=0;        
    }
} 

2.eeprom52

代码如下:

#ifndef _EEPROM52_H_
#define _EEPROM52_H_
#include <intrins.h>

/********STC89C52扇区分布*******
第一扇区:2000H--21FF
第二扇区:2200H--23FF
第三扇区:2400H--25FF
第四扇区:2600H--27FF
第五扇区:2800H--29FF
第六扇区:2A00H--2BFF
第七扇区:2C00H--2DFF
第八扇区:2E00H--2FFF
*****************/

#define RdCommand 0x01 //定义ISP的操作命令
#define PrgCommand 0x02
#define EraseCommand 0x03 
#define Error 1
#define Ok 0
#define WaitTime 0x01 //定义CPU的等待时间
sfr ISP_DATA=0xe2;  //寄存器申明
sfr ISP_ADDRH=0xe3;
sfr ISP_ADDRL=0xe4;
sfr ISP_CMD=0xe5;
sfr ISP_TRIG=0xe6;
sfr ISP_CONTR=0xe7;

/* ================ 打开 ISP,IAP 功能 ================= */
void ISP_IAP_enable(void)
{
     EA = 0;       /* 关中断   */
     ISP_CONTR = ISP_CONTR & 0x18;       /* 0001,1000 */
     ISP_CONTR = ISP_CONTR | WaitTime; /* 写入硬件延时 */
     ISP_CONTR = ISP_CONTR | 0x80;       /* ISPEN=1  */
}
/* =============== 关闭 ISP,IAP 功能 ================== */
void ISP_IAP_disable(void)
{
     ISP_CONTR = ISP_CONTR & 0x7f; /* ISPEN = 0 */
     ISP_TRIG = 0x00;
     EA   =   1;   /* 开中断 */
}
/* ================ 公用的触发代码 ==================== */
void ISPgoon(void)
{
     ISP_IAP_enable();   /* 打开 ISP,IAP 功能 */
     ISP_TRIG = 0x46;  /* 触发ISP_IAP命令字节1 */
     ISP_TRIG = 0xb9;  /* 触发ISP_IAP命令字节2 */
     _nop_();
}
/* ==================== 字节读 ======================== */
unsigned char byte_read(unsigned int byte_addr)
{
    EA = 0;
     ISP_ADDRH = (unsigned char)(byte_addr >> 8);/* 地址赋值 */
     ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff);
     ISP_CMD   = ISP_CMD & 0xf8;   /* 清除低3位  */
     ISP_CMD   = ISP_CMD | RdCommand; /* 写入读命令 */
     ISPgoon();       /* 触发执行  */
     ISP_IAP_disable();    /* 关闭ISP,IAP功能 */
     EA  = 1;
     return (ISP_DATA);    /* 返回读到的数据 */
}
/* ================== 扇区擦除 ======================== */
void SectorErase(unsigned int sector_addr)
{
     unsigned int iSectorAddr;
     iSectorAddr = (sector_addr & 0xfe00); /* 取扇区地址 */
     ISP_ADDRH = (unsigned char)(iSectorAddr >> 8);
     ISP_ADDRL = 0x00;
     ISP_CMD = ISP_CMD & 0xf8;   /* 清空低3位  */
     ISP_CMD = ISP_CMD | EraseCommand; /* 擦除命令3  */
     ISPgoon();       /* 触发执行  */
     ISP_IAP_disable();    /* 关闭ISP,IAP功能 */
}
/* ==================== 字节写 ======================== */
void byte_write(unsigned int byte_addr, unsigned char original_data)
{
     EA  = 0;
//     SectorErase(byte_addr);
     ISP_ADDRH = (unsigned char)(byte_addr >> 8);  /* 取地址  */
     ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff);
     ISP_CMD  = ISP_CMD & 0xf8;    /* 清低3位 */
     ISP_CMD  = ISP_CMD | PrgCommand;  /* 写命令2 */
     ISP_DATA = original_data;   /* 写入数据准备 */
     ISPgoon();       /* 触发执行  */
     ISP_IAP_disable();     /* 关闭IAP功能 */
     EA =1;
}
        
#endif

3.ADC0832

代码如下:

sbit ADC0832_CS=P1^0;
sbit ADC0832_CLK=P1^2;
sbit ADC0832_DIO=P1^1;
 
unsigned int A_D()
{
   unsigned char i,dat;
   ADC0832_CS=1;   //一个转换周期开始
   ADC0832_CLK=0;  //为第一个脉冲作准备
   ADC0832_CS=0;  //CS置0,片选有效

   ADC0832_DIO=1;    //DIO置1,规定的起始信号  
   ADC0832_CLK=1;   //第一个脉冲
   ADC0832_CLK=0;   //第一个脉冲的下降沿,此前DIO必须是高电平
   ADC0832_DIO=1;   //DIO置1, 通道选择信号  
   ADC0832_CLK=1;   //第二个脉冲,第2、3个脉冲下沉之前,DI必须跟别输入两位数据用于选择通道,这里选通道CH0 
   ADC0832_CLK=0;   //第二个脉冲下降沿 
   ADC0832_DIO=0;   //DI置0,选择通道0
   ADC0832_CLK=1;    //第三个脉冲
   ADC0832_CLK=0;    //第三个脉冲下降沿 
   ADC0832_DIO=1;    //第三个脉冲下沉之后,输入端DIO失去作用,应置1
   ADC0832_CLK=1;    //第四个脉冲
   for(i=0;i<8;i++)  //高位在前
    {
      ADC0832_CLK=1;         //第四个脉冲
      ADC0832_CLK=0; 
      dat<<=1;       //将下面储存的低位数据向右移
        dat|=(unsigned char)ADC0832_DIO;      //将输出数据DIO通过或运算储存在dat最低位 
    }                      
    ADC0832_CS=1;          //片选无效 
    return dat;     //将读书的数据返回     
  }
    暂无数据