找回密码
 立即注册

QQ登录

只需一步,快速开始

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

单片机PM2.5检测仪的PCB+原理图+程序设计资料

  [复制链接]
ID:521857 发表于 2019-4-25 23:12 | 显示全部楼层 |阅读模式
很高兴来到这里。
Altium Designer画的原理图和PCB图如下:(51hei附件中可下载工程文件)
0.png 0.png 0.png 0.png


元件清单:
单片机STC89C52    1片
单片机座    1个
晶振11.0592M    1个
独石电容30pF    2个
电解电容10uF    1个
电解电容220uF    1个
电阻10K    1个
排阻10K(103)    1片
按键    2个
1602液晶    1个
1602液晶座    1个
电位器10K(103)    1个
PM2.5传感器 GP2Y1051AU0F    1个
有源蜂鸣器    1个
S8550(PNP)三极管    1个
洞洞板9*15cm    1张
电源座    1个
电源开关    1个
电源线    1根


      为什么会有两个原理图呢?                                      
                                                                                                           
      1、两个原理图本质是一样的,只不过连接方式不同而已。                                                     
      2、原理图1是以模块化的形式展现,模块和模块之间通过网络编号                                                  
         关联起来,这样连接的好处是整个原理图看起来非常简洁明了。   
                                                      
                                                                    
      3、原理图2中,各个元件都通过导线连接起来,好处是看起来比较                                                         
         直观,缺点是整个图给人的感觉比较乱。                       
                                                                     
                                                                    
      4、建议,最好是采用原理图1的连接方式,这种修改和查看都         
         都比较方便的。之所以还给了一个原理图2,是满足部分有强迫   
         症的同学,他们没看到有导线相连,总觉得不对。其实原理图1                                                         
         这种网络编号连接才是规范的。                              
                                                                    
         以后出去工作接触的项目,要么一个芯片上百个引脚,要么一个   
         原理图上百个元件,要是都通过线来连接,可以想象整个原理图   
         将是多么的乱,已经失去了原理图的意义了,所以还是建议采用   
         模块化的思想来画原理图。


     一、程序用什么软件查看?                                                                             
      打开程序的软件是Keil,我们提供了相关的安装包,放在资料包                                                                        
      《24、软件--Keil 安装包》里面。                              
                                                                                                                       
     二、如何复制到word?                                          
                                                                    
      1、进入“程序(Keil版)”这个文件夹;                                                                                       
      2、找到“main.c”这个文件,程序源码就在这个文件里面;                                                         
      3、用电脑自带的记事本软件可以打开这个“main.c”文件;                                                                    
      4、用记事本打开之后,自己复制到word就可以了;                                                                              
                                                                    
     三、如何下载程序到板子上?                                    
                                                                    
      我们提供了下载程序的视频教程,放在资料包《19、如何下载                              
      单片机程序》。

单片机源程序如下:
  1. #include <reg52.h>
  2. #include <intrins.h>

  3. #define uchar unsigned char                // 以后unsigned char就可以用uchar代替
  4. #define uint  unsigned int                // 以后unsigned int 就可以用uint 代替

  5. sfr ISP_DATA  = 0xe2;                        // 数据寄存器
  6. sfr ISP_ADDRH = 0xe3;                        // 地址寄存器高八位
  7. sfr ISP_ADDRL = 0xe4;                        // 地址寄存器低八位
  8. sfr ISP_CMD   = 0xe5;                        // 命令寄存器
  9. sfr ISP_TRIG  = 0xe6;                        // 命令触发寄存器
  10. sfr ISP_CONTR = 0xe7;                        // 命令寄存器

  11. sbit LcdRs_P  = P2^7;     // 1602液晶的RS管脚      
  12. sbit LcdRw_P  = P2^6;     // 1602液晶的RW管脚
  13. sbit LcdEn_P  = P2^5;     // 1602液晶的EN管脚
  14. sbit Key1_P   = P3^2;                        // 减按键
  15. sbit Key2_P   = P3^3;                        // 加按键
  16. sbit Buzzer_P = P1^0;                        // 蜂鸣器


  17. uint  gAlarm ;                                                // 报警值
  18. uchar gIndex=0;                                                // 串口接收索引
  19. uint  Value[20]={0};                        // 串口数据缓存区


  20. /*********************************************************/
  21. // 单片机内部EEPROM不使能
  22. /*********************************************************/
  23. void ISP_Disable()
  24. {
  25.         ISP_CONTR = 0;
  26.         ISP_ADDRH = 0;
  27.         ISP_ADDRL = 0;
  28. }


  29. /*********************************************************/
  30. // 从单片机内部EEPROM读一个字节,从0x2000地址开始
  31. /*********************************************************/
  32. unsigned char EEPROM_Read(unsigned int add)
  33. {
  34.         ISP_DATA  = 0x00;
  35.         ISP_CONTR = 0x83;
  36.         ISP_CMD   = 0x01;
  37.         ISP_ADDRH = (unsigned char)(add>>8);
  38.         ISP_ADDRL = (unsigned char)(add&0xff);
  39.         // 对STC89C51系列来说,每次要写入0x46,再写入0xB9,ISP/IAP才会生效
  40.         ISP_TRIG  = 0x46;          
  41.         ISP_TRIG  = 0xB9;
  42.         _nop_();
  43.         ISP_Disable();
  44.         return (ISP_DATA);
  45. }


  46. /*********************************************************/
  47. // 往单片机内部EEPROM写一个字节,从0x2000地址开始
  48. /*********************************************************/
  49. void EEPROM_Write(unsigned int add,unsigned char ch)
  50. {
  51.         ISP_CONTR = 0x83;
  52.         ISP_CMD   = 0x02;
  53.         ISP_ADDRH = (unsigned char)(add>>8);
  54.         ISP_ADDRL = (unsigned char)(add&0xff);
  55.         ISP_DATA  = ch;
  56.         ISP_TRIG  = 0x46;
  57.         ISP_TRIG  = 0xB9;
  58.         _nop_();
  59.         ISP_Disable();
  60. }


  61. /*********************************************************/
  62. // 擦除单片机内部EEPROM的一个扇区
  63. // 写8个扇区中随便一个的地址,便擦除该扇区,写入前要先擦除
  64. /*********************************************************/
  65. void Sector_Erase(unsigned int add)          
  66. {
  67.         ISP_CONTR = 0x83;
  68.         ISP_CMD   = 0x03;
  69.         ISP_ADDRH = (unsigned char)(add>>8);
  70.         ISP_ADDRL = (unsigned char)(add&0xff);
  71.         ISP_TRIG  = 0x46;
  72.         ISP_TRIG  = 0xB9;
  73.         _nop_();
  74.         ISP_Disable();
  75. }


  76. /*********************************************************/
  77. // 毫秒级的延时函数,time是要延时的毫秒数
  78. /*********************************************************/
  79. void DelayMs(uint time)
  80. {
  81.         uint i,j;
  82.         for(i=0;i<time;i++)
  83.                 for(j=0;j<112;j++);
  84. }


  85. /*********************************************************/
  86. // 1602液晶写命令函数,cmd就是要写入的命令
  87. /*********************************************************/
  88. void LcdWriteCmd(uchar cmd)
  89. {
  90.         LcdRs_P = 0;
  91.         LcdRw_P = 0;
  92.         LcdEn_P = 0;
  93.         P0=cmd;
  94.         DelayMs(2);
  95.         LcdEn_P = 1;   
  96.         DelayMs(2);
  97.         LcdEn_P = 0;       
  98. }


  99. /*********************************************************/
  100. // 1602液晶写数据函数,dat就是要写入的数据
  101. /*********************************************************/
  102. void LcdWriteData(uchar dat)
  103. {
  104.         LcdRs_P = 1;
  105.         LcdRw_P = 0;
  106.         LcdEn_P = 0;
  107.         P0=dat;
  108.         DelayMs(2);
  109.         LcdEn_P = 1;   
  110.         DelayMs(2);
  111.         LcdEn_P = 0;
  112. }


  113. /*********************************************************/
  114. // 1602液晶初始化函数
  115. /*********************************************************/
  116. void LcdInit()
  117. {
  118.         LcdWriteCmd(0x38);        // 16*2显示,5*7点阵,8位数据口
  119.         LcdWriteCmd(0x0C);        // 开显示,不显示光标
  120.         LcdWriteCmd(0x06);        // 地址加1,当写入数据后光标右移
  121.         LcdWriteCmd(0x01);        // 清屏
  122. }


  123. /*********************************************************/
  124. // 液晶光标定位函数
  125. /*********************************************************/
  126. void LcdGotoXY(uchar line,uchar column)
  127. {
  128.         // 第一行
  129.         if(line==0)        
  130.                 LcdWriteCmd(0x80+column);
  131.          // 第二行
  132.         if(line==1)        
  133.                 LcdWriteCmd(0x80+0x40+column);
  134. }


  135. /*********************************************************/
  136. // 液晶输出数字
  137. /*********************************************************/
  138. void LcdPrintNum(uint num)
  139. {
  140.         if(num>999)
  141.         {
  142.                 LcdWriteData(num/1000+48);                        // 如果有千位,则输出千位
  143.         }
  144.         else                                                                                                                        // 如果没有千位,则输出空格
  145.         {
  146.                 LcdWriteData(' ');
  147.         }
  148.         LcdWriteData(num%1000/100+48);                // 百位
  149.         LcdWriteData(num%100/10+48);                        // 十位
  150.         LcdWriteData(num%10+48);                                         // 个位
  151. }


  152. /*********************************************************/
  153. // 液晶输出字符串函数
  154. /*********************************************************/
  155. void LcdPrintStr(uchar *str)
  156. {
  157.         while(*str!='\0')
  158.                 LcdWriteData(*str++);
  159. }


  160. /*********************************************************/
  161. // 液晶显示内容初始化
  162. /*********************************************************/
  163. void LcdShowInit()
  164. {
  165.         LcdGotoXY(0,0);                                // 液晶光标定位到第0行第0列
  166.         LcdPrintStr("PM2.5:     ug/m3");
  167.         LcdGotoXY(1,0);                                // 液晶光标定位到第1行第0列
  168.         LcdPrintStr("Alarm:     ug/m3");
  169. }



  170. /*********************************************************/
  171. // 串口初始化
  172. /*********************************************************/
  173. void UartInit()
  174. {
  175.         TMOD = 0x20;
  176.         SCON = 0x50;
  177.         TH1  = 0xf4;
  178.         TL1  = 0xf4;
  179.         TR1  = 1;
  180.         REN  = 1;
  181.         EA   = 1;
  182.         ES   = 1;
  183. }


  184. /*********************************************************/
  185. // 按键扫描
  186. /*********************************************************/
  187. void KeyScanf()
  188. {
  189.         /* 减按键被按下 */
  190.         if(Key1_P==0)               
  191.         {
  192.                 if(gAlarm>1)                                                                                                // 只有报警值大于1,才能完成减操作
  193.                 {
  194.                         gAlarm--;                                                                                                        // 报警值减1
  195.                         LcdGotoXY(1,6);                                                                                // 液晶定位到第1行第6列
  196.                         LcdPrintNum(gAlarm);                                                        // 显示报警浓度值
  197.                         Sector_Erase(0x2000);                                                         // 存储之前必须先擦除
  198.                         EEPROM_Write(0x2000,gAlarm/100);        // 存储新的报警值
  199.                         EEPROM_Write(0x2001,gAlarm%100);       
  200.                 }
  201.         }
  202.        
  203.         /* 减按键被按下 */
  204.         if(Key2_P==0)
  205.         {
  206.                 if(gAlarm<1300)                                                                                        // 只有报警值小于1300,才能完成加操作
  207.                 {
  208.                         gAlarm++;                                                                                                        // 报警值加1
  209.                         LcdGotoXY(1,6);                                                                                // 液晶定位到第1行第6列
  210.                         LcdPrintNum(gAlarm);                                                        // 显示报警浓度值
  211.                         Sector_Erase(0x2000);                                                         // 存储之前必须先擦除
  212.                         EEPROM_Write(0x2000,gAlarm/100);        // 存储新的报警值
  213.                         EEPROM_Write(0x2001,gAlarm%100);       
  214.                 }
  215.         }
  216. }


  217. /*********************************************************/
  218. // 报警判断
  219. /*********************************************************/
  220. void AlarmJudge(uint num)
  221. {
  222.         if(num>gAlarm)       
  223.                 Buzzer_P=0;                // 开启蜂鸣器报警
  224.         else
  225.                 Buzzer_P=1;                // 停止蜂鸣器报警
  226. }


  227. /*********************************************************/
  228. // 主函数
  229. /*********************************************************/
  230. void main(void)
  231. {
  232.         uchar i;                                // 循环变量
  233.         uint ret;                                // 保存测量结果
  234.        
  235.         gAlarm=EEPROM_Read(0x2000)*100+EEPROM_Read(0x2001);                // 上电时,先读取报警值
  236.         if((gAlarm==0)||(gAlarm>999))                                                                                                        // 如果读取到的报警值异常,则重新赋值
  237.                 gAlarm=200;
  238.                
  239.        
  240.         LcdInit();                                            // 液晶功能初始化
  241.         LcdShowInit();                                        // 液晶显示初始化
  242.         UartInit();                                                        // 串口初始化

  243.         LcdGotoXY(1,6);                                        // 液晶定位到第1行第6列
  244.         LcdPrintNum(gAlarm);                // 显示报警浓度值
  245.        
  246.         while(1)
  247.         {
  248.                 ret=0;                                                                                        // 清零测量结果
  249.                 for(i=0;i<20;i++)                                                // 将最新的20个测量结果求和
  250.                 {
  251.                         ret=ret+Value[i];
  252.                 }
  253.                 ret=ret/20;                                                                        // 再除以20求得平均值
  254.                
  255.                 ret=((ret*5)/1024.0)*380;                // 将读取到的电压值转换为灰尘浓度值       
  256.                
  257.                 LcdGotoXY(0,6);                                                        // 液晶定位到第0行第6列
  258.                 LcdPrintNum(ret);                                                // 显示测量结果
  259.                
  260.                 AlarmJudge(ret);                                                // 判断是否需要报警
  261.                
  262.                 KeyScanf();                                                                        // 进行按键扫描,判断是否有按键按下
  263. ……………………

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


全部资料51hei下载地址:
10、元件清单.rar (2.97 KB, 下载次数: 82)
07、单片机程序.rar (39.13 KB, 下载次数: 117)
08、电路图(原理图和PCB图).rar (514.6 KB, 下载次数: 129)


评分

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

查看全部评分

回复

使用道具 举报

ID:585455 发表于 2019-7-22 22:15 | 显示全部楼层
感謝分享
回复

使用道具 举报

ID:689272 发表于 2020-2-1 18:05 | 显示全部楼层
感谢分享
回复

使用道具 举报

ID:441206 发表于 2020-2-14 16:30 来自触屏版 | 显示全部楼层
怎么下载啊
回复

使用道具 举报

ID:692917 发表于 2020-2-15 19:04 | 显示全部楼层
感谢资料分享
回复

使用道具 举报

ID:741640 发表于 2020-5-1 14:32 | 显示全部楼层
怎末没有仿真啊
回复

使用道具 举报

ID:47293 发表于 2021-8-29 11:58 | 显示全部楼层
你的PM2.5传感器输出是模拟信号,为何直接送给了单片机?不需要AD转换?
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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