找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 12044|回复: 29
收起左侧

单片机酒精检测仪源程序+仿真

  [复制链接]
ID:198955 发表于 2017-5-10 09:24 | 显示全部楼层 |阅读模式
这是一个用51单片机做的酒精浓度检测仪

原理图:
0.png

pcb图:
0.png


酒精浓度检测仿真:
0.png 0.png

DXP格式用            Altium designer Summer 软件打开
protel99格式用       protel99SE 软件打开
PDF格式用            PDF 软件打开
Word格式用           Word 或用WPS 软件打开

4种格式的原理图都内容是一样的  只是打开方式不同

请看原理图焊接,不要看仿真图焊接。

wrod格式里面的原理图是复制出来的,有一点点变行变形,麻烦大家注意一下,尽量看其他三种格式的图焊接,

如果论文里面的原理图和原理图文件夹内的图不一样的话,请大家以原理图文件夹内的为准,原理图文件夹的图是和实物配套的,可以自己截图或复制,然后粘贴到论文里面去。

单片机源程序:
  1. #include <reg52.h>                 //调用单片机头文件
  2. #define uchar unsigned char  //无符号字符型 宏定义        变量范围0~255
  3. #define uint  unsigned int         //无符号整型 宏定义        变量范围0~65535
  4. uchar a_a;
  5. #include <intrins.h>


  6. //数码管段选定义      0     1    2    3    4    5         6         7          8           9       
  7. uchar code smg_du[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x04,0x14,
  8.                                            0x0c,0xa4,0x27,0xc4,0x26,0x2e,0xff};         //断码

  9. //数码管位选定义
  10. uchar code smg_we[]={0x7f,0xbf,0xdf,0xef};
  11. uchar dis_smg[8]  = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8};       
  12. uchar smg_i = 4;    //显示数码管的个位数

  13. sbit CS=P3^2;                //CS定义为P1口的第4位脚,连接ADC0832CS脚
  14. sbit SCL=P3^3;                //SCL定义为P1口的第3位脚,连接ADC0832SCL脚
  15. sbit DO=P3^4;                //DO定义为P1口的第4位脚,连接ADC0832DO脚

  16. sbit beep = P3^6;   //蜂鸣器IO口定义
  17. long dengji,s_dengji = 50;     //酒精等级

  18. bit flag_300ms = 1;
  19. uchar menu_1;        //菜单设计的变量

  20. #define RdCommand 0x01 //定义ISP的操作命令
  21. #define PrgCommand 0x02
  22. #define EraseCommand 0x03
  23. #define Error 1
  24. #define Ok 0
  25. #define WaitTime 0x01 //定义CPU的等待时间
  26. sfr ISP_DATA=0xe2;  //寄存器申明
  27. sfr ISP_ADDRH=0xe3;
  28. sfr ISP_ADDRL=0xe4;
  29. sfr ISP_CMD=0xe7;
  30. sfr ISP_TRIG=0xe6;
  31. sfr ISP_CONTR=0xe5;

  32. /* ================ 打开 ISP,IAP 功能 ================= */
  33. void ISP_IAP_enable(void)
  34. {
  35.          EA = 0;       /* 关中断   */
  36.          ISP_CONTR = ISP_CONTR & 0x18;       /* 0001,1000 */
  37.          ISP_CONTR = ISP_CONTR | WaitTime; /* 写入硬件延时 */
  38.          ISP_CONTR = ISP_CONTR | 0x80;       /* ISPEN=1  */
  39. }
  40. /* =============== 关闭 ISP,IAP 功能 ================== */
  41. void ISP_IAP_disable(void)
  42. {
  43.          ISP_CONTR = ISP_CONTR & 0x7f; /* ISPEN = 0 */
  44.          ISP_TRIG = 0x00;
  45.          EA   =   1;   /* 开中断 */
  46. }
  47. /* ================ 公用的触发代码 ==================== */
  48. void ISPgoon(void)
  49. {
  50.          ISP_IAP_enable();   /* 打开 ISP,IAP 功能 */
  51.          ISP_TRIG = 0x46;  /* 触发ISP_IAP命令字节1 */
  52.          ISP_TRIG = 0xb9;  /* 触发ISP_IAP命令字节2 */
  53.          _nop_();
  54. }
  55. /* ==================== 字节读 ======================== */
  56. unsigned char byte_read(unsigned int byte_addr)
  57. {
  58.         EA = 0;
  59.          ISP_ADDRH = (unsigned char)(byte_addr >> 8);/* 地址赋值 */
  60.          ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff);
  61.          ISP_CMD   = ISP_CMD & 0xf8;   /* 清除低3位  */
  62.          ISP_CMD   = ISP_CMD | RdCommand; /* 写入读命令 */
  63.          ISPgoon();       /* 触发执行  */
  64.          ISP_IAP_disable();    /* 关闭ISP,IAP功能 */
  65.          EA  = 1;
  66.          return (ISP_DATA);    /* 返回读到的数据 */
  67. }
  68. /* ================== 扇区擦除 ======================== */
  69. void SectorErase(unsigned int sector_addr)
  70. {
  71.          unsigned int iSectorAddr;
  72.          iSectorAddr = (sector_addr & 0xfe00); /* 取扇区地址 */
  73.          ISP_ADDRH = (unsigned char)(iSectorAddr >> 8);
  74.          ISP_ADDRL = 0x00;
  75.          ISP_CMD = ISP_CMD & 0xf8;   /* 清空低3位  */
  76.          ISP_CMD = ISP_CMD | EraseCommand; /* 擦除命令3  */
  77.          ISPgoon();       /* 触发执行  */
  78.          ISP_IAP_disable();    /* 关闭ISP,IAP功能 */
  79. }
  80. /* ==================== 字节写 ======================== */
  81. void byte_write(unsigned int byte_addr, unsigned char original_data)
  82. {
  83.          EA  = 0;
  84.          SectorErase(byte_addr);
  85.          ISP_ADDRH = (unsigned char)(byte_addr >> 8);  /* 取地址  */
  86.          ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff);
  87.          ISP_CMD  = ISP_CMD | PrgCommand;  /* 写命令2 */
  88.          ISP_DATA = original_data;   /* 写入数据准备 */
  89.          ISPgoon();       /* 触发执行  */
  90.          ISP_IAP_disable();     /* 关闭IAP功能 */
  91.          EA =1;
  92. }


  93. /***********************1ms延时函数*****************************/
  94. void delay_1ms(uint q)
  95. {
  96.         uint i,j;
  97.         for(i=0;i<q;i++)
  98.                 for(j=0;j<120;j++);
  99. }

  100. /******************把数据从单片机内部eeprom中读出来*****************/
  101. void read_eeprom()         //读出保存数据
  102. {
  103.         s_dengji  = byte_read(0x2001);
  104.         s_dengji <<= 8;
  105.         s_dengji  |= byte_read(0x2000);
  106.         a_a      = byte_read(0x2058);
  107. }

  108. /******************把数据保存到单片机内部eeprom中******************/
  109. void write_eeprom()        //保存数据
  110. {
  111.         SectorErase(0x2000);
  112.         byte_write(0x2000, s_dengji % 256);
  113.         byte_write(0x2001, s_dengji / 256);
  114.         byte_write(0x2058,a_a);       
  115. }

  116. /**************开机自检eeprom初始化*****************/
  117. void init_eeprom()         ////开始初始化保存的数据
  118. {
  119.         read_eeprom();           //读出保存数据
  120.         if(a_a != 33)
  121.         {
  122.                 a_a = 33;
  123.                 s_dengji = 80;
  124.                 write_eeprom();                 //保存数据
  125.         }
  126. }

  127. /***********读数模转换数据********************************************************/       
  128. //请先了解ADC0832模数转换的串行协议,再来读本函数,主要是对应时序图来理解,本函数是模拟0832的串行协议进行的
  129.                                                 //  1  0  0 通道
  130.                                                 //  1  1  1 通道
  131. unsigned char ad0832read(bit SGL,bit ODD)
  132. {
  133.         unsigned char i=0,value=0,value1=0;               
  134.                 SCL=0;
  135.                 DO=1;
  136.                 CS=0;                //开始
  137.                 SCL=0;                //第一个上升沿       
  138.                 SCL=1;
  139.                 DO=SGL;
  140.                 SCL=1;          //第二个上升沿
  141.                 SCL=0;
  142.                 DO=ODD;
  143.                 SCL=1;            //第三个上升沿
  144.                 SCL=0;            //第三个下降沿
  145.                 DO=1;
  146.                 for(i=0;i<8;i++)
  147.                 {
  148.                         SCL=0;
  149.                         SCL=1; //开始从第四个下降沿接收数据
  150.                         value<<=1;
  151.                         if(DO)
  152.                                 value++;                                               
  153.                 }
  154.                 for(i=0;i<8;i++)
  155.                 {                        //接收校验数据
  156.                         value1>>=1;
  157.                         if(DO)
  158.                                 value1+=0x80;
  159.                         SCL=0;
  160.                         SCL=1;
  161.                 }
  162.                 CS=1;
  163.                 SCL=1;       
  164.                 if(value==value1)                                //与校验数据比较,正确就返回数据,否则返回0       
  165.                         return value;
  166.         return 0;
  167. }

  168. /********************独立按键程序*****************/
  169. uchar key_can;         //按键值

  170. void key()         //独立按键程序
  171. {
  172.         static uchar key_new;
  173.         key_can = 20;                   //按键值还原
  174.         P2 |= 0x0f;                                        //把按键的IO口输出为高电平
  175.         if((P2 & 0x0f) != 0x0f)                //按键按下
  176.         {
  177.                 delay_1ms(1);                     //按键消抖动
  178.                 if(((P2 & 0x0f) != 0x0f) && (key_new == 1))
  179.                 {                                                //确认是按键按下
  180.                         key_new = 0;
  181.                         switch(P2 & 0x0f)
  182.                         {
  183.                                 case 0x0e: key_can = 3; break;           //得到k1键值
  184.                                 case 0x0d: key_can = 2; break;           //得到k2键值
  185.                                 case 0x0b: key_can = 1; break;           //得到k3键值
  186. //                                case 0x07: key_can = 1; break;           //得到k4键值
  187.                         }
  188.                 }                       
  189.         }
  190.         else
  191.                 key_new = 1;       
  192. }



  193. /****************按键处理数码管显示函数***************/
  194. void key_with()
  195. {
  196.         if(key_can == 1)                   //设置键
  197.         {
  198.                 menu_1 ++;
  199.                 if(menu_1 >= 2)
  200.                 {
  201.                         menu_1 = 0;
  202.                 }
  203.         }
  204.         if(menu_1 == 1)                        //设置酒精报警值
  205.         {
  206.                 smg_i = 4;                    //显示4位数码管
  207.                 if(key_can == 2)
  208.                 {
  209.                         s_dengji ++ ;                 //加1
  210.                         if(s_dengji > 500)
  211.                                 s_dengji = 500;
  212.                 }
  213.                 if(key_can == 3)
  214.                 {
  215.                         s_dengji -- ;                //减1       
  216.                         if(s_dengji <= 1)
  217.                                 s_dengji = 1;
  218.                 }
  219.                 dis_smg[0] = smg_du[s_dengji % 10];                   //取个位显示
  220.                 dis_smg[1] = smg_du[s_dengji / 10 % 10] ;      //取十位显示
  221.                 dis_smg[2] = smg_du[s_dengji / 100 % 10] ;           //
  222.                 dis_smg[3] = 0x0c;         //a
  223.                 write_eeprom();        //保存数据
  224.         }       
  225. }  


  226. /***********************数码显示函数*****************************/
  227. void display()
  228. {
  229.         static uchar i;       
  230.         P1 = 0xff;                         //消隐                                           
  231.         P2 = smg_we[i];                          //位选
  232.         P1 = dis_smg[i];                 //段选          
  233.         i ++;
  234.         if(i >= smg_i)
  235.                 i = 0;         
  236. }


  237. /*************定时器0初始化程序***************/
  238. void time_init()          
  239. {
  240.         EA   = 1;                   //开总中断
  241.         TMOD = 0X01;          //定时器0、定时器1工作方式1
  242.         ET0  = 1;                  //开定时器0中断
  243.         TR0  = 1;                  //允许定时器0定时
  244. }



  245. /****************报警函数***************/
  246. void clock_h_l()
  247. {
  248.         static uchar value;
  249.         if((dengji >= s_dengji))                //报警
  250.         {
  251.                 value ++;
  252.                 if(value >= 2)
  253.                 {
  254.                         value = 10;
  255.                         beep = ~beep;          //蜂鸣器报警
  256.                 }
  257.         }else
  258.         {
  259.                 if((dengji < s_dengji))          //取消报警
  260.                 {
  261.                         value = 0;
  262.                         beep = 1;
  263.                 }       
  264.         }

  265. }

  266. /****************主函数***************/
  267. void main()
  268. {
  269.         beep = 0;                                //开机叫一声   
  270.         delay_1ms(150);
  271.         P0 = P1 = P2 = P3 = 0xff;                //单片机IO口初始化为1
  272.         time_init();                                //初始化定时器
  273.         init_eeprom();              //开始初始化保存的数据
  274.         while(1)
  275.         {
  276.                 key();                                        //独立按键程序
  277.                 if(key_can < 10)
  278.                 {
  279.                         key_with();                        //按键按下要执行的程序
  280.                 }
  281.                 if(flag_300ms == 1)
  282.                 {               
  283.                         flag_300ms = 0;
  284.                         clock_h_l();
  285.                         dengji = ad0832read(1,0);       
  286.                         dengji = dengji * 450 / 255.0;
  287.                     dengji = dengji - 100;              //首先减去零点漂移,一般是100mV
  288.                         if(dengji < 0)
  289.                                 dengji = 0;       
  290.                         dengji = dengji * 2;             //将mV转变成mg/L,系数需要校准   
  291.                                                                   //电压每升高0.1V,实际被测气体的浓度增加20ppm
  292.                                                                   //1ppm=1mg/kg=1mg/L=1×10-6 常用来表示气体浓度,或者溶液浓度。      
  293.                         if(menu_1 == 0)
  294.                         {
  295.                                 if(dengji >= 1000)
  296.                                         smg_i = 4;
  297.                                 else
  298.                                         smg_i = 3;
  299.                                 dis_smg[3]=smg_du[dengji/1000%10];        //千位
  300.                                 dis_smg[2]=smg_du[dengji/100%10];        //百位
  301.                                 dis_smg[1]=smg_du[dengji/10%10];        //十位
  302.                                 dis_smg[0]=smg_du[dengji%10];            //个位        ADC0832为8位ADC,数值为0~255,我们将其分开放入l_tmpdate数组中显示
  303.                         }
  304.                 }
  305.         }
  306. }

  307. /*************定时器0中断服务程序***************/
  308. void time0_int() interrupt 1
  309. {       
  310.         static uchar value;


  311. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码

全部资料下载:
单片机酒精检测仪.rar (244.6 KB, 下载次数: 324)

评分

参与人数 1黑币 +5 收起 理由
Guard1an + 5 很给力!

查看全部评分

回复

使用道具 举报

ID:203292 发表于 2017-5-21 22:57 | 显示全部楼层
我的是LCD的,但是程序仿真不太对,ADC0832可能程序有问题,心烦。
回复

使用道具 举报

ID:210860 发表于 2017-6-13 16:45 | 显示全部楼层
很给力
回复

使用道具 举报

ID:210860 发表于 2017-6-13 16:51 | 显示全部楼层
谢谢,很给力
回复

使用道具 举报

ID:250694 发表于 2017-11-18 12:26 | 显示全部楼层
厉害了,很不错
回复

使用道具 举报

ID:310050 发表于 2018-4-17 16:06 | 显示全部楼层
剩下的代码在哪里看
回复

使用道具 举报

ID:310050 发表于 2018-4-19 11:51 | 显示全部楼层
用这个代码生成的hex文件在仿真软件中不可以用啊,数码管显示不正常,压缩包里的文件就可以正常使用
回复

使用道具 举报

ID:335470 发表于 2018-5-22 10:25 | 显示全部楼层
给力。看好你,赞
回复

使用道具 举报

ID:397905 发表于 2018-9-14 13:47 | 显示全部楼层
楼主牛逼
回复

使用道具 举报

ID:388870 发表于 2018-9-17 09:49 | 显示全部楼层
谢谢分享
回复

使用道具 举报

ID:454833 发表于 2018-12-27 21:05 | 显示全部楼层
程序根本都不对,仿真不了
回复

使用道具 举报

ID:464742 发表于 2019-1-8 17:06 | 显示全部楼层
弱冠 发表于 2018-6-8 22:29
嗨,你这个压缩包可以给我发一下吗?我加你qq

可以给我发一下吗,谢谢你,你是好人,1416022721,我的qq
回复

使用道具 举报

ID:464849 发表于 2019-1-8 19:55 | 显示全部楼层
还不错
回复

使用道具 举报

ID:493171 发表于 2019-3-18 14:15 | 显示全部楼层
很给力
回复

使用道具 举报

ID:302759 发表于 2019-3-18 14:55 | 显示全部楼层
酒精检测模块能仿真吗?
回复

使用道具 举报

ID:505638 发表于 2019-4-6 10:55 来自手机 | 显示全部楼层
求分享一下 593785475  跪求 谢谢!!
回复

使用道具 举报

ID:510184 发表于 2019-4-12 09:24 来自手机 | 显示全部楼层
1345105875 发表于 2019-3-18 14:15
很给力

求分享呀,大神!感谢qq1329302962
回复

使用道具 举报

ID:509040 发表于 2019-4-13 10:52 | 显示全部楼层
很厉害
回复

使用道具 举报

ID:73182 发表于 2019-4-13 16:14 | 显示全部楼层

谢谢分享
回复

使用道具 举报

ID:518918 发表于 2019-4-22 19:53 | 显示全部楼层
有一部分看不懂,感觉很厉害
回复

使用道具 举报

ID:519203 发表于 2019-4-23 04:05 | 显示全部楼层
不错啊 谢谢了
回复

使用道具 举报

ID:560957 发表于 2019-6-13 12:56 | 显示全部楼层
非常实用
回复

使用道具 举报

ID:602696 发表于 2019-8-22 15:22 | 显示全部楼层
看了一遍,心里有底感觉启发很大,收藏了。
回复

使用道具 举报

ID:607340 发表于 2019-9-11 16:00 | 显示全部楼层
厉害厉害
回复

使用道具 举报

ID:607340 发表于 2019-9-11 16:01 | 显示全部楼层
很好用
回复

使用道具 举报

ID:641028 发表于 2019-11-13 11:02 | 显示全部楼层
很给力
回复

使用道具 举报

ID:770626 发表于 2020-6-26 18:25 | 显示全部楼层
代码根本运行不了
回复

使用道具 举报

ID:782510 发表于 2020-12-9 20:10 | 显示全部楼层
代码编译有错误吧
回复

使用道具 举报

ID:1097113 发表于 2023-10-24 16:55 | 显示全部楼层
为什么我的仿真显示的数字不完整啊家人
回复

使用道具 举报

ID:1097113 发表于 2023-10-24 16:56 | 显示全部楼层
ccccckjjy 发表于 2018-4-19 11:51
用这个代码生成的hex文件在仿真软件中不可以用啊,数码管显示不正常,压缩包里的文件就可以正常使用

我的也是,什么问题啊解决了吗
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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