找回密码
 立即注册
查看: 133|回复: 0

基于STC15W408AS单片机设计的MQ系列气体传感器报警控制板+原理图+程序

[复制链接]

该用户从未签到

42

主题

7

回帖

246

积分

中级会员

积分
246
发表于 2023-7-10 10:17:14 | 显示全部楼层 |阅读模式
DSC_0283.JPG

标示图.jpg

接线图.jpg

设置说明.jpg

GYJ-0254 气体传感器_00.jpg

GYJ-0254 气体传感器_01.jpg

GYJ-0254 气体传感器_02.jpg


/********************************************************************
实现功能:应用程序

使用芯片:STC15W408AS
晶振:11.0592MHZ
波特率:9600
编译环境:Keil 4
【声明】此程序仅用于学习与参考!     

*********************************************************************/
/********************************************************************
                            宏定义
*********************************************************************/
#include<STC15W408AS.h>                 //库文件
#include<intrins.h>
#define uchar unsigned char//宏定义无符号字符型
#define uint unsigned int  //宏定义无符号整型
#define ulong unsigned long  //宏定义无符号整型

#define ADC_POWER   0x80            //ADC 电源控制位
#define ADC_FLAG    0x10            //ADC 转换结束标志位
#define ADC_START   0x08            //ADC 开始转换控制位
#define ADC_SPEEDLL 0x00            //210 个时钟周期转换一次
#define ADC_SPEEDL  0x20            //420 个时钟周期转换一次
#define ADC_SPEEDH  0x40            //630 个时钟周期转换一次
#define ADC_SPEEDHH 0x60            //840 个时钟周期转换一次

typedef unsigned char  INT8U;
typedef unsigned int   INT16U;
#define ENABLE_ISP 0x82 //系统工作时钟<20MHz 时,对IAP_CONTR 寄存器设置此值
#define WD1        0x5a        //使用STC11xx系列单片机时,先写入0x5a,然写入0xa5
#define WD2        0xa5
char IAPAddr=0;
uint AD_H=0,AD_L=0,AD_Dat=0;
/********************************************************************
                            初始定义
*********************************************************************/
//code uchar seg7code[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; //显示段码 数码管字跟
code uchar seg7code[10]={0x7E,0x0C,0xB6,0x9E,0xCC,0xDA,0xFA,0x0E,0xFE,0xDE}; //显示段码 数码管字跟
/********************************************************************
                            I/O定义
*********************************************************************/
bit z=0,ba=0,zs=0,kz=0;
uchar y=0,smg2=0;
uchar s1=0,s2=0,s3=0,qian=0,bai=0,shi=0,ge=0,js=0;
uchar s11=0,s22=0,s33=0;
ulong dat=0,dat1=0,dat2=0,dat3=0,dat4=0,dat5=0;
sbit aj1=P5^4;
sbit aj2=P5^5;
sbit out=P1^7;
sbit L1=P1^4;//数码管位控制
sbit L2=P1^3;//数码管位控制
sbit L3=P1^2;//数码管位控制
sbit dp=P1^0;//小数点
sbit CS=P1^5;//测试脚
bit pdbz=0;

uchar jsz=0; //AD计数值
bit bz1=0; //按键标志
bit bz2=0; //按键标志
bit smbz=0;//扫描标志
uint date;//设置报警阀值
bit write=0;//EEPROM存储标志位
xdata uint receBuf[50]; //接收缓冲区
uchar r=0;//读取AD计数
bit flag_zx=0;
ulong sum=0;//AD值总和
uchar i=0;
bit qlbz=0;//触发清零标志
/********************************************************************
                            E2P函数
*********************************************************************/
union union_temp16
{
    INT16U un_temp16;
    INT8U  un_temp8[2];
}
my_unTemp16;
INT8U Byte_Read(INT16U add);              //读一字节,调用前需打开IAP 功能
void Byte_Program(INT16U add, INT8U ch);  //字节编程,调用前需打开IAP 功能
void Sector_Erase(INT16U add);            //擦除扇区
void IAP_Disable();                       //关闭IAP 功能
/********************************************************************
                         AD转换初始化程序
*********************************************************************/
void InitADC()
{
   P1ASF = 0x40;          //设置P1.7口AD转换,必须加
   P1M0=0X40;
   P1M1=0X40;        //设置P1.7口为开漏模式,使用AD功能
    ADC_RES = 0;                   //AD数据寄存器清空
    ADC_CONTR = ADC_POWER | ADC_SPEEDLL;//打开AD电源,转换周期210
    _nop_();                        //延时一个机器周期
    _nop_();
        _nop_();                        //延时一个机器周期
}
void  delay(uchar z)
{
   uchar i,j;
        for(i=0;i<z;i++)
        {
          for(j=130;j>0;j--);
        }       
}
/********************************************************************
                         AD转换控制程序
*********************************************************************/
uint ADCRead(uchar px)          //转换输出的数据 (PX为通道口)
{
    ADC_CONTR = ADC_POWER | ADC_SPEEDLL |px| ADC_START;//开始转换
    _nop_();                        //延时一个机器周期
    _nop_();                                                //延时一个机器周期
    _nop_();                                                //延时一个机器周期
    _nop_();                                                //延时一个机器周期
    while (!(ADC_CONTR & ADC_FLAG));//等待转换结束
    ADC_CONTR &= ~ADC_FLAG;         //关闭AD转换
        AD_H = ADC_RES;                                        // 修改的 注:把高8位AD值付给AD_H
        AD_H <<= 2;                                           // 修改的  注:高八位左移2位
        AD_L = ADC_RESL & 0x03;                   // 修改的  注:取低两位
        AD_Dat =        AD_H | AD_L;          // 修改的          注:把高八位和低两位合并在一起
    return AD_Dat;                //返回数据
}

/*******************************************************************
*                        按键函数
********************************************************************/
void key()
{
if((aj1==0)&&(bz1==0)){delay(10);if((aj1==0)&&(bz1==0))          //位选
{
smbz=0;bz1=1;z=1;y++;if(y==5){z=0;y=0;date=s3*100+s2*10+s1;write=1;}smg2=y;if(y==4){smbz=1;}}}
if((aj1==1)&&(bz1==1)){delay(10);if((aj1==1)&&(bz1==1)){bz1=0;}}

if((aj2==0)&&(bz2==0)) //段选
{
delay(10);if((aj2==0)&&(bz2==0))
{

if(y==1){smg2=1;s1++;if(s1>9){s1=0;}}
if(y==2){smg2=2;s2++;if(s2>9){s2=0;}}
if(y==3){smg2=3;s3++;if(s3>9){s3=0;}}
bz2=1;
}

}
if((aj2==1)&&(bz2==1)){delay(10);if((aj2==1)&&(bz2==1)){bz2=0;}} }
                                                                                                                                                                                       
/*******************************************************************
*                        定时器配置
********************************************************************/
void ConfigTimer0(){
TMOD=0x01;//将定时器0,1都设置为模式1
TH0=0XFC;//1ms
TL0=0X66;
TR0=1;//开启定时器0
ET0=1;//开定时器0的中断
EA=1;//开总中断
}

/*******************************************************************
*                         显示 1
********************************************************************/
void led(uint date)
{
bai=date%1000/100;
shi=date%100/10;
ge=date%10;
}
/*******************************************************************
*                         显示2
********************************************************************/
//void dispay(uint date)
//{
// bai=date/100;
// shi=date%100/10;
// ge=date%10;
//}
/*******************************************************************
*                         t0定时器
********************************************************************/
void timer0() interrupt 1
{
TH0=0XFC;//1ms
TL0=0X66;
js++;
if(js==200){ba=1;js=0;}
if(z==0){
smg2++;
switch(smg2){                 //数码管扫描
/**************数码管-开始*****************/
case 1:  P3=seg7code[ge]; L3=1;L2=1;L1=0;dp=0;break;//从P2进P0出
case 2:  P3=seg7code[shi];L3=1;L2=0;L1=1;dp=0;break;
case 3:  P3=seg7code[bai];L3=0;L2=1;L1=1;dp=0;break;
/**************数码管-结束*****************/       
default: smg2=0; L3=1;L2=1;L1=1; break;
}
}
else{
if(smbz==1){smg2++;}else {smg2=y;}
switch(smg2){                 //数码管扫描
/**************数码管-开始*****************/
case 1:  P3=seg7code[s1];L3=1;L2=1;L1=0;dp=0;break;//从P2进P0出
case 2:  P3=seg7code[s2];L3=1;L2=0;L1=1;dp=0;break;
case 3:  P3=seg7code[s3];L3=0;L2=1;L1=1;dp=0;break;
/**************数码管-结束*****************/       
default: smg2=0; L3=1;L2=1;L1=1; break;
}
}

}
/********************************************************************
                            主函数
*********************************************************************/
void main()
{
ConfigTimer0();        //定时器初始化
InitADC();           //AD初始化
P3M0 = 0xff;   //设置强推挽和开漏模式
P3M1 = 0x00;
P1M0 = 0x40;   //小数点使用
P1M1 = 0x40;
if(s1>9){s1=0;}
if(s2>9){s1=0;}
if(s3>9){s1=0;}
if(Byte_Read(0X0001)==0xff){s1=0;s2=0;s3=0;}//首次读取,如果读到0xFF说明没有存过数据,直接付给00值
else
{
s1=Byte_Read(0X0001);
s2=Byte_Read(0X0002);
s3=Byte_Read(0X0003);
}
date=s3*100+s2*10+s1;
while(1)
{

jsz++;
if(jsz>=20){ dat=ADCRead(6);jsz=0; }
/////****************去掉两个最大值和两个最小值求平均值*********************************/
////receBuf[r++]=dat1;     //把接受的数据存储到BUT数组中
////
////if(r>=7)
////{       
////r=0;
////flag_zx=1;
////}
////if(flag_zx==1)
////{
////flag_zx=0;
////for(i=0; i<7-1; i++)
////{
////每一轮比较前n-1-i个,即已排序好的最后i个不用比较
////for( j=0; j<7-1-i; j++)
////{
////if(receBuf[j] > receBuf[j+1])
////{
////temp = receBuf[j];
////receBuf[j] = receBuf[j+1];
////receBuf[j+1]=temp;
////}
////}
//
///****************取100个数值求平均数*********************************/
receBuf[r++]=dat;     //把接受的数据存储到BUT数组中

if(r>=100)
{       
r=0;
sum=0;
flag_zx=1;
}
if(flag_zx==1)
{
flag_zx=0;
for(i=0; i<100; i++)
{
sum=sum+receBuf;
}
dat2=sum/100;
}

key();
dat4=dat2;
if(CS==0){dat3=0;qlbz=1;dat5=dat4;}
if(dat4>dat5){qlbz=0;}
if(qlbz==1){led(dat3);        }

if((y==0)&&(z==0)&&(qlbz==0))
{
if((dat2>=(dat3+32))||(dat2<(dat3+29)))
{
dat3=dat2-30;led(dat3);
}
} //zs标志位控制小数点

          if(write==1)
          {
             Sector_Erase(0);
                 Byte_Program(0x0001,s1);  //写入扇区
                 Byte_Program(0x0002,(s2));  //写入扇区
                 Byte_Program(0x0003,(s3));  //写入扇区
                 write=0;
          }
        }
}

////          if((dat2>s)&&(pdbz==0)){out=0;dat4=dat2*0.98;pdbz=1;}
////          if((dat2<s)&&(pdbz==1)){out=1;pdbz=0;}          
////          if(dat4<s){out=1;}
//          KeyRead(); //按键扫描


  

//读一字节,调用前需打开IAP 功能,入口:DPTR = 字节地址,返回:A = 读出字节
INT8U Byte_Read(INT16U add)
{
    IAP_DATA = 0x00;
    IAP_CONTR = ENABLE_ISP;         //打开IAP 功能, 设置Flash 操作等待时间
    IAP_CMD = 0x01;                 //IAP/ISP/EEPROM 字节读命令

    my_unTemp16.un_temp16 = add;
    IAP_ADDRH = my_unTemp16.un_temp8[0];    //设置目标单元地址的高8 位地址
    IAP_ADDRL = my_unTemp16.un_temp8[1];    //设置目标单元地址的低8 位地址

    //EA = 0;
    IAP_TRIG = WD1;   //先送 WD1,再送WD2 到ISP/IAP 触发寄存器,每次都需如此
    IAP_TRIG = WD2;   //送完WD2 后,ISP/IAP 命令立即被触发起动
    _nop_();
    //EA = 1;
    IAP_Disable();  //关闭IAP 功能, 清相关的特殊功能寄存器,使CPU 处于安全状态,
                    //一次连续的IAP 操作完成之后建议关闭IAP 功能,不需要每次都关
    return (IAP_DATA);
}
/*********************************************************************************************/
//字节编程,调用前需打开IAP 功能,入口:DPTR = 字节地址, A= 须编程字节的数据
void Byte_Program(INT16U add, INT8U ch)
{
    IAP_CONTR = ENABLE_ISP;         //打开 IAP 功能, 设置Flash 操作等待时间
    IAP_CMD = 0x02;                 //IAP/ISP/EEPROM 字节编程命令

    my_unTemp16.un_temp16 = add;
    IAP_ADDRH = my_unTemp16.un_temp8[0];    //设置目标单元地址的高8 位地址
    IAP_ADDRL = my_unTemp16.un_temp8[1];    //设置目标单元地址的低8 位地址

    IAP_DATA = ch;                  //要编程的数据先送进IAP_DATA 寄存器
    //EA = 0;
    IAP_TRIG = WD1;   //先送 WD1,再送WD2 到ISP/IAP 触发寄存器,每次都需如此
    IAP_TRIG = WD2;   //送完WD2 后,ISP/IAP 命令立即被触发起动
    _nop_();
    //EA = 1;
    IAP_Disable();  //关闭IAP 功能, 清相关的特殊功能寄存器,使CPU 处于安全状态,
                    //一次连续的IAP 操作完成之后建议关闭IAP 功能,不需要每次都关
}
/*********************************************************************************************
//擦除扇区, 入口:DPTR = 扇区地址 */
void Sector_Erase(INT16U add)
{
    IAP_CONTR = ENABLE_ISP;         //打开IAP 功能, 设置Flash 操作等待时间
    IAP_CMD = 0x03;                 //IAP/ISP/EEPROM 扇区擦除命令

    my_unTemp16.un_temp16 = add;
    IAP_ADDRH = my_unTemp16.un_temp8[0];    //设置目标单元地址的高8 位地址
    IAP_ADDRL = my_unTemp16.un_temp8[1];    //设置目标单元地址的低8 位地址

    //EA = 0;
    IAP_TRIG = WD1;   //先送 WD1,再送WD2 到ISP/IAP 触发寄存器,每次都需如此
    IAP_TRIG = WD2;   //送完WD2 后,ISP/IAP 命令立即被触发起动
    _nop_();
    //EA = 1;
    IAP_Disable();  //关闭IAP 功能, 清相关的特殊功能寄存器,使CPU 处于安全状态,
                    //一次连续的IAP 操作完成之后建议关闭IAP 功能,不需要每次都关
}
/*********************************************************************************************/
void IAP_Disable()
{
    //关闭IAP 功能, 清相关的特殊功能寄存器,使CPU 处于安全状态,
    //一次连续的IAP 操作完成之后建议关闭IAP 功能,不需要每次都关
    IAP_CONTR = 0;      //关闭IAP 功能
    IAP_CMD   = 0;      //清命令寄存器,使命令寄存器无命令,此句可不用
    IAP_TRIG  = 0;      //清命令触发寄存器,使命令触发寄存器无触发,此句可不用
    IAP_ADDRH = 0;
    IAP_ADDRL = 0;
}
/*********************************************************************************************/



回复 送花

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2024-5-22 05:16 , Processed in 0.055307 second(s), 31 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表