用可變電阻來模擬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
|