找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1701|回复: 0
收起左侧

单片机酒精检测程序Proteus仿真原理图 24C02掉电保存

[复制链接]
ID:150530 发表于 2022-5-8 14:26 | 显示全部楼层 |阅读模式
用可变电阻来模拟MQ-3酒精传感器
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
51hei.gif

单片机源程序如下:
  1. #include<reg52.h>
  2. #include <intrins.h>
  3. #define uchar unsigned char
  4. #define uint unsigned int
  5. #define nops4();                {_nop_();_nop_();_nop_();_nop_();}//4个空操作延时

  6. #define add_write 0x90
  7. #define add_rec 0x91
  8. #define control_byte 0x03
  9. #define u16 unsigned char
  10. #define u8 unsigned int

  11. #define lcd_data P0
  12. sbit pe=P3^4;
  13. sbit sda=P2^4;                          //IO口定义
  14. sbit scl=P2^3;
  15. sbit green=P2^5;
  16. sbit red=P2^6;
  17. sbit yellow=P2^7;
  18. sbit k1=P3^5;
  19. sbit k2=P3^6;
  20. sbit k3=P3^7;
  21. sbit SDA=P1^1;                 
  22. sbit SCL=P1^0;
  23. sbit RS = P2^0;               
  24. sbit RW = P2^1;
  25. sbit E = P2^2;
  26. sbit bf = P0^7;
  27. bit flag=0,flag2=0;

  28. unsigned int voldata1;
  29. unsigned char i,j,k,m=0,tt=0;
  30. unsigned int vol;
  31. unsigned char num[]={"0,1,2,3,4,5,6,7,8,9"};
  32. unsigned char character[]="alcohol:";
  33. unsigned char character2[]="warn:";
  34. unsigned char hdata=60,ldata=20;

  35. extern voldate1;
  36. unsigned char voldate;
  37. void nop()
  38. {
  39.         _nop_();
  40.         _nop_();
  41. }
  42. void delay(unsigned char i)                //延时程序
  43. {
  44.   for(j=i;j>0;j--)
  45.     for(k=125;k>0;k--);
  46. }
  47. void delay1s(void)   //误差 0us
  48. {
  49.     unsigned char a,b,c;
  50.     for(c=167;c>0;c--)
  51.         for(b=171;b>0;b--)
  52.             for(a=16;a>0;a--);
  53.     _nop_();  //if Keil,require use intrins.h
  54. }
  55. void delay1ms(void)   //误差 0us
  56. {
  57.     unsigned char a,b,c;
  58.     for(c=1;c>0;c--)
  59.         for(b=142;b>0;b--)
  60.             for(a=2;a>0;a--);
  61. }
  62. void delay10ms(void)   //误差 0us
  63. {
  64.     unsigned char a,b,c;
  65.     for(c=1;c>0;c--)
  66.         for(b=38;b>0;b--)
  67.             for(a=130;a>0;a--);
  68. }
  69. void delay10us(void)   //误差 0us
  70. {
  71.     unsigned char a,b;
  72.     for(b=1;b>0;b--)
  73.         for(a=2;a>0;a--);
  74. }
  75. void delay100ms(void)   //误差 0us
  76. {
  77.     unsigned char a,b,c;
  78.     for(c=19;c>0;c--)
  79.         for(b=20;b>0;b--)
  80.             for(a=130;a>0;a--);
  81. }

  82. void iic_start()
  83. {
  84.         SCL = 1;
  85.         SDA = 1;
  86.         nops4();
  87.         SDA = 0;
  88.         nops4();
  89.         SCL = 0;
  90. }

  91. //IIC停止函数
  92. void iic_stop()
  93. {
  94.         SCL = 0;
  95.         SDA = 0;
  96.         nops4();
  97.         SCL = 1;
  98.         nops4();
  99.         SDA = 1;
  100.         nops4();
  101. }

  102. /*
  103. * 函数: void iic_sendACK(bit ack_back)
  104. 功能: 主机读完数据后是否向从机发送应答信号
  105. *ck为1时发送应答信号ACK, SDA拉低,继续通信
  106. *ck为0时不发送ACK,SDA置1,结束通信*/

  107. void iic_sendACK(bit ack_back)                                                
  108. {
  109.         if(ack_back)
  110.                 SDA = 0;        //应答,SDA拉低,继续通信
  111.         else
  112.                 SDA = 1;        //非应答,SDA置1,结束通信
  113.         nops4();
  114.         SCL = 1;
  115.         nops4();
  116.         SCL = 0;
  117.         nops4();
  118.         SDA = 1;
  119. }
  120. /*主机写字节后检测读取从机发送的应答(写应答)*/
  121. bit iic_recACK()
  122. {
  123.         unsigned char i=0;
  124.         SDA = 1;                   //先拉高SDA,等待检测
  125.         nops4();
  126.         SCL = 1;
  127.         nops4();
  128.         while((1==SDA)&&(i<255)) i++;        //SDA为1时,循环检测255次
  129.         if(SDA)                          //非应答,拉低SCL,停止,返回1
  130.         {
  131.                 SCL = 0;
  132.                 iic_stop();
  133.                 return 1;        
  134.         }        
  135.         else                          //应答,拉低SCL,返回1
  136.         {
  137.                 SCL = 0;
  138.                 return 0;
  139.         }
  140. }

  141. /*主机发送1字节数据给从机*/
  142. /*从最高位开始发送*/
  143. void iic_sendbyte(unsigned char byt)
  144. {
  145.         unsigned char i;
  146.         for(i=0;i<8;i++)
  147.         {                        
  148.                 if(byt&0x80)          //判断最高位,并赋予SDA
  149.                         SDA = 1;
  150.                 else
  151.                         SDA = 0;
  152.                 nops4();
  153.                 SCL = 1;                          //SCL高电平,SDA数据稳定,发送
  154.                 nops4();
  155.                 byt<<=1;                                  //发送完成,字节左移
  156.                 SCL = 0;
  157.         }        
  158. }

  159. /*主机读取1字节数据*/
  160. /*从高位接收,存放在低位*/
  161. unsigned char iic_recbyte()
  162. {
  163.         unsigned char i,byt;
  164.         for(i=0;i<8;i++)
  165.         {
  166.                 SCL = 1;             //SCL高电平,SDA数据稳定
  167.                 nops4();
  168.                 byt<<=1;                         //接收数据左移
  169.                 if(SDA)                                 //判断接收数据,并赋给byt,1则+1,0则保持0;
  170.                         byt = byt|0x01;
  171.                 SCL = 0;                         //拉低SCL,准备接收下一位数据
  172.                 nops4();
  173.         }
  174.         return byt;                                 //读取字节完毕,返回读取值
  175. }
  176. unsigned char iic_readvoldata()
  177. {
  178.         iic_start();                                                        //起始
  179.         iic_sendbyte(add_write);                                //发送“写”地址
  180.         if(!iic_recACK())                                                //应答判断
  181.         {
  182.                 iic_sendbyte(control_byte);                        //发送控制字
  183.                 if(!iic_recACK())                                        //应答判断
  184.                 {
  185.                         iic_start();                                        //起始
  186.                         iic_sendbyte(add_rec);                        //发送“读”地址
  187.                         if(!iic_recACK())                                //应答判断
  188.                         {
  189.                                 voldata1 = iic_recbyte();        //读取A/D值
  190.                                 iic_sendbyte(0);                        //不发送应答
  191.                                 iic_stop();                                        //停止
  192.                         }               
  193.                 }        
  194.         }
  195.         return voldata1;                                                //返回A/D值
  196. }

  197. bit lcd_busytest()                 //忙碌检测
  198. {
  199.         bit result;                 //定义检测结果变量
  200.         RS = 0;                         //从CGRAM或DDRAM读取,RS=1,RW=1,E高电平有效
  201.         RW = 1;
  202.         E = 1;                  
  203.         nops4();                 //空操作,给硬件反应时间
  204.         result = bf;         //读取忙碌标志
  205.         E = 0;                         //使能信号复位
  206.         return result;         //返回忙碌标志值,0空闲,1忙碌
  207. }

  208. void lcd_writecmd(unsigned char cmd)
  209. {
  210.         while(lcd_busytest()==1);          //忙碌检测
  211.         RS = 0;                                          //写指令RS=0,RW=0,E一个脉冲
  212.         RW = 0;                                          
  213.         E = 0;                                          //E先置零
  214.         nops4();
  215.         lcd_data = cmd;                          //将要写的数据给I/O口
  216.         nops4();
  217.         E = 1;                                          
  218.         nops4();
  219.         E = 0;                                          //E置1,再置零,执行写操作有效
  220. }

  221. void lcd_writeadd(unsigned char add)  //地址指令需要在原地址上+0x80
  222. {
  223.         lcd_writecmd(add|0x80);        
  224. }

  225. void lcd_writedata(unsigned char dat)
  226. {
  227.         while(lcd_busytest()==1);          //忙碌检测
  228.         RS = 1;
  229.         RW = 0;
  230.         E = 0;                                          //E先置零
  231.         nops4();
  232.         lcd_data = dat;                          //将要写的数据给I/O口
  233.         nops4();
  234.         E = 1;                                          
  235.         nops4();
  236.         E = 0;                                          //E置1,再置零,执行写操作有效
  237. }

  238. void lcd_init()
  239. {
  240.         delay1ms();                          //延时15ms,首次写指令时应给LCD一段较长的反应时间
  241.         lcd_writecmd(0x38);                  //指令6,显示模式设置:16×2显示,5×7点阵,8位数据接口
  242.         delay1ms();
  243.         lcd_writecmd(0x38);
  244.         delay1ms();
  245.         lcd_writecmd(0x38);
  246.         delay1ms();
  247.         lcd_writecmd(0x0f);                  //指令4显示开关控制,关闭显示         
  248.         delay1ms();
  249.         lcd_writecmd(0x01);                  //指令1,清显示
  250.         delay1ms();
  251.         lcd_writecmd(0x06);                  //指令3,设置输入模式,写入字符后,光标右移、字符不动
  252.         delay1ms();
  253. }

  254. /////////24C02读写驱动程序////////////////////
  255. void delay1(unsigned char m)
  256. {        unsigned int n;
  257.           for(n=0;n<m;n++);
  258. }



  259. void init()  //24c02初始化子程序
  260. {
  261.         scl=1;
  262.         nop();
  263.         sda=1;
  264.         nop();
  265. }

  266. void start()        //启动I2C总线
  267. {
  268.         sda=1;
  269.         nop();
  270.         scl=1;
  271.         nop();
  272.         sda=0;
  273.         nop();
  274.         scl=0;
  275.         nop();
  276. }

  277. void stop()         //停止I2C总线
  278. {
  279.         sda=0;
  280.         nop();
  281.         scl=1;
  282.         nop();
  283.         sda=1;
  284.         nop();
  285. }

  286. void writebyte(unsigned char j)  //写一个字节
  287. {
  288.         unsigned char i,temp;
  289.            temp=j;
  290.            for (i=0;i<8;i++)
  291.    {
  292.            temp=temp<<1;
  293.            scl=0;
  294.            nop();
  295.            sda=CY;                //temp左移时,移出的值放入了CY中
  296.            nop();
  297.            scl=1;                //待sda线上的数据稳定后,将scl拉高
  298.            nop();
  299.    }
  300.    scl=0;
  301.    nop();
  302.    sda=1;                                    
  303.    nop();
  304. }

  305. unsigned char readbyte()   //读一个字节
  306. {
  307.    unsigned char i,j,k=0;
  308.    scl=0; nop(); sda=1;
  309.    for (i=0;i<8;i++)
  310.    {  
  311.                 nop(); scl=1; nop();
  312.               if(sda==1)
  313.                 j=1;
  314.               else
  315.                 j=0;
  316.               k=(k<<1)|j;
  317.                   scl=0;
  318.         }
  319.            nop();
  320.         return(k);
  321. }

  322. void clock()         //I2C总线时钟
  323. {
  324.    unsigned char i=0;
  325.    scl=1;
  326.    nop();
  327.    while((sda==1)&&(i<255))
  328.              i++;
  329.    scl=0;
  330.    nop();
  331. }

  332. ////////从24c02的地址address中读取一个字节数据/////
  333. unsigned char read24c02(unsigned char address)
  334. {
  335.      unsigned char i;
  336.    start();
  337.    writebyte(0xa0);
  338.    clock();
  339.    writebyte(address);
  340.    clock();
  341.    start();
  342.    writebyte(0xa1);
  343.    clock();
  344.    i=readbyte();
  345.    stop();
  346.    delay1(100);
  347.    return(i);
  348. }

  349. //////向24c02的address地址中写入一字节数据info/////
  350. void write24c02(unsigned char address,unsigned char info)
  351. {
  352.     start();
  353.    writebyte(0xa0);
  354.    clock();
  355.    writebyte(address);
  356.    clock();
  357.    writebyte(info);
  358.    clock();
  359.    stop();
  360.    delay1(5000); //这个延时一定要足够长,否则会出错。因为24c02在从sda上取得数据后,还需要一定时间的烧录过程。
  361. }
  362. void keyscan()         //按键扫描程序
  363. {
  364.    if(k1==0)
  365.    {
  366.            delay1ms();
  367.         if(k1==0)
  368.         {
  369.          flag=!flag;
  370.          
  371.          }
  372.          while(!k1);
  373.     }
  374.          if(flag==0)
  375.          {
  376.                 if(k2==0)
  377.            {
  378.                    delay1ms();
  379.                 if(k2==0)
  380.                 {
  381.                  ldata++;
  382.                  }
  383.             }
  384.                 if(k3==0)
  385.            {
  386.                    delay1ms();
  387.                 if(k3==0)
  388.                 {
  389.                 if( ldata>0)
  390.                  ldata--;
  391.                  }
  392.             }
  393.           }

  394.            if(flag==1)
  395.          {
  396.                 if(k2==0)
  397.            {
  398.                    delay1ms();
  399.                 if(k2==0)
  400.                 {
  401.                  hdata++;
  402.                  }
  403.             }
  404.                 if(k3==0)
  405.            {
  406.                    delay1ms();
  407.                 if(k3==0)
  408.                 {
  409.                 if( hdata>0)
  410.                  hdata--;
  411.                  }
  412.             }
  413.           }
  414.          

  415. }
  416. void main()
  417. {          TMOD=0x10;        //定时器1定时模式,均16位计数模式
  418.         TH1=(65536-50000)/256;         //约每50ms计数1次
  419.         TL1=(65536-50000)%256;
  420.         ET1=1;        //        开定时器1中断
  421.         TR1=0;          //  定时器1
  422.         EA=1;        //开总中断
  423.      init();        //初始化24C02
  424.          lcd_init();        //初始化lcd
  425.         hdata=read24c02(1);
  426.         ldata=read24c02(0);
  427.         pe=0;
  428.         while(1)
  429.         {        
  430.                 voldate=iic_readvoldata();
  431.                 //vol = voldate*5/0.255;
  432.                 vol = voldate/2.7;
  433.                 if(vol<=7)
  434.                 vol=0;
  435.                 lcd_writecmd(0x80);
  436.                 for(i=0;i<8;i++)
  437.                 {
  438.                  lcd_writedata(character[i]);
  439.                 }
  440.                 lcd_writedata(vol/100%10+0x30);           //显示酒精浓度
  441.                 lcd_writedata(vol/10%10+0x30);
  442.                 lcd_writedata(vol%10+0x30);
  443.                 lcd_writedata('m');
  444.                 lcd_writedata('g');
  445.                 lcd_writedata('/');
  446.                 lcd_writedata('m');
  447.                 lcd_writedata('L');

  448.                 lcd_writecmd(0xc0);
  449.                 for(i=0;i<5;i++)
  450.                 {
  451.                  lcd_writedata(character2[i]);
  452.                 }
  453.                 lcd_writedata(ldata/100%10+0x30);  //显示设定的低警示值
  454.                 lcd_writedata(ldata/10%10+0x30);
  455.                 lcd_writedata(ldata%10+0x30);
  456.                 lcd_writedata('m');
  457.                 lcd_writedata('g');
  458.                 lcd_writedata(' ');
  459.                 lcd_writedata(hdata/100%10+0x30);        //显示设定的高警示值
  460.                 lcd_writedata(hdata/10%10+0x30);
  461.                 lcd_writedata(hdata%10+0x30);
  462.                 lcd_writedata('m');
  463.                 lcd_writedata('g');
  464.                
  465.                 delay100ms();
  466.                 keyscan();
  467.                 if(vol<=ldata)                                         //红绿灯设定
  468.                 red=1,green=0,yellow=1;
  469.                 if(vol>ldata && vol<=hdata)
  470.                 red=1,green=1,yellow=0;
  471.                 if(vol>hdata)
  472.                 red=0,green=1,yellow=1;        
  473.                
  474.                   
  475.                 write24c02(0,ldata);          //写数据到24c02
  476.                 delay10ms();
  477.                 write24c02(1,hdata);
  478.                 if(vol>ldata && vol<hdata )
  479.              pe=1;
  480.                 if(vol<ldata)
  481.                 pe=0;
  482.                 if(vol>hdata )
  483.              pe=1,TR1=1;
  484.                 if(vol==hdata)
  485.                 pe=0;
  486.                 if(tt==1)
  487.                 {
  488.                  pe=0;
  489.                  tt=0;
  490.                  m=0;
  491.                  TR1=0;
  492.                  pe=1;
  493.                 }
  494.         }
  495. }
  496. void timer1() interrupt 3
  497. {
  498.         TH1=(65536-50000)/256;
  499.         TL1=(65536-50000)%256;
  500.         m++;
  501.         if(m==20)//1s
  502.         { m=0;
  503.           tt++;
  504.         }
  505. }
复制代码

Keil5代码与Proteus8.8仿真下载:
酒精检测显示.zip (137.48 KB, 下载次数: 73)

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

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

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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