一、电路原理图
基于单片机的数据采集系统方案,该方案根据热敏电阻随温度变化而变化的特性,采用串联分压电路。
单片机采集热敏电阻的电压,通过A/D转换将模拟量电压信号转换成数字量电压信号,
经过查表转换得到温度值,控制LCD实时显示温度值。
二、源码
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; //将读书的数据返回
}