找回密码
 立即注册

QQ登录

只需一步,快速开始

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

单片机室内一氧化碳报警设计源码

[复制链接]
ID:295874 发表于 2018-4-27 16:44 | 显示全部楼层 |阅读模式
本设计程序代码实现室内CO浓度值的在线监测,并可实现报警设定功能。可以互相学习 。,

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

  4. #include <intrins.h>
  5. //#include "lcd1602.h"

  6. sbit CS=P1^3;                //CS定义为P2口的第4位脚,连接ADC0832CS脚
  7. sbit SCL=P1^0;                //SCL定义为P2口的第3位脚,连接ADC0832SCL脚
  8. sbit DO=P1^1;                //DO定义为P2口的第4位脚,连接ADC0832DO脚

  9. sbit beep = P2^0;   //蜂鸣器IO口定义
  10. long dengji,s_dengji = 50;     //浓度等级


  11. bit flag_300ms ;
  12. uchar key_can;                 //按键值的变量
  13. uchar menu_1;        //菜单设计的变量
  14. uchar flag_clock;

  15. //这三个引脚参考资料
  16. sbit rs=P1^2;         //1602数据/命令选择引脚 H:数据              L:命令
  17. //sbit rw=P2^6;         //1602读写引脚                 H:数据寄存器          L:指令寄存器
  18. sbit e =P1^4;         //1602使能引脚          下降沿触发
  19. uchar code table_num[]="0123456789abcdefg";

  20. /********************************************************************
  21. * 名称 : delay_uint()
  22. * 功能 : 小延时。
  23. * 输入 : 无
  24. * 输出 : 无
  25. ***********************************************************************/
  26. void delay_uint(uint q)
  27. {
  28.         while(q--);
  29. }

  30. /********************************************************************
  31. * 名称 : write_com(uchar com)
  32. * 功能 : 1602命令函数
  33. * 输入 : 输入的命令值
  34. * 输出 : 无
  35. ***********************************************************************/
  36. void write_com(uchar com)
  37. {
  38.         e=0;
  39.         rs=0;
  40. //        rw=0;
  41.         P0=com;
  42.         delay_uint(3);
  43.         e=1;
  44.         delay_uint(25);
  45.         e=0;
  46. }

  47. /********************************************************************
  48. * 名称 : write_data(uchar dat)
  49. * 功能 : 1602写数据函数
  50. * 输入 : 需要写入1602的数据
  51. * 输出 : 无
  52. ***********************************************************************/
  53. void write_data(uchar dat)
  54. {
  55.         e=0;
  56.         rs=1;
  57. //        rw=0;
  58.         P0=dat;
  59.         delay_uint(3);
  60.         e=1;
  61.         delay_uint(25);
  62.         e=0;       
  63. }

  64. /********************************************************************
  65. * 名称 : write_sfm2(uchar hang,uchar add,uchar date)
  66. * 功能 : 显示2位十进制数,如果要让第一行,第五个字符开始显示"23" ,调用该函数如下
  67.                   write_sfm1(1,5,23)
  68. * 输入 : 行,列,需要输入1602的数据
  69. * 输出 : 无
  70. ***********************************************************************/
  71. void write_sfm2(uchar hang,uchar add,uint date)
  72. {
  73.         if(hang==1)   
  74.                 write_com(0x80+add);
  75.         else
  76.                 write_com(0x80+0x40+add);
  77.         if(date >= 100)
  78.         {
  79.                 write_data(0x30+date/100%10);
  80.                 write_data(0x30+date/10%10);
  81.         }
  82.         else
  83.         {
  84.                 write_data(' ');
  85.                 write_data(0x30+date/10%10);
  86.         }
  87.         write_data(0x30+date%10);       
  88. }

  89. /********************************************************************
  90. * 名称 : write_string(uchar hang,uchar add,uchar *p)
  91. * 功能 : 改变液晶中某位的值,如果要让第一行,第五个字符开始显示"ab cd ef" ,调用该函数如下
  92.                   write_string(1,5,"ab cd ef;")
  93. * 输入 : 行,列,需要输入1602的数据
  94. * 输出 : 无
  95. ***********************************************************************/
  96. void write_string(uchar hang,uchar add,uchar *p)
  97. {
  98.         if(hang==1)   
  99.                 write_com(0x80+add);
  100.         else
  101.                 write_com(0x80+0x40+add);
  102.                 while(1)
  103.                 {
  104.                         if(*p == '\0')  break;
  105.                         write_data(*p);
  106.                         p++;
  107.                 }       
  108. }

  109. /********************************************************************
  110. * 名称 : clear_1602()
  111. * 功能 : 清除1602显示
  112. * 输入 : 无
  113. * 输出 : 无
  114. ***********************************************************************/
  115. void clear_1602()
  116. {
  117.         write_string(1,0,"                ");
  118.         write_string(2,0,"                ");
  119. }

  120. /***********************lcd1602上显示特定的字符************************/
  121. void write_zifu(uchar hang,uchar add,uchar date)
  122. {
  123.         if(hang==1)   
  124.                 write_com(0x80+add);
  125.         else
  126.                 write_com(0x80+0x40+add);
  127.         write_data(date);       
  128. }


  129. /********************************************************************
  130. * 名称 : init_1602()
  131. * 功能 : 初始化1602液晶
  132. * 输入 : 无
  133. * 输出 : 无
  134. ***********************************************************************/
  135. void init_1602()
  136. {
  137.         write_com(0x38);
  138.         write_com(0x0c);
  139.         write_com(0x06);
  140.         write_string(1,0," thickne: 00    ");
  141.         write_string(2,0," Thresho: 80    ");
  142.         write_sfm2(2,9,s_dengji);                   //显示浓度等级
  143. }

  144. uchar a_a;

  145. /********STC89C51扇区分布*******
  146. 第一扇区:1000H--11FF
  147. 第二扇区:1200H--13FF
  148. 第三扇区:1400H--15FF
  149. 第四扇区:1600H--17FF
  150. 第五扇区:1800H--19FF
  151. 第六扇区:1A00H--1BFF
  152. 第七扇区:1C00H--1DFF
  153. 第八扇区:1E00H--1FFF
  154. *****************/

  155. /********STC89C52扇区分布*******
  156. 第一扇区:2000H--21FF
  157. 第二扇区:2200H--23FF
  158. 第三扇区:2400H--25FF
  159. 第四扇区:2600H--27FF
  160. 第五扇区:2800H--29FF
  161. 第六扇区:2A00H--2BFF
  162. 第七扇区:2C00H--2DFF
  163. 第八扇区:2E00H--2FFF
  164. *****************/


  165. #define RdCommand 0x01 //定义ISP的操作命令
  166. #define PrgCommand 0x02
  167. #define EraseCommand 0x03
  168. #define Error 1
  169. #define Ok 0
  170. #define WaitTime 0x01 //定义CPU的等待时间
  171. sfr ISP_DATA=0xe2;  //寄存器申明
  172. sfr ISP_ADDRH=0xe3;
  173. sfr ISP_ADDRL=0xe4;
  174. sfr ISP_CMD=0xe5;
  175. sfr ISP_TRIG=0xe6;
  176. sfr ISP_CONTR=0xe7;

  177. /* ================ 打开 ISP,IAP 功能 ================= */
  178. void ISP_IAP_enable(void)
  179. {
  180.          EA = 0;       /* 关中断   */
  181.          ISP_CONTR = ISP_CONTR & 0x18;       /* 0001,1000 */
  182.          ISP_CONTR = ISP_CONTR | WaitTime; /* 写入硬件延时 */
  183.          ISP_CONTR = ISP_CONTR | 0x80;       /* ISPEN=1  */
  184. }
  185. /* =============== 关闭 ISP,IAP 功能 ================== */
  186. void ISP_IAP_disable(void)
  187. {
  188.          ISP_CONTR = ISP_CONTR & 0x7f; /* ISPEN = 0 */
  189.          ISP_TRIG = 0x00;
  190.          EA   =   1;   /* 开中断 */
  191. }
  192. /* ================ 公用的触发代码 ==================== */
  193. void ISPgoon(void)
  194. {
  195.          ISP_IAP_enable();   /* 打开 ISP,IAP 功能 */
  196.          ISP_TRIG = 0x46;  /* 触发ISP_IAP命令字节1 */
  197.          ISP_TRIG = 0xb9;  /* 触发ISP_IAP命令字节2 */
  198.          _nop_();
  199. }
  200. /* ==================== 字节读 ======================== */
  201. unsigned char byte_read(unsigned int byte_addr)
  202. {
  203.         EA = 0;
  204.          ISP_ADDRH = (unsigned char)(byte_addr >> 8);/* 地址赋值 */
  205.          ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff);
  206.          ISP_CMD   = ISP_CMD & 0xf8;   /* 清除低3位  */
  207.          ISP_CMD   = ISP_CMD | RdCommand; /* 写入读命令 */
  208.          ISPgoon();       /* 触发执行  */
  209.          ISP_IAP_disable();    /* 关闭ISP,IAP功能 */
  210.          EA  = 1;
  211.          return (ISP_DATA);    /* 返回读到的数据 */
  212. }
  213. /* ================== 扇区擦除 ======================== */
  214. void SectorErase(unsigned int sector_addr)
  215. {
  216.          unsigned int iSectorAddr;
  217.          iSectorAddr = (sector_addr & 0xfe00); /* 取扇区地址 */
  218.          ISP_ADDRH = (unsigned char)(iSectorAddr >> 8);
  219.          ISP_ADDRL = 0x00;
  220.          ISP_CMD = ISP_CMD & 0xf8;   /* 清空低3位  */
  221.          ISP_CMD = ISP_CMD | EraseCommand; /* 擦除命令3  */
  222.          ISPgoon();       /* 触发执行  */
  223.          ISP_IAP_disable();    /* 关闭ISP,IAP功能 */
  224. }
  225. /* ==================== 字节写 ======================== */
  226. void byte_write(unsigned int byte_addr, unsigned char original_data)
  227. {
  228.          EA  = 0;
  229. //         SectorErase(byte_addr);
  230.          ISP_ADDRH = (unsigned char)(byte_addr >> 8);  /* 取地址  */
  231.          ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff);
  232.          ISP_CMD  = ISP_CMD & 0xf8;    /* 清低3位 */
  233.          ISP_CMD  = ISP_CMD | PrgCommand;  /* 写命令2 */
  234.          ISP_DATA = original_data;   /* 写入数据准备 */
  235.          ISPgoon();       /* 触发执行  */
  236.          ISP_IAP_disable();     /* 关闭IAP功能 */
  237.          EA =1;
  238. }


  239. /***********************1ms延时函数*****************************/
  240. void delay_1ms(uint q)
  241. {
  242.         uint i,j;
  243.         for(i=0;i<q;i++)
  244.                 for(j=0;j<120;j++);
  245. }


  246. /******************把数据保存到单片机内部eeprom中******************/
  247. void write_eeprom()
  248. {
  249.         SectorErase(0x2000);
  250. //        byte_write(0x2000, s_dengji);
  251.         byte_write(0x2001, s_dengji);
  252.         byte_write(0x2060, a_a);       
  253. }

  254. /******************把数据从单片机内部eeprom中读出来*****************/
  255. void read_eeprom()
  256. {
  257. //        s_dengji   = byte_read(0x2000);
  258.         s_dengji = byte_read(0x2001);
  259.         a_a      = byte_read(0x2060);
  260. }

  261. /**************开机自检eeprom初始化*****************/
  262. void init_eeprom()
  263. {
  264.         read_eeprom();                //先读
  265.         if(a_a != 2)                //新的单片机初始单片机内问eeprom
  266.         {
  267.                 s_dengji = 80;
  268.                 a_a = 2;
  269.                 write_eeprom();
  270.         }       
  271. }

  272. /***********读数模转换数据********************************************************/       
  273. //请先了解ADC0832模数转换的串行协议,再来读本函数,主要是对应时序图来理解,本函数是模拟0832的串行协议进行的
  274.                                                 //  1  0  0 通道
  275.                                                 //  1  1  1 通道
  276. unsigned char ad0832read(bit SGL,bit ODD)
  277. {
  278.         unsigned char i=0,value=0,value1=0;               
  279.                 SCL=0;
  280.                 DO=1;
  281.                 CS=0;                //开始
  282.                 SCL=1;                //第一个上升沿       
  283.                 SCL=0;
  284.                 DO=SGL;
  285.                 SCL=1;          //第二个上升沿
  286.                 SCL=0;
  287.                 DO=ODD;
  288.                 SCL=1;            //第三个上升沿
  289.                 SCL=0;            //第三个下降沿
  290.                 DO=1;
  291.                 for(i=0;i<8;i++)
  292.                 {
  293.                         SCL=1;
  294.                         SCL=0; //开始从第四个下降沿接收数据
  295.                         value<<=1;
  296.                         if(DO)
  297.                                 value++;                                               
  298.                 }
  299.                 for(i=0;i<8;i++)
  300.                 {                        //接收校验数据
  301.                         value1>>=1;
  302.                         if(DO)
  303.                                 value1+=0x80;
  304.                         SCL=1;
  305.                         SCL=0;
  306.                 }
  307.                 CS=1;
  308.                 SCL=1;       
  309.                 if(value==value1)                                //与校验数据比较,正确就返回数据,否则返回0       
  310.                         return value;
  311.         return 0;
  312. }


  313. /*************定时器0初始化程序***************/
  314. void time_init()          
  315. {
  316.         EA   = 1;                   //开总中断
  317.         TMOD = 0X01;          //定时器0、定时器1工作方式1
  318.         ET0  = 1;                  //开定时器0中断
  319.         TR0  = 1;                  //允许定时器0定时
  320. }

  321. /****************按键处理显示函数***************/
  322. void key_with()
  323. {
  324.         if(key_can == 1)
  325.         {
  326.                 s_dengji ++ ;                    //浓度设置数加1
  327.                 if(s_dengji > 999)
  328.                         s_dengji = 999;
  329.         }
  330.         if(key_can == 2)
  331.         {
  332.                         s_dengji -= 1;        //浓度设置数减1
  333.                 if(s_dengji <= 1)
  334.                         s_dengji = 1 ;
  335.         }
  336.         write_sfm2(2,9,s_dengji);                   //显示浓度等级
  337.         write_eeprom();       //保存数据                                       
  338.                        
  339. }  

  340. /********************独立按键程序*****************/
  341. uchar key_can;         //按键值
  342. sbit key1=P1^5;
  343. sbit key2=P3^3;
  344. void key()         //独立按键程序
  345. {
  346.         static uchar key_new;
  347.         key_can = 20;                   //按键值还原
  348.         key1 = 1;
  349.         key2 = 1;
  350.         if((key1==0)||(key2==0))                //按键按下
  351.         {
  352.                 delay_1ms(1);                     //按键消抖动
  353.                 if(((key1==0)||(key2==0)) && (key_new == 1))
  354.                 {                                                //确认是按键按下
  355.                         key_new = 0;
  356.                         if(key1==0) key_can = 1;
  357.                         if(key2==0) key_can = 2;
  358.                 }                       
  359.         }
  360.         else
  361.                 key_new = 1;       
  362. }

  363. /****************报警函数***************/
  364. void clock_h_l()
  365. {
  366.         static uchar value;
  367.         if(dengji >= s_dengji )                //报警
  368.         {
  369.                 value ++;
  370.                 if(value >= 2)
  371.                 {
  372.                         value = 10;
  373.                         beep = ~beep;          //蜂鸣器报警
  374.                 }
  375.         }else
  376.         {
  377.                 if(dengji < s_dengji)          //取消报警
  378.                 {
  379.                         value = 0;
  380.                         beep = 1;
  381.                 }       
  382.         }
  383. }

  384. /***************主函数*****************/
  385. void main()
  386. {
  387.         beep = 0;                                //开机叫一声   
  388.         delay_1ms(150);
  389.         P0 = P1 = P2 = P3 = 0xff;                //单片机IO口初始化为1       
  390.         init_eeprom();                              //读eeprom数据
  391.         time_init();                                //初始化定时器
  392.         init_1602();
  393.         while(1)
  394.         {
  395.                 key();                                        //独立按键程序
  396.                 if(key_can < 10)
  397.                 {
  398.                         key_with();                        //按键按下要执行的程序
  399.                 }
  400.                 if(flag_300ms == 1)
  401.                 {               
  402.                         flag_300ms = 0;
  403.                         clock_h_l();           //报警函数
  404.                         dengji = ad0832read(1,0);       
  405.                         dengji = dengji * 450 / 255.0;
  406.                     dengji = dengji - 100;              //首先减去零点漂移,一般是1V
  407.                         if(dengji < 0)
  408.                                 dengji = 0;       
  409.                         dengji = dengji * 2;             //将mV转变成mg/L,系数需要校准   
  410.                                                                   //电压每升高0.1V,实际被测气体的浓度增加20ppm
  411.                                                                   //1ppm=1mg/kg=1mg/L=1×10-6 常用来表示气体浓度,或者溶液浓度。      

  412.                         write_sfm2(1,9,dengji);                  //显示浓度
  413.                 }
  414.         }
  415. }

  416. /*************定时器0中断服务程序***************/
  417. void time0_int() interrupt 1
  418. {       
  419.         static uchar value;
  420.         TH0 = 0x3c;
  421.         TL0 = 0xb0;     // 50ms
  422.         value ++;         
  423.         if(value % 6 == 0)
  424.         {
  425.                 flag_300ms = 1;           //300ms
  426.                 value = 0;
  427.         }
  428. }
复制代码

所有资料51hei提供下载:
keil4版.rar (50.05 KB, 下载次数: 69)
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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