标题:
单片机酒精检测程序Proteus仿真原理图 24C02掉电保存
[打印本页]
作者:
梦不虚存
时间:
2022-5-8 14:26
标题:
单片机酒精检测程序Proteus仿真原理图 24C02掉电保存
用可变电阻来模拟MQ-3酒精传感器
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
51hei.gif
(99.77 KB, 下载次数: 54)
下载附件
2022-5-8 15:35 上传
单片机源程序如下:
#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)
2022-5-8 14:12 上传
点击文件名下载附件
下载积分: 黑币 -5
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1