一款以DSL-08PM2.5/10检测模块为主的检测装置,控制器为STC89C52RC,显示屏为LCD1602。主要可显示PM2.5和PM10浓度,有报警功能,且报警阈值可通过按键设置。硬件包含DS1302,可按键切换显示时间。附件包含传感器资料、源程序、PCB工程文件及操作说明供参考。
制作出来的实物图如下:
1. 模块上电默认显示PM2.5/10数据,按一下“显示切换”键可切换至PM2.5/10报警值设置界面;再按一次则进入时间显示界面,显示北京时间;再按可返回初始界面。
2. 在PM2.5/10报警值设置界面时,按“M“键开始设置数据。按下“M”键后首位数据光标闪烁,此时可以按“▲”修改数值,该位可在0~9内递增变换。“ ”键可切换数据位,切换到哪一位,该位的光标闪烁。
3. 在设置过程中(即光标闪烁时)“显示切换”键不可用,所有数据设置完后再按一次“M“键可确认数据,此时“显示切换”键恢复功能。
4. 在设置过程中(即光标闪烁时)报警功能暂停,所有数据设置完后再按一次“M“键可确认数据,此时报警功能恢复。
Altium Designer画的原理图和PCB图如下:(51hei附件中可下载工程文件)
单片机源程序如下:
- #include<reg52.h>
- #include<intrins.h>
- #define uint unsigned int
- #define uchar unsigned char
- #define FOSC 11059200L
- #define T1MS (65536 - FOSC / 12 / 500)
- sbit DS1302_CLK = P3 ^ 7; //实时时钟时钟线引脚
- sbit DS1302_IO = P3 ^ 6; //实时时钟数据线引脚
- sbit DS1302_RST = P3 ^ 5; //实时时钟复位线引脚
- sbit ACC0 = ACC^0;
- sbit ACC7 = ACC^7;
- unsigned char t0,t1,t2,t3,t4,t5;
- unsigned char d0,d1,d2,d3,d4,d5;
- typedef struct __SYSTEMTIME__
- {
- unsigned char Second;
- unsigned char Minute;
- unsigned char Hour;
- unsigned char Week;
- unsigned char Day;
- unsigned char Month;
- unsigned char Year;
- unsigned char DateString[9];
- unsigned char TimeString[9];
- }SYSTEMTIME; //定义的时间类型
- #define AM(X) X
- #define PM(X) (X+12) // 转成24小时制
- #define DS1302_SECOND 0x80
- #define DS1302_MINUTE 0x82
- #define DS1302_HOUR 0x84
- #define DS1302_WEEK 0x8A
- #define DS1302_DAY 0x86
- #define DS1302_MONTH 0x88
- #define DS1302_YEAR 0x8C
- #define DS1302_RAM(X) (0xC0+(X)*2) //用于计算 DS1302_RAM 地址的宏
- uchar cnt_PM25 = 0;//数码管PM2.5循环计数
- uchar R_data = 0;//读数据标志
- uchar USART_RX_STB = 0;
- uchar complete_flag = 0;
- uchar USART_RX_BUF[32];//接收缓冲,最大32个字节.
- uchar time_js = 0;//判断串口一帧数据是否接收完成的计数变量
- uchar PM25_data[2] = {0};
- uchar PM10_data[2] = {0};
- uchar PM_ASC[8];//数据分解后的存储数组
- uint PM25_1, PM25_2, PM10_1, PM10_2;
- sbit RS = P2 ^ 5; //定义端口
- sbit RW = P2 ^ 6;
- sbit EN = P2 ^ 7;
- #define RS_CLR RS = 0
- #define RS_SET RS = 1
- #define RW_CLR RW = 0
- #define RW_SET RW = 1
- #define EN_CLR EN = 0
- #define EN_SET EN = 1
- #define DataPort P0
- uchar key_temp, key_num, display_num = 0, KKK = 0;
- uchar mode_flag = 0;
- uchar bit_select_counter;
- uchar PM_data_buf[8] = {0, 2, 0, 0, 0, 2, 0, 0};
- //-------开机、读数据、关机指令----------------------------------
- uchar code open[9] = {0xAA, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x66, 0xBB};
- uchar code autodata[9] = {0xAA, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x6B, 0xBB};
- //void Delay500ms( ) //@11.0592MHz
- //{
- // unsigned char i, j, k;
- // _nop_( );
- // i = 4;
- // j = 129;
- // k = 119;
- // do
- // {
- // do
- // {
- // while (--k);
- // } while (--j);
- // } while (--i);
- //}
- /*------------------------------------------------
- uS延时函数,含有输入参数 unsigned char t,无返回值
- unsigned char 是定义无符号字符变量,其值的范围是
- 0~255 这里使用晶振12M,精确延时请使用汇编,大致延时
- 长度如下 T=tx2+5 uS
- ------------------------------------------------*/
- void DelayUs2x(unsigned int t)
- {
- while(-- t);
- }
- /*------------------------------------------------
- mS延时函数,含有输入参数 unsigned char t,无返回值
- unsigned char 是定义无符号字符变量,其值的范围是
- 0~255 这里使用晶振12M,精确延时请使用汇编
- ------------------------------------------------*/
- void DelayMs(unsigned char t)
- {
- while(t --)
- {
- //大致延时1mS
- DelayUs2x(245);
- DelayUs2x(245);
- }
- }
- /*------------------------------------------------
- 判忙函数
- ------------------------------------------------*/
- bit LCD_Check_Busy(void)
- {
- DataPort= 0xFF;
- RS_CLR;
- RW_SET;
- EN_CLR;
- _nop_( );
- EN_SET;
- return (bit)(DataPort & 0x80);
- }
- /*------------------------------------------------
- 写入命令函数
- ------------------------------------------------*/
- void LCD_Write_Com(unsigned char com)
- {
- while(LCD_Check_Busy( )); //忙则等待
- RS_CLR;
- RW_CLR;
- EN_SET;
- DataPort = com;
- _nop_( );
- EN_CLR;
- }
- //=======数据解析===========
- void shuju_jiexi(void)
- {
- uint PM25, PM10;
- uint sss;
- PM25 = (((uint)PM25_data[0]) << 8) + PM25_data[1];
- PM10 = (((uint)PM10_data[0]) << 8) + PM10_data[1];
- //------------------------
- if(PM25 > 999)
- PM25 = 999;
- if(PM10 > 1500)
- PM10 = 1500;
-
- PM25_1 = PM25;
- PM10_1 = PM10;
- //===========分解PM2.5的值=========================
- sss = PM25;
- PM_ASC[0] = sss / 1000;
- sss = sss % 1000;
- PM_ASC[1] = sss / 100;
- sss = sss % 100;
- PM_ASC[2] = sss / 10;
- PM_ASC[3] = sss % 10;
- //
- //============分解PM10的值=========================
- sss = PM10;
- PM_ASC[4] = sss / 1000;
- sss = sss % 1000;
- PM_ASC[5] = sss / 100;
- sss = sss % 100;
- PM_ASC[6] = sss / 10;
- PM_ASC[7] = sss % 10;
- }
- void Display_cal( )//显示切换
- {
- if(P45 == 0)
- {
- Delay_1ms(100);
- if(P45 == 0 && mode_flag == 0)
- {
- LCD_Clear( );//清屏
- display_num ++;
-
- if(display_num > 2)
- {
- display_num = 0;
- }
- }
- }
- else
- {
- display_num = display_num;
- }
- }
- uchar Key_Drive( )//键值读取
- {
- if(P20 == 0)
- {
- Delay_1ms(100);
- if(P20 == 0)
- {
- key_temp = 1;
- }
- }
- if(P21 == 0)
- {
- Delay_1ms(100);
- if(P21 == 0)
- {
- key_temp = 2;
- }
- }
- if(P22 == 0)
- {
- Delay_1ms(100);
- if(P22 == 0)
- {
- key_temp = 3;
- }
- }
- return key_temp;
- }
- void Set_Drive( )//设置数值
- {
- uchar temp;
- temp = Key_Drive( );
- key_temp = 0;
-
- if(temp == 2)//选择修改位置
- {
- if(mode_flag != 0)
- {
- bit_select_counter ++;
- if(bit_select_counter > 7)
- {
- bit_select_counter = 0;
- }
- }
- }
-
- if(temp == 3)//修改数据值按键
- {
- if(mode_flag != 0)
- {
- PM_data_buf[bit_select_counter] += 1;
- if(PM_data_buf[bit_select_counter] > 9)
- {
- PM_data_buf[bit_select_counter] = 0;
- }
- }
- }
-
- if(temp == 1)//M
- {
- bit_select_counter = 0;
- mode_flag ++;
-
- if(mode_flag > 1)
- {
- mode_flag = 0;
- }
- }
- }
- //定时器T2,用于显示, 70ms刷新一次
- /******定时器2初始化函数******/
- void Timer2_Init( ) //初始化子函数
- {
- T2MOD = 0;//T2定时器,16位自动重装
- T2CON = 0;
- TL2 = 0x00;
- TH2 = 0x04;
- RCAP2L = 0x00;
- RCAP2H = 0x04;
- TR2 = 1;
- EA = 1;
- ET2 = 1;
- }
- void DS1302InputByte(unsigned char d) //实时时钟写入一字节(内部函数)
- {
- unsigned char i;
- ACC = d;
- for(i=8; i>0; i--)
- {
- DS1302_IO = ACC0; //相当于汇编中的 RRC
- DS1302_CLK = 1;
- DS1302_CLK = 0;
- ACC = ACC >> 1;
- }
- }
- unsigned char DS1302OutputByte(void) //实时时钟读取一字节(内部函数)
- {
- unsigned char i;
- for(i=8; i>0; i--)
- {
- ACC = ACC >>1; //相当于汇编中的 RRC
- ACC7 = DS1302_IO;
- DS1302_CLK = 1;
- DS1302_CLK = 0;
- }
- return(ACC);
- }
- void Write1302(unsigned char ucAddr, unsigned char ucDa) //ucAddr: DS1302地址, ucData: 要写的数据
- {
- DS1302_RST = 0;
- DS1302_CLK = 0;
- DS1302_RST = 1;
- DS1302InputByte(ucAddr); // 地址,命令
- DS1302InputByte(ucDa); // 写1Byte数据
- // DS1302_CLK = 1;
- DS1302_RST = 0;
- }
- unsigned char Read1302(unsigned char ucAddr) //读取DS1302某地址的数据
- {
- unsigned char ucData;
- DS1302_RST = 0;
- DS1302_CLK = 0;
- DS1302_RST = 1;
- DS1302InputByte(ucAddr|0x01); // 地址,命令
- ucData = DS1302OutputByte(); // 读1Byte数据
- // DS1302_CLK = 1;
- DS1302_RST = 0;
- return(ucData);
- }
- void DS1302_SetProtect(bit flag) //是否写保护
- {
- if(flag)
- Write1302(0x8E,0x10);
- else
- Write1302(0x8E,0x00);
- }
- void DS1302_SetTime(unsigned char Address, unsigned char Value) // 设置时间函数
- {
- DS1302_SetProtect(0);
- Write1302(Address, ((Value/10)<<4 | (Value%10)));
- }
- void DS1302_GetTime(SYSTEMTIME *Time)
- {
- unsigned char ReadValue;
- ReadValue = Read1302(DS1302_SECOND);
- Time->Second = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); //八进制转换成十进制
- ReadValue = Read1302(DS1302_MINUTE);
- Time->Minute = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
- ReadValue = Read1302(DS1302_HOUR);
- Time->Hour = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
- ReadValue = Read1302(DS1302_DAY);
- Time->Day = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
- ReadValue = Read1302(DS1302_WEEK);
- Time->Week = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
- ReadValue = Read1302(DS1302_MONTH);
- Time->Month = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
- ReadValue = Read1302(DS1302_YEAR);
- Time->Year = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
- }
- void DateToStr(SYSTEMTIME *Time) //
- {
- Time->DateString[0] = Time->Year/10 ; //分离出个位和十位
- Time->DateString[1] = Time->Year%10 ;
- Time->DateString[2] = '-';
- Time->DateString[3] = Time->Month/10;
- Time->DateString[4] = Time->Month%10 ;
- Time->DateString[5] = '-';
- Time->DateString[6] = Time->Day/10 ;
- Time->DateString[7] = Time->Day%10 ;
- Time->DateString[8] = '\0';
- }
- void TimeToStr(SYSTEMTIME *Time)
- {
- Time->TimeString[0] = Time->Hour/10 ;
- Time->TimeString[1] = Time->Hour%10 ;
- Time->TimeString[2] = ':';
- Time->TimeString[3] = Time->Minute/10 ;
- Time->TimeString[4] = Time->Minute%10 ;
- Time->TimeString[5] = ':';
- Time->TimeString[6] = Time->Second/10;
- Time->TimeString[7] = Time->Second%10 ;
- Time->DateString[8] = '\0';
- }
- void Initial_DS1302(void)
- {
- unsigned char Second=Read1302(DS1302_SECOND);
- if(Second&0x80)
- DS1302_SetTime(DS1302_SECOND,0);
- }
- void main( )
- {
- uchar i;
- uint HR_crc = 0;
- uint check = 0;
-
- SYSTEMTIME CurrentTime;
-
- P44 = 0;
- display_num = 0;
- mode_flag = 0;
- Serial_Init( ); //串口初始化
- Timer0_Init( ); //T0 初始化
- LCD_Init( );
- LCD_Clear( );//清屏
- Initial_DS1302( );
- Timer2_Init( );
-
- // DS1302_SetTime(DS1302_YEAR, 20); //设置时间
- // DS1302_SetTime(DS1302_MONTH, 03);
- // DS1302_SetTime(DS1302_DAY, 15);
- // DS1302_SetTime(DS1302_HOUR, 18); //设置时间
- // DS1302_SetTime(DS1302_MINUTE, 42);
- // DS1302_SetTime(DS1302_SECOND, 30);
-
- Delay_1ms(100);
- //---------开机指令--------------------
- for(i = 0; i < 9; i ++)
- {
- Serial_send(open[i]);
- Delay_1ms(1);
- }
- Delay_1ms(10);
- //---------将传感器改为自动发送模式--------
- for(i = 0; i < 9; i ++)
- {
- Serial_send(autodata[i]);
- Delay_1ms(1);
- }
- Delay_1ms(10);
-
- while(1)
- {
- Display_cal( );
-
- DS1302_GetTime(&CurrentTime);
- DateToStr(&CurrentTime);
- TimeToStr(&CurrentTime);
-
- d0=CurrentTime.DateString[0];
- d1=CurrentTime.DateString[1];
- d2=CurrentTime.DateString[3];
- d3=CurrentTime.DateString[4];
- d4=CurrentTime.DateString[6];
- d5=CurrentTime.DateString[7];
-
- t0=CurrentTime.TimeString[0];
- t1=CurrentTime.TimeString[1];
- t2=CurrentTime.TimeString[3];
- t3=CurrentTime.TimeString[4];
- t4=CurrentTime.TimeString[6];
- t5=CurrentTime.TimeString[7];
-
- R_data = 1;
- /*------------------------------------------------
- // 在接收一帧数据后,首先检验接收数据是否正确
- // 若正确,将数据分解,然后显示 */
- if(complete_flag == 1)
- {
- //--------------计算校验和-------------------
- HR_crc = 0;
- check = 0;
- for(i = 0; i < 30; i ++)
- {
- HR_crc += USART_RX_BUF[i];
- }
- check = (((uint)USART_RX_BUF[30]) << 8) + USART_RX_BUF[31];
- //---------------------------------------------
- if((HR_crc == check)) //判断接收的数据是否正确
- {
- //------------数据解析-------------------
- PM10_data[0] = USART_RX_BUF[8];
- PM10_data[1] = USART_RX_BUF[9];
- PM25_data[0] = USART_RX_BUF[6];
- PM25_data[1] = USART_RX_BUF[7];
- shuju_jiexi( ); //将检测数值由十六进制转成ASCII码
- }
- complete_flag = 0;
- }
-
- if(display_num == 0)
- {
- LCD_Write_String(0, 0, "PM2.5:");
- LCD_Write_Char(6, 0, PM_ASC[0] + '0');
- LCD_Write_Char(7, 0, PM_ASC[1] + '0');
- LCD_Write_Char(8, 0, PM_ASC[2] + '0');
- LCD_Write_Char(9, 0, PM_ASC[3] + '0');
- LCD_Write_String(10, 0, "ug/m3");
-
- LCD_Write_String(0, 1, "PM10:");
- LCD_Write_Char(5, 1, PM_ASC[4] + '0');
- LCD_Write_Char(6, 1, PM_ASC[5] + '0');
- LCD_Write_Char(7, 1, PM_ASC[6] + '0');
- LCD_Write_Char(8, 1, PM_ASC[7] + '0');
- LCD_Write_String(9, 1, "ug/m3");
- }
- else if(display_num == 1)
- {
- Set_Drive( );
-
- LCD_Write_String(0, 0, "SET PM2.5:");
- LCD_Write_String(5, 1, "PM10:");
-
- LCD_Write_Char(11, 0, PM_data_buf[0] + '0');
- LCD_Write_Char(12, 0, PM_data_buf[1] + '0');
- LCD_Write_Char(13, 0, PM_data_buf[2] + '0');
- LCD_Write_Char(14, 0, PM_data_buf[3] + '0');
-
- LCD_Write_Char(10, 1, PM_data_buf[4] + '0');
- LCD_Write_Char(11, 1, PM_data_buf[5] + '0');
- LCD_Write_Char(12, 1, PM_data_buf[6] + '0');
- LCD_Write_Char(13, 1, PM_data_buf[7] + '0');
- }
- else
- {
- LCD_Write_String(3, 0, "20");
- LCD_Write_Char(5, 0, d0 + '0');
- LCD_Write_Char(6, 0, d1 + '0');
- LCD_Write_Char(7, 0, '-');
- LCD_Write_Char(8, 0, d2 + '0');
- LCD_Write_Char(9, 0, d3 + '0');
- LCD_Write_Char(10, 0, '-');
- LCD_Write_Char(11, 0, d4 + '0');
- LCD_Write_Char(12, 0, d5 + '0');
-
- LCD_Write_Char(4, 1, t0 + '0');
- LCD_Write_Char(5, 1, t1 + '0');
- LCD_Write_Char(6, 1, ':');
- LCD_Write_Char(7, 1, t2 + '0');
- LCD_Write_Char(8, 1, t3 + '0');
- LCD_Write_Char(9, 1, ':');
- LCD_Write_Char(10, 1, t4 + '0');
- LCD_Write_Char(11, 1, t5 + '0');
- }
-
- if(mode_flag == 1)//光标闪烁
- {
- switch(bit_select_counter)
- {
- case 0:
- LCD_Write_Com(0x80 + 11);
- LCD_Write_Com(0x0f);
- break;
- case 1:
- LCD_Write_Com(0x80 + 12);
- LCD_Write_Com(0x0f);
- break;
- case 2:
- LCD_Write_Com(0x80 + 13);
- LCD_Write_Com(0x0f);
- break;
- case 3:
- LCD_Write_Com(0x80 + 14);
- LCD_Write_Com(0x0f);
- break;
- case 4:
- LCD_Write_Com(0xC0 + 10);
- LCD_Write_Com(0x0f);
- break;
- case 5:
- LCD_Write_Com(0xC0 + 11);
- LCD_Write_Com(0x0f);
- break;
- case 6:
- LCD_Write_Com(0xC0 + 12);
- LCD_Write_Com(0x0f);
- break;
- case 7:
- LCD_Write_Com(0xC0 + 13);
- LCD_Write_Com(0x0f);
- break;
- }
- }
- if(mode_flag == 0)
- {
- LCD_Write_Com(0x0C); /*显示开及光标设置*/
-
- PM25_2 = PM_data_buf[0] * 1000 + PM_data_buf[1] * 100 + PM_data_buf[2] * 10 + PM_data_buf[3];
- PM10_2 = PM_data_buf[4] * 1000 + PM_data_buf[5] * 100 + PM_data_buf[6] * 10 + PM_data_buf[7];
-
- if(PM25_1 >= PM25_2 || PM10_1 >= PM10_2)
- {
- P44 = 1;
- KKK = 1;
- }
- else
- {
- P44 = 0;
- KKK = 0;
- }
- if(KKK == 1)
- {
- if(PM25_1 >= PM25_2)
- {
- P24 = 0;
- Delay_1ms(20);
- P24 = 1;
- Delay_1ms(20);
- }
- else
- {
- P24 = 1;
- ……………………
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
所有资料51hei提供下载:
pcb+源程序.7z
(1.71 MB, 下载次数: 47)
|