单片机论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4578|回复: 6
收起左侧

基于单片机的烟雾报警器,包括程序和proteus仿真图

  [复制链接]
AotYan666 发表于 2018-1-24 15:49 | 显示全部楼层 |阅读模式
分享一个基于51单片机的烟雾报警器,当烟雾浓度和温度达到设定值时,点亮对应的发光二极管并控制蜂鸣器发出警报。(仿真中MQ-2传感器用滑动变阻器代替)

proteus仿真原理图:

仿真

仿真

程序

程序


单片机源程序:
  1. #include <reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
  2. #include "intrins.h"                                                                                          

  3. #define     u8                          unsigned char
  4. #define     u16                   unsigned int
  5. #define     uchar                          unsigned char
  6. #define     uint                   unsigned int

  7. uchar yushe_wendu=50;                                //温度预设值
  8. uchar yushe_yanwu=45;                                //烟雾预设值
  9. uint wendu;                                                                 //温度值全局变量
  10. uchar yanwu;                                                                 //用于读取ADC数据

  11. //运行模式  
  12. uchar Mode=0;                                                                 //=1是设置温度阀值  =2是设置烟雾阀值        =0是正常监控模式
  13. //管脚声明
  14. sbit Led_Reg                 =P2^2;                                 //红灯
  15. sbit Led_Yellow  =P2^4;                                 //黄灯
  16. sbit Buzzer             =P2^0;                                 //蜂鸣器
  17. sbit Fan                      =P3^3;                                 //


  18. /********************************************************************
  19. * 名称 : delay_1ms()
  20. * 功能 : 延时1ms函数
  21. * 输入 : q
  22. * 输出 : 无
  23. ***********************************************************************/
  24. void delay_ms(uint q)
  25. {
  26.         uint i,j;
  27.         for(i=0;i<q;i++)
  28.                 for(j=0;j<110;j++);
  29. }
  30. /***********************************************************************************************************
  31. LCD1602相关函数
  32. ***********************************************************************************************************/

  33. //LCD管脚声明 (RW引脚实物直接接地,因为本设计只用到液晶的写操作,RW引脚一直是低电平)
  34. sbit LCDRS = P2^7;
  35. sbit LCDEN = P2^6;
  36. sbit D0                 = P0^0;
  37. sbit D1                 = P0^1;
  38. sbit D2                 = P0^2;
  39. sbit D3                 = P0^3;
  40. sbit D4                 = P0^4;
  41. sbit D5                 = P0^5;
  42. sbit D6                 = P0^6;
  43. sbit D7                 = P0^7;



  44. //LCD延时
  45. void LCDdelay(uint z)                  //该延时大约100us(不精确,液晶操作的延时不要求很精确)
  46. {
  47.   uint x,y;
  48.   for(x=z;x>0;x--)
  49.     for(y=10;y>0;y--);
  50. }
  51. void LCD_WriteData(u8 dat)          
  52. {
  53.         if(dat&0x01)D0=1;else D0=0;
  54.         if(dat&0x02)D1=1;else D1=0;
  55.         if(dat&0x04)D2=1;else D2=0;
  56.         if(dat&0x08)D3=1;else D3=0;
  57.         if(dat&0x10)D4=1;else D4=0;
  58.         if(dat&0x20)D5=1;else D5=0;
  59.         if(dat&0x40)D6=1;else D6=0;
  60.         if(dat&0x80)D7=1;else D7=0;
  61. }
  62. //写命令
  63. void write_com(uchar com)
  64. {
  65.   LCDRS=0;                                  
  66.         LCD_WriteData(com);
  67. //  DAT=com;
  68.   LCDdelay(5);
  69.   LCDEN=1;
  70.   LCDdelay(5);
  71.   LCDEN=0;
  72. }
  73. //写数据
  74. void write_data(uchar date)
  75. {
  76.   LCDRS=1;
  77.         LCD_WriteData(date);
  78. //  DAT=date;
  79.   LCDdelay(5);
  80.   LCDEN=1;
  81.   LCDdelay(5);
  82.   LCDEN=0;
  83. }

  84. /*------------------------------------------------
  85.               选择写入位置
  86. ------------------------------------------------*/
  87. void SelectPosition(unsigned char x,unsigned char y)
  88. {     
  89.         if (x == 0)
  90.         {     
  91.                 write_com(0x80 + y);     //表示第一行
  92.         }
  93.         else
  94.         {      
  95.                 write_com(0xC0 + y);      //表示第二行
  96.         }        
  97. }
  98. /*------------------------------------------------
  99.               写入字符串函数
  100. ------------------------------------------------*/
  101. void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s)
  102. {     
  103.         SelectPosition(x,y) ;
  104.         while (*s)
  105.         {     
  106.                 write_data( *s);     
  107.                 s ++;     
  108.         }
  109. }
  110. //========================================================================
  111. // 函数: void LCD_Write_Char(u8 x,u8 y,u16 s,u8 l)
  112. // 应用: LCD_Write_Char(0,1,366,4) ;
  113. // 描述: 在第0行第一个字节位置显示366的后4位,显示结果为 0366
  114. // 参数: x:行,y:列,s:要显示的字,l:显示的位数
  115. // 返回: none.
  116. // 版本: VER1.0
  117. // 日期: 2013-4-1
  118. // 备注: 最大显示65535
  119. //========================================================================
  120. void LCD_Write_Char(u8 x,u8 y,u16 s,u8 l)
  121. {     
  122.         SelectPosition(x,y) ;

  123.         if(l>=5)
  124.                 write_data(0x30+s/10000%10);        //万位
  125.         if(l>=4)
  126.                 write_data(0x30+s/1000%10);                //千位
  127.         if(l>=3)
  128.                 write_data(0x30+s/100%10);                //百位
  129.         if(l>=2)
  130.                 write_data(0x30+s/10%10);                        //十位
  131.         if(l>=1)
  132.                 write_data(0x30+s%10);                //个位

  133. }
  134. /*1602指令简介
  135.   write_com(0x38);//屏幕初始化
  136.   write_com(0x0c);//打开显示 无光标 无光标闪烁
  137.   write_com(0x0d);//打开显示 阴影闪烁
  138.   write_com(0x0d);//打开显示 阴影闪烁
  139. */
  140. //1602初始化
  141. void Init1602()
  142. {
  143.   uchar i=0;
  144.   write_com(0x38);//屏幕初始化
  145.   write_com(0x0c);//打开显示 无光标 无光标闪烁
  146.   write_com(0x06);//当读或写一个字符是指针后一一位
  147.   write_com(0x01);//清屏
  148.        
  149. }

  150. void Display_1602(yushe_wendu,yushe_yanwu,c,temp)
  151. {
  152.         //显示预设温度
  153.         LCD_Write_Char(0,6,yushe_wendu,2) ;
  154.        
  155.         //显示预设烟雾
  156.         LCD_Write_Char(0,13,yushe_yanwu,3) ;
  157.        
  158.         //时时温度
  159.         LCD_Write_Char(1,6,c/10,2) ;
  160.         write_data('.');
  161.         LCD_Write_Char(1,9,c%10,1) ;
  162.        
  163.         //时时烟雾
  164.         LCD_Write_Char(1,13,temp,3) ;
  165. }



  166. /***********************************************************************************************************
  167. ADC0832相关函数
  168. ***********************************************************************************************************/
  169. sbit ADCS         =P1^5; //ADC0832 片选
  170. sbit ADCLK  =P1^2; //ADC0832 时钟
  171. sbit ADDI         =P1^3; //ADC0832 数据输入                /*因为单片机的管脚是双向的,且ADC0832的数据输入输出不同时进行,
  172. sbit ADDO         =P1^3; //ADC0832 数据输出                /*为节省单片机引脚,简化电路所以输入输出连接在同一个引脚上



  173. //========================================================================
  174. // 函数: unsigned int Adc0832(unsigned char channel)
  175. // 应用:                 temp=Adc0832(0);
  176. // 描述: 读取0通道的AD值
  177. // 参数: channel:通道0和通道1选择
  178. // 返回: 选取通道的AD值
  179. // 版本: VER1.0
  180. // 日期: 2015-05-29
  181. // 备注:
  182. //========================================================================
  183. unsigned int Adc0832(unsigned char channel)
  184. {
  185.         uchar i=0;
  186.         uchar j;
  187.         uint dat=0;
  188.         uchar ndat=0;
  189.         uchar  Vot=0;

  190.         if(channel==0)channel=2;
  191.         if(channel==1)channel=3;
  192.         ADDI=1;
  193.         _nop_();
  194.         _nop_();
  195.         ADCS=0;//拉低CS端
  196.         _nop_();
  197.         _nop_();
  198.         ADCLK=1;//拉高CLK端
  199.         _nop_();
  200.         _nop_();
  201.         ADCLK=0;//拉低CLK端,形成下降沿1
  202.         _nop_();
  203.         _nop_();
  204.         ADCLK=1;//拉高CLK端
  205.         ADDI=channel&0x1;
  206.         _nop_();
  207.         _nop_();
  208.         ADCLK=0;//拉低CLK端,形成下降沿2
  209.         _nop_();
  210.         _nop_();
  211.         ADCLK=1;//拉高CLK端
  212.         ADDI=(channel>>1)&0x1;
  213.         _nop_();
  214.         _nop_();
  215.         ADCLK=0;//拉低CLK端,形成下降沿3
  216.         ADDI=1;//控制命令结束
  217.         _nop_();
  218.         _nop_();
  219.         dat=0;
  220.         for(i=0;i<8;i++)
  221.         {
  222.                 dat|=ADDO;//收数据
  223.                 ADCLK=1;
  224.                 _nop_();
  225.                 _nop_();
  226.                 ADCLK=0;//形成一次时钟脉冲
  227.                 _nop_();
  228.                 _nop_();
  229.                 dat<<=1;
  230.                 if(i==7)dat|=ADDO;
  231.         }
  232.         for(i=0;i<8;i++)
  233.         {
  234.                 j=0;
  235.                 j=j|ADDO;//收数据
  236.                 ADCLK=1;
  237.                 _nop_();
  238.                 _nop_();
  239.                 ADCLK=0;//形成一次时钟脉冲
  240.                 _nop_();
  241.                 _nop_();
  242.                 j=j<<7;
  243.                 ndat=ndat|j;
  244.                 if(i<7)ndat>>=1;
  245.         }
  246.         ADCS=1;//拉低CS端
  247.         ADCLK=0;//拉低CLK端
  248.         ADDO=1;//拉高数据端,回到初始状态
  249.         dat<<=8;
  250.         dat|=ndat;

  251.         return(dat);            //return ad data
  252. }

  253. /***********************************************************************************************************
  254. DS18B20相关函数
  255. ***********************************************************************************************************/

  256. sbit DQ = P1^0;                                 //ds18b20的数据引脚



  257. /*****延时子程序:该延时主要用于ds18b20延时*****/
  258. void Delay_DS18B20(int num)
  259. {
  260.   while(num--) ;
  261. }
  262. /*****初始化DS18B20*****/
  263. void Init_DS18B20(void)
  264. {
  265.   unsigned char x=0;
  266.   DQ = 1;         //DQ复位
  267.   Delay_DS18B20(8);    //稍做延时
  268.   DQ = 0;         //单片机将DQ拉低
  269.   Delay_DS18B20(80);   //精确延时,大于480us
  270.   DQ = 1;         //拉高总线
  271.   Delay_DS18B20(14);
  272.   x = DQ;           //稍做延时后,如果x=0则初始化成功,x=1则初始化失败
  273.   Delay_DS18B20(20);
  274. }
  275. /*****读一个字节*****/
  276. unsigned char ReadOneChar(void)
  277. {
  278.   unsigned char i=0;
  279.   unsigned char dat = 0;
  280.   for (i=8;i>0;i--)
  281.   {
  282.     DQ = 0;     // 给脉冲信号
  283.     dat>>=1;
  284.     DQ = 1;     // 给脉冲信号
  285.     if(DQ)
  286.     dat|=0x80;
  287.     Delay_DS18B20(4);
  288.   }
  289.   return(dat);
  290. }
  291. /*****写一个字节*****/
  292. void WriteOneChar(unsigned char dat)
  293. {
  294.   unsigned char i=0;
  295.   for (i=8; i>0; i--)
  296.   {
  297.     DQ = 0;
  298.     DQ = dat&0x01;
  299.     Delay_DS18B20(5);
  300.     DQ = 1;
  301.     dat>>=1;
  302.   }
  303. }
  304. /*****读取温度*****/
  305. unsigned int ReadTemperature(void)
  306. {
  307.   unsigned char a=0;
  308.   unsigned char b=0;
  309.   unsigned int t=0;
  310.   float tt=0;
  311.   Init_DS18B20();
  312.   WriteOneChar(0xCC);  //跳过读序号列号的操作
  313.   WriteOneChar(0x44);  //启动温度转换
  314.   Init_DS18B20();
  315.   WriteOneChar(0xCC);  //跳过读序号列号的操作
  316.   WriteOneChar(0xBE);  //读取温度寄存器
  317.   a=ReadOneChar();     //读低8位
  318.   b=ReadOneChar();    //读高8位
  319.   t=b;
  320.   t<<=8;
  321.   t=t|a;
  322.   tt=t*0.0625;
  323.   t= tt*10+0.5;     //放大10倍输出并四舍五入
  324.   return(t);
  325. }
  326. //=====================================================================================
  327. //=====================================================================================
  328. //=====================================================================================


  329. /*****校准温度*****/
  330. u16 check_wendu(void)
  331. {
  332.         u16 c;
  333.         c=ReadTemperature()-5;                          //获取温度值并减去DS18B20的温漂误差
  334.         if(c<1) c=0;
  335.         if(c>=999) c=999;
  336.         return c;
  337. }


  338. /***********************************************************************************************************
  339. 按键检测相关函数
  340. ***********************************************************************************************************/
  341. //按键
  342. sbit Key1=P1^6;                                 //设置键
  343. sbit Key2=P1^7;                                 //加按键
  344. sbit Key3=P3^2;                                 //减按键



  345. #define KEY_SET                 1                //设置
  346. #define KEY_ADD                        2                //加
  347. #define KEY_MINUS                3                //减


  348. //========================================================================
  349. // 函数: u8 Key_Scan()
  350. // 应用: temp=u8 Key_Scan();
  351. // 描述: 按键扫描并返回按下的键值
  352. // 参数: NONE
  353. // 返回: 按下的键值
  354. // 版本: VER1.0
  355. // 日期: 2015-05-29
  356. // 备注: 该函数带松手检测,按下键返回一次键值后返回0,直至第二次按键按下
  357. //========================================================================
  358. u8 Key_Scan()
  359. {         
  360.         static u8 key_up=1;//按键按松开标志
  361.         if(key_up&&(Key1==0||Key2==0||Key3==0))
  362.         {
  363.                 delay_ms(10);//去抖动
  364.                 key_up=0;
  365.                 if(Key1==0)                        return 1;
  366.                 else if(Key2==0)return 2;
  367.                 else if(Key3==0)return 3;
  368.         }
  369.         else if(Key1==1&&Key2==1&&Key3==1)
  370.                 key_up=1;             
  371.         return 0;// 无按键按下
  372. }



  373. void main (void)
  374. {
  375.         u8 key;
  376.         wendu=check_wendu();                  //初始化时调用温度读取函数 防止开机85°C
  377.         Init1602();                          //调用初始化显示函数
  378.         LCD_Write_String(0,0,"SET T:00   E:000");  //开机界面
  379.         LCD_Write_String(1,0,"NOW T:00.0 E:000");  
  380.         delay_ms(1000);
  381.         wendu=check_wendu();                  //初始化时调用温度读取函数 防止开机85°C
  382.         while (1)                                                //主循环
  383.         {
  384.                 key=Key_Scan();                                        //按键扫描
  385.                 yanwu=Adc0832(0);                                //读取烟雾值
  386.                 wendu=check_wendu();          //读取温度值
  387.                
  388.                 if(key==KEY_SET)
  389.                 {
  390.                         Mode++;
  391.                 }
  392.                
  393.                 switch(Mode)                                                //判断模式的值
  394.                 {
  395.                         case 0:                                                                //监控模式
  396.                         {
  397.                                 Display_1602(yushe_wendu,yushe_yanwu,wendu,yanwu);  //显示预设温度,预设烟雾,温度值,烟雾值
  398.                                 if(yanwu>=yushe_yanwu)          //烟雾值大于等于预设值时
  399.                                 {
  400.                                         Led_Reg=0;                                          //烟雾指示灯亮
  401.                                         Fan=0;
  402.                                         Buzzer=0;                                                  //蜂鸣器报警
  403.                                 }
  404.                                 else                                                                                  //烟雾值小于预设值时
  405.                                 {
  406.                                         Led_Reg=1;                                          //关掉报警灯
  407.                                         Fan=1;
  408.                                 }
  409.                                 if(wendu>=(yushe_wendu*10))          //温度大于等于预设温度值时(为什么是大于预设值*10:因为我们要显示的温度是有小数点后一位,是一个3位数,25.9°C时实际读的数是259,所以判断预设值时将预设值*10)
  410.                                 {
  411.                                         Buzzer=0;                                                  //打开蜂鸣器报警
  412.                                         Led_Yellow=0;                                          //打开温度报警灯
  413.                                 }
  414.                                 else                                                                                  //温度值小于预设值时
  415.                                 {
  416.                                         Led_Yellow=1;                                          //关闭报警灯
  417.                                 }
  418.                                 if((yanwu<yushe_yanwu)&&(wendu<(yushe_wendu*10)))          //当烟雾小于预设值并且温度也小于预设值时 (&&:逻辑与,左右两边的表达式都成立(都为真,也就是1)时,该if语句才成立)
  419.                                 {
  420.                                         Buzzer=1;                                                  //停止报警
  421.                                 }
  422.                                 break;
  423.                         }
  424.                         case 1://预设温度模式
  425.                         {
  426.                                 SelectPosition(0,5) ;                                        //指定位置
  427.                            write_com(0x0d);                                                        //阴影闪烁
  428.                                 if(key==KEY_ADD)                                                        //加键按下
  429.                                 {
  430. ……………………

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

所有资料51hei提供下载:
单片机烟雾报警器.zip (152.74 KB, 下载次数: 233)

评分

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

查看全部评分

回复

使用道具 举报

18830266820 发表于 2018-4-22 15:27 | 显示全部楼层
你们都好会啊,我一点都不懂
回复

使用道具 举报

嗨你好 发表于 2018-4-27 10:26 | 显示全部楼层
请问有PCB图吗
回复

使用道具 举报

Armxmr 发表于 2018-5-5 20:38 | 显示全部楼层
很不错,赞一个
回复

使用道具 举报

iCIaOSo 发表于 2018-11-15 09:04 来自手机 | 显示全部楼层
真棒,楼主好厉害啊
回复

使用道具 举报

zxzxc 发表于 2019-5-6 11:19 | 显示全部楼层
楼主,你好,我这下了之后,仿真没有产生预期效果怎么回事?
回复

使用道具 举报

HTT3711 发表于 2019-5-19 17:26 | 显示全部楼层
真棒,楼主好厉害啊
回复

使用道具 举报

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

本版积分规则

QQ|手机版|小黑屋|单片机论坛 |51Hei单片机16群 联系QQ:125739409;技术交流QQ群7344883

Powered by 单片机教程网

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