用可变电阻来模拟MQ-3酒精传感器
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
单片机源程序如下:
- #include<reg52.h>
- #include <intrins.h>
- #define uchar unsigned char
- #define uint unsigned int
- #define nops4(); {_nop_();_nop_();_nop_();_nop_();}//4个空操作延时
- #define add_write 0x90
- #define add_rec 0x91
- #define control_byte 0x03
- #define u16 unsigned char
- #define u8 unsigned int
- #define lcd_data P0
- sbit pe=P3^4;
- sbit sda=P2^4; //IO口定义
- sbit scl=P2^3;
- sbit green=P2^5;
- sbit red=P2^6;
- sbit yellow=P2^7;
- sbit k1=P3^5;
- sbit k2=P3^6;
- sbit k3=P3^7;
- sbit SDA=P1^1;
- sbit SCL=P1^0;
- sbit RS = P2^0;
- sbit RW = P2^1;
- sbit E = P2^2;
- sbit bf = P0^7;
- bit flag=0,flag2=0;
- unsigned int voldata1;
- unsigned char i,j,k,m=0,tt=0;
- unsigned int vol;
- unsigned char num[]={"0,1,2,3,4,5,6,7,8,9"};
- unsigned char character[]="alcohol:";
- unsigned char character2[]="warn:";
- unsigned char hdata=60,ldata=20;
- extern voldate1;
- unsigned char voldate;
- void nop()
- {
- _nop_();
- _nop_();
- }
- void delay(unsigned char i) //延时程序
- {
- for(j=i;j>0;j--)
- for(k=125;k>0;k--);
- }
- void delay1s(void) //误差 0us
- {
- unsigned char a,b,c;
- for(c=167;c>0;c--)
- for(b=171;b>0;b--)
- for(a=16;a>0;a--);
- _nop_(); //if Keil,require use intrins.h
- }
- void delay1ms(void) //误差 0us
- {
- unsigned char a,b,c;
- for(c=1;c>0;c--)
- for(b=142;b>0;b--)
- for(a=2;a>0;a--);
- }
- void delay10ms(void) //误差 0us
- {
- unsigned char a,b,c;
- for(c=1;c>0;c--)
- for(b=38;b>0;b--)
- for(a=130;a>0;a--);
- }
- void delay10us(void) //误差 0us
- {
- unsigned char a,b;
- for(b=1;b>0;b--)
- for(a=2;a>0;a--);
- }
- void delay100ms(void) //误差 0us
- {
- unsigned char a,b,c;
- for(c=19;c>0;c--)
- for(b=20;b>0;b--)
- for(a=130;a>0;a--);
- }
- void iic_start()
- {
- SCL = 1;
- SDA = 1;
- nops4();
- SDA = 0;
- nops4();
- SCL = 0;
- }
- //IIC停止函数
- void iic_stop()
- {
- SCL = 0;
- SDA = 0;
- nops4();
- SCL = 1;
- nops4();
- SDA = 1;
- nops4();
- }
- /*
- * 函数: void iic_sendACK(bit ack_back)
- 功能: 主机读完数据后是否向从机发送应答信号
- *ck为1时发送应答信号ACK, SDA拉低,继续通信
- *ck为0时不发送ACK,SDA置1,结束通信*/
- void iic_sendACK(bit ack_back)
- {
- if(ack_back)
- SDA = 0; //应答,SDA拉低,继续通信
- else
- SDA = 1; //非应答,SDA置1,结束通信
- nops4();
- SCL = 1;
- nops4();
- SCL = 0;
- nops4();
- SDA = 1;
- }
- /*主机写字节后检测读取从机发送的应答(写应答)*/
- bit iic_recACK()
- {
- unsigned char i=0;
- SDA = 1; //先拉高SDA,等待检测
- nops4();
- SCL = 1;
- nops4();
- while((1==SDA)&&(i<255)) i++; //SDA为1时,循环检测255次
- if(SDA) //非应答,拉低SCL,停止,返回1
- {
- SCL = 0;
- iic_stop();
- return 1;
- }
- else //应答,拉低SCL,返回1
- {
- SCL = 0;
- return 0;
- }
- }
- /*主机发送1字节数据给从机*/
- /*从最高位开始发送*/
- void iic_sendbyte(unsigned char byt)
- {
- unsigned char i;
- for(i=0;i<8;i++)
- {
- if(byt&0x80) //判断最高位,并赋予SDA
- SDA = 1;
- else
- SDA = 0;
- nops4();
- SCL = 1; //SCL高电平,SDA数据稳定,发送
- nops4();
- byt<<=1; //发送完成,字节左移
- SCL = 0;
- }
- }
- /*主机读取1字节数据*/
- /*从高位接收,存放在低位*/
- unsigned char iic_recbyte()
- {
- unsigned char i,byt;
- for(i=0;i<8;i++)
- {
- SCL = 1; //SCL高电平,SDA数据稳定
- nops4();
- byt<<=1; //接收数据左移
- if(SDA) //判断接收数据,并赋给byt,1则+1,0则保持0;
- byt = byt|0x01;
- SCL = 0; //拉低SCL,准备接收下一位数据
- nops4();
- }
- return byt; //读取字节完毕,返回读取值
- }
- unsigned char iic_readvoldata()
- {
- iic_start(); //起始
- iic_sendbyte(add_write); //发送“写”地址
- if(!iic_recACK()) //应答判断
- {
- iic_sendbyte(control_byte); //发送控制字
- if(!iic_recACK()) //应答判断
- {
- iic_start(); //起始
- iic_sendbyte(add_rec); //发送“读”地址
- if(!iic_recACK()) //应答判断
- {
- voldata1 = iic_recbyte(); //读取A/D值
- iic_sendbyte(0); //不发送应答
- iic_stop(); //停止
- }
- }
- }
- return voldata1; //返回A/D值
- }
- bit lcd_busytest() //忙碌检测
- {
- bit result; //定义检测结果变量
- RS = 0; //从CGRAM或DDRAM读取,RS=1,RW=1,E高电平有效
- RW = 1;
- E = 1;
- nops4(); //空操作,给硬件反应时间
- result = bf; //读取忙碌标志
- E = 0; //使能信号复位
- return result; //返回忙碌标志值,0空闲,1忙碌
- }
- void lcd_writecmd(unsigned char cmd)
- {
- while(lcd_busytest()==1); //忙碌检测
- RS = 0; //写指令RS=0,RW=0,E一个脉冲
- RW = 0;
- E = 0; //E先置零
- nops4();
- lcd_data = cmd; //将要写的数据给I/O口
- nops4();
- E = 1;
- nops4();
- E = 0; //E置1,再置零,执行写操作有效
- }
- void lcd_writeadd(unsigned char add) //地址指令需要在原地址上+0x80
- {
- lcd_writecmd(add|0x80);
- }
- void lcd_writedata(unsigned char dat)
- {
- while(lcd_busytest()==1); //忙碌检测
- RS = 1;
- RW = 0;
- E = 0; //E先置零
- nops4();
- lcd_data = dat; //将要写的数据给I/O口
- nops4();
- E = 1;
- nops4();
- E = 0; //E置1,再置零,执行写操作有效
- }
- void lcd_init()
- {
- delay1ms(); //延时15ms,首次写指令时应给LCD一段较长的反应时间
- lcd_writecmd(0x38); //指令6,显示模式设置:16×2显示,5×7点阵,8位数据接口
- delay1ms();
- lcd_writecmd(0x38);
- delay1ms();
- lcd_writecmd(0x38);
- delay1ms();
- lcd_writecmd(0x0f); //指令4显示开关控制,关闭显示
- delay1ms();
- lcd_writecmd(0x01); //指令1,清显示
- delay1ms();
- lcd_writecmd(0x06); //指令3,设置输入模式,写入字符后,光标右移、字符不动
- delay1ms();
- }
- /////////24C02读写驱动程序////////////////////
- void delay1(unsigned char m)
- { unsigned int n;
- for(n=0;n<m;n++);
- }
- void init() //24c02初始化子程序
- {
- scl=1;
- nop();
- sda=1;
- nop();
- }
- void start() //启动I2C总线
- {
- sda=1;
- nop();
- scl=1;
- nop();
- sda=0;
- nop();
- scl=0;
- nop();
- }
- void stop() //停止I2C总线
- {
- sda=0;
- nop();
- scl=1;
- nop();
- sda=1;
- nop();
- }
- void writebyte(unsigned char j) //写一个字节
- {
- unsigned char i,temp;
- temp=j;
- for (i=0;i<8;i++)
- {
- temp=temp<<1;
- scl=0;
- nop();
- sda=CY; //temp左移时,移出的值放入了CY中
- nop();
- scl=1; //待sda线上的数据稳定后,将scl拉高
- nop();
- }
- scl=0;
- nop();
- sda=1;
- nop();
- }
- unsigned char readbyte() //读一个字节
- {
- unsigned char i,j,k=0;
- scl=0; nop(); sda=1;
- for (i=0;i<8;i++)
- {
- nop(); scl=1; nop();
- if(sda==1)
- j=1;
- else
- j=0;
- k=(k<<1)|j;
- scl=0;
- }
- nop();
- return(k);
- }
- void clock() //I2C总线时钟
- {
- unsigned char i=0;
- scl=1;
- nop();
- while((sda==1)&&(i<255))
- i++;
- scl=0;
- nop();
- }
- ////////从24c02的地址address中读取一个字节数据/////
- unsigned char read24c02(unsigned char address)
- {
- unsigned char i;
- start();
- writebyte(0xa0);
- clock();
- writebyte(address);
- clock();
- start();
- writebyte(0xa1);
- clock();
- i=readbyte();
- stop();
- delay1(100);
- return(i);
- }
- //////向24c02的address地址中写入一字节数据info/////
- void write24c02(unsigned char address,unsigned char info)
- {
- start();
- writebyte(0xa0);
- clock();
- writebyte(address);
- clock();
- writebyte(info);
- clock();
- stop();
- delay1(5000); //这个延时一定要足够长,否则会出错。因为24c02在从sda上取得数据后,还需要一定时间的烧录过程。
- }
- void keyscan() //按键扫描程序
- {
- if(k1==0)
- {
- delay1ms();
- if(k1==0)
- {
- flag=!flag;
-
- }
- while(!k1);
- }
- if(flag==0)
- {
- if(k2==0)
- {
- delay1ms();
- if(k2==0)
- {
- ldata++;
- }
- }
- if(k3==0)
- {
- delay1ms();
- if(k3==0)
- {
- if( ldata>0)
- ldata--;
- }
- }
- }
- if(flag==1)
- {
- if(k2==0)
- {
- delay1ms();
- if(k2==0)
- {
- hdata++;
- }
- }
- if(k3==0)
- {
- delay1ms();
- if(k3==0)
- {
- if( hdata>0)
- hdata--;
- }
- }
- }
-
- }
- void main()
- { TMOD=0x10; //定时器1定时模式,均16位计数模式
- TH1=(65536-50000)/256; //约每50ms计数1次
- TL1=(65536-50000)%256;
- ET1=1; // 开定时器1中断
- TR1=0; // 定时器1
- EA=1; //开总中断
- init(); //初始化24C02
- lcd_init(); //初始化lcd
- hdata=read24c02(1);
- ldata=read24c02(0);
- pe=0;
- while(1)
- {
- voldate=iic_readvoldata();
- //vol = voldate*5/0.255;
- vol = voldate/2.7;
- if(vol<=7)
- vol=0;
- lcd_writecmd(0x80);
- for(i=0;i<8;i++)
- {
- lcd_writedata(character[i]);
- }
- lcd_writedata(vol/100%10+0x30); //显示酒精浓度
- lcd_writedata(vol/10%10+0x30);
- lcd_writedata(vol%10+0x30);
- lcd_writedata('m');
- lcd_writedata('g');
- lcd_writedata('/');
- lcd_writedata('m');
- lcd_writedata('L');
- lcd_writecmd(0xc0);
- for(i=0;i<5;i++)
- {
- lcd_writedata(character2[i]);
- }
- lcd_writedata(ldata/100%10+0x30); //显示设定的低警示值
- lcd_writedata(ldata/10%10+0x30);
- lcd_writedata(ldata%10+0x30);
- lcd_writedata('m');
- lcd_writedata('g');
- lcd_writedata(' ');
- lcd_writedata(hdata/100%10+0x30); //显示设定的高警示值
- lcd_writedata(hdata/10%10+0x30);
- lcd_writedata(hdata%10+0x30);
- lcd_writedata('m');
- lcd_writedata('g');
-
- delay100ms();
- keyscan();
- if(vol<=ldata) //红绿灯设定
- red=1,green=0,yellow=1;
- if(vol>ldata && vol<=hdata)
- red=1,green=1,yellow=0;
- if(vol>hdata)
- red=0,green=1,yellow=1;
-
-
- write24c02(0,ldata); //写数据到24c02
- delay10ms();
- write24c02(1,hdata);
- if(vol>ldata && vol<hdata )
- pe=1;
- if(vol<ldata)
- pe=0;
- if(vol>hdata )
- pe=1,TR1=1;
- if(vol==hdata)
- pe=0;
- if(tt==1)
- {
- pe=0;
- tt=0;
- m=0;
- TR1=0;
- pe=1;
- }
- }
- }
- void timer1() interrupt 3
- {
- TH1=(65536-50000)/256;
- TL1=(65536-50000)%256;
- m++;
- if(m==20)//1s
- { m=0;
- tt++;
- }
- }
复制代码
Keil5代码与Proteus8.8仿真下载:
酒精检测显示.zip
(137.48 KB, 下载次数: 79)
|