课设-51单片机+PM2.5+温湿度(程序+原理图+Protel仿真)
生活随笔
收集整理的這篇文章主要介紹了
课设-51单片机+PM2.5+温湿度(程序+原理图+Protel仿真)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
51單片機PM2.5+溫濕度(程序+原理圖+Protel仿真)
- 一、功能
- 二、模塊
- 三、代碼程序
- 四、PCB原理圖
一、功能
本系統以STC89C52單片機為核心,主要包括傳感器溫濕度采集,傳感器PM2.5采集,LCD液晶顯示,聲光報警和按鍵設置等部分。系統通過搭建的傳感器元件采集家居中溫度、濕度和PM2.5濃度,并實時顯示上述采集數據。
二、模塊
2.1、PM 2.5模塊
模塊引腳說明:
采集數據對照:
2.2、DHT11溫濕度傳感器:
模塊接口說明
DHT11數據格式:一次完整的數據傳輸為40bit,高位先出。
數據格式:
8bit濕度整數數據
+8bit濕度小數數據
+8bi溫度整數數據
+8bit溫度小數數據
+8bit校驗和
數據傳送正確時校驗和數據等于**“ 8bit濕度整數數據+8bit濕度小數數據+8bi溫度整數數據+8bit溫度小數數據”** 所得結果的末8位。
三、代碼程序
#include <reg52.h> #include <intrins.h>#define uchar unsigned char // 以后unsigned char就可以用uchar代替 #define uint unsigned int // 以后unsigned int 就可以用uint 代替sfr ISP_DATA = 0xe2; // 數據寄存器 sfr ISP_ADDRH = 0xe3; // 地址寄存器高八位 sfr ISP_ADDRL = 0xe4; // 地址寄存器低八位 sfr ISP_CMD = 0xe5; // 命令寄存器 sfr ISP_TRIG = 0xe6; // 命令觸發寄存器 sfr ISP_CONTR = 0xe7; // 命令寄存器sbit LcdRs_P = P1^2; // 1602液晶的RS管腳 sbit LcdRw_P = P1^3; // 1602液晶的RW管腳 sbit LcdEn_P = P1^4; // 1602液晶的EN管腳 sbit KeySet_P = P3^2; // “設置”按鍵的管腳 sbit KeyDown_P = P3^3; // “減”按鍵的管腳 sbit KeyUp_P = P3^4; // “加”按鍵的管腳 sbit Buzzer_P = P1^5; // 蜂鳴器 sbit DHT11_P = P1^1; // 溫濕度傳感器DHT11數據接入 sbit LedTH_P = P2^0; // 溫度過高報警指示燈 sbit LedTL_P = P2^1; // 溫度過低報警指示燈 sbit LedHH_P = P2^2; // 濕度過高報警指示燈 sbit LedHL_P = P2^3; // 濕度過低報警指示燈 sbit LedPM_P = P2^4; // PM2.5過高報警指示燈uchar temp; // 保存溫度 uchar humi; // 保存濕度 uint pm; // 保存PM2.5uchar gIndex=0; // 串口接收索引 uint Value[20]={0}; // 串口數據緩存區uchar AlarmTL; // 溫度下限報警值 uchar AlarmTH; // 溫度上限報警值 uchar AlarmHL; // 濕度下限報警值 uchar AlarmHH; // 濕度上限報警值 uint AlarmPM; // PM2.5報警值/*********************************************************/ // 單片機內部EEPROM不使能 /*********************************************************/ void ISP_Disable() {ISP_CONTR = 0;ISP_ADDRH = 0;ISP_ADDRL = 0; }/*********************************************************/ // 從單片機內部EEPROM讀一個字節,從0x2000地址開始 /*********************************************************/ unsigned char EEPROM_Read(unsigned int add) {ISP_DATA = 0x00;ISP_CONTR = 0x83;ISP_CMD = 0x01;ISP_ADDRH = (unsigned char)(add>>8);ISP_ADDRL = (unsigned char)(add&0xff);// 對STC89C51系列來說,每次要寫入0x46,再寫入0xB9,ISP/IAP才會生效ISP_TRIG = 0x46; ISP_TRIG = 0xB9;_nop_();ISP_Disable();return (ISP_DATA); }/*********************************************************/ // 往單片機內部EEPROM寫一個字節,從0x2000地址開始 /*********************************************************/ void EEPROM_Write(unsigned int add,unsigned char ch) {ISP_CONTR = 0x83;ISP_CMD = 0x02;ISP_ADDRH = (unsigned char)(add>>8);ISP_ADDRL = (unsigned char)(add&0xff);ISP_DATA = ch;ISP_TRIG = 0x46;ISP_TRIG = 0xB9;_nop_();ISP_Disable(); }/*********************************************************/ // 擦除單片機內部EEPROM的一個扇區 // 寫8個扇區中隨便一個的地址,便擦除該扇區,寫入前要先擦除 /*********************************************************/ void Sector_Erase(unsigned int add) {ISP_CONTR = 0x83;ISP_CMD = 0x03;ISP_ADDRH = (unsigned char)(add>>8);ISP_ADDRL = (unsigned char)(add&0xff);ISP_TRIG = 0x46;ISP_TRIG = 0xB9;_nop_();ISP_Disable(); }/*********************************************************/ // 毫秒級的延時函數,time是要延時的毫秒數 /*********************************************************/ void DelayMs(uint time) {uint i,j;for(i=0;i<time;i++)for(j=0;j<112;j++); }/*********************************************************/ // 10us級延時程序 /*********************************************************/ void Delay10us() {_nop_(); // 執行一條指令,延時1微秒_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_(); }/*********************************************************/ // 讀取DHT11單總線上的一個字節 /*********************************************************/ uchar DhtReadByte(void) { bit bit_i; uchar j;uchar dat=0;for(j=0;j<8;j++) {while(!DHT11_P); // 等待低電平結束 Delay10us(); // 延時Delay10us();Delay10us();if(DHT11_P==1) // 判斷數據線是高電平還是低電平{bit_i=1; while(DHT11_P);} else{bit_i=0;}dat<<=1; // 將該位移位保存到dat變量中dat|=bit_i; }return(dat); }/*********************************************************/ // 讀取DHT11的一幀數據,濕高、濕低(0)、溫高、溫低(0)、校驗碼 /*********************************************************/ void ReadDhtData() { uchar HumiHig; // 濕度高檢測值uchar HumiLow; // 濕度低檢測值 uchar TemHig; // 溫度高檢測值uchar TemLow; // 溫度低檢測值uchar check; // 校驗字節 DHT11_P=0; // 主機拉低DelayMs(20); // 保持20毫秒DHT11_P=1; // DATA總線由上拉電阻拉高Delay10us(); // 延時等待30usDelay10us();Delay10us();while(!DHT11_P); // 等待DHT的低電平結束while(DHT11_P); // 等待DHT的高電平結束//進入數據接收狀態HumiHig = DhtReadByte(); // 濕度高8位HumiLow = DhtReadByte(); // 濕度低8為,總為0TemHig = DhtReadByte(); // 溫度高8位 TemLow = DhtReadByte(); // 溫度低8為,總為0 check = DhtReadByte(); // 8位校驗碼,其值等于讀出的四個字節相加之和的低8位DHT11_P=1; // 拉高總線if(check==HumiHig + HumiLow + TemHig + TemLow) // 如果收到的數據無誤{temp=TemHig; // 將溫度的檢測結果賦值給全局變量temphumi=HumiHig; // 將濕度的檢測結果賦值給全局變量humi} }/*********************************************************/ // 1602液晶寫命令函數,cmd就是要寫入的命令 /*********************************************************/ void LcdWriteCmd(uchar cmd) { LcdRs_P = 0;LcdRw_P = 0;LcdEn_P = 0;P0=cmd;DelayMs(2);LcdEn_P = 1; DelayMs(2);LcdEn_P = 0; }/*********************************************************/ // 1602液晶寫數據函數,dat就是要寫入的數據 /*********************************************************/ void LcdWriteData(uchar dat) {LcdRs_P = 1; LcdRw_P = 0;LcdEn_P = 0;P0=dat;DelayMs(2);LcdEn_P = 1; DelayMs(2);LcdEn_P = 0; }/*********************************************************/ // 1602液晶初始化函數 /*********************************************************/ void LcdInit() {LcdWriteCmd(0x38); // 16*2顯示,5*7點陣,8位數據口LcdWriteCmd(0x0C); // 開顯示,不顯示光標LcdWriteCmd(0x06); // 地址加1,當寫入數據后光標右移LcdWriteCmd(0x01); // 清屏 }/*********************************************************/ // 液晶光標定位函數 /*********************************************************/ void LcdGotoXY(uchar line,uchar column) {// 第一行if(line==0) LcdWriteCmd(0x80+column); // 第二行if(line==1) LcdWriteCmd(0x80+0x40+column); }/*********************************************************/ // 液晶輸出數字(PM2.5的) /*********************************************************/ void LcdPrintNum1(uint num) {LcdWriteData(num/100+48); // 百位LcdWriteData(num%100/10+48); // 十位LcdWriteData(num%10+48); // 個位 }/*********************************************************/ // 液晶輸出數字(溫濕度的) /*********************************************************/ void LcdPrintNum2(uchar num) {LcdWriteData(num/10+48); // 十位LcdWriteData(num%10+48); // 個位 }/*********************************************************/ // 液晶輸出字符串函數 /*********************************************************/ void LcdPrintStr(uchar *str) {while(*str!='\0')LcdWriteData(*str++); }/*********************************************************/ // 液晶顯示內容初始化 /*********************************************************/ void LcdShowInit() {LcdGotoXY(0,0); // 液晶光標定位到第0行第0列LcdPrintStr(" PM2.5: ug/m3 "); // 顯示內容LcdGotoXY(1,0); // 液晶光標定位到第1行第0列LcdPrintStr("T: C H: %RH"); // 顯示內容LcdGotoXY(1,4); // 溫度單位攝氏度上面的圓圈符號LcdWriteData(0xdf); }/*********************************************************/ // 串口初始化 /*********************************************************/ void UartInit() {SCON = 0x50; // 配置串口寄存器TMOD = 0x20; // 配置定時器寄存器TH1 = 0xf4; // 計算波特率的值為2400TL1 = 0xf4; // 計算波特率的值為2400EA = 1; // 打開總中斷ES = 1; // 打開串口中斷TR1 = 1; // 啟動定時器 }/*********************************************************/ // 按鍵掃描 /*********************************************************/ void KeyScanf() {if(KeySet_P==0) // 判斷設置按鍵是否被按下{EA=0;/*將液晶顯示改為設置溫度的頁面****************************************************/LcdWriteCmd(0x01); LcdGotoXY(0,0);LcdPrintStr("Temperature Set ");LcdGotoXY(1,0);LcdPrintStr(" - C ");LcdGotoXY(1,10); LcdWriteData(0xdf); LcdGotoXY(1,4); // 在液晶上填充溫度的下限值 LcdPrintNum2(AlarmTL); LcdGotoXY(1,7); // 在液晶上填充溫度的上限值LcdPrintNum2(AlarmTH);LcdGotoXY(1,5); // 光標定位到第1行第5列LcdWriteCmd(0x0f); // 光標閃爍DelayMs(10); // 去除按鍵按下的抖動while(!KeySet_P); // 等待按鍵釋放DelayMs(10); // 去除按鍵松開的抖動/*設置溫度的下限值****************************************************************/while(KeySet_P) // “設置鍵”沒有被按下,則一直處于溫度下限的設置{if(KeyDown_P==0) // 判斷 “減按鍵“ 是否被按下 {if(AlarmTL>0) // 只有當溫度下限值大于0時,才能減1AlarmTL--;LcdGotoXY(1,4); // 重新刷新顯示更改后的溫度下限值 LcdPrintNum2(AlarmTL); LcdGotoXY(1,5); // 重新定位閃爍的光標位置DelayMs(350); // 延時}if(KeyUp_P==0) // 判斷 “加按鍵“ 是否被按下{if(AlarmTL<99) // 只有當溫度下限值小于99時,才能加1AlarmTL++;LcdGotoXY(1,4); // 重新刷新顯示更改后的溫度下限值LcdPrintNum2(AlarmTL);LcdGotoXY(1,5); // 重新定位閃爍的光標位置DelayMs(350); // 延時} }LcdGotoXY(1,8);DelayMs(10); // 去除按鍵按下的抖動while(!KeySet_P); // 等待按鍵釋放DelayMs(10); // 去除按鍵松開的抖動/*設置溫度的上限值****************************************************************/ while(KeySet_P) // “設置鍵”沒有被按下,則一直處于溫度上限的設置{if(KeyDown_P==0) // 判斷 “減按鍵“ 是否被按下{if(AlarmTH>0) // 只有當溫度上限值大于0時,才能減1 AlarmTH--;LcdGotoXY(1,7); // 重新刷新顯示更改后的溫度上限值LcdPrintNum2(AlarmTH);LcdGotoXY(1,8); // 重新定位閃爍的光標位置DelayMs(350); // 延時}if(KeyUp_P==0) // 判斷 “加按鍵“ 是否被按下{if(AlarmTH<99) // 只有當溫度上限值小于99時,才能加1AlarmTH++;LcdGotoXY(1,7); // 重新刷新顯示更改后的溫度上限值 LcdPrintNum2(AlarmTH);LcdGotoXY(1,8); // 重新定位閃爍的光標位置DelayMs(350); // 延時} }/*將液晶顯示改為設置濕度的頁面****************************************************/LcdWriteCmd(0x01); LcdGotoXY(0,0);LcdPrintStr(" Humidity Set ");LcdGotoXY(1,0);LcdPrintStr(" - %RH "); LcdGotoXY(1,4); // 在液晶上填充濕度的下限值 LcdPrintNum2(AlarmHL); LcdGotoXY(1,7); // 在液晶上填充濕度的上限值LcdPrintNum2(AlarmHH);LcdGotoXY(1,5); // 光標定位到第1行第5列DelayMs(10); // 去除按鍵按下的抖動while(!KeySet_P); // 等待按鍵釋放DelayMs(10);/*設置濕度的下限值****************************************************************/while(KeySet_P) // “設置鍵”沒有被按下,則一直處于濕度下限的設置{if(KeyDown_P==0) // 判斷 “減按鍵“ 是否被按下{if(AlarmHL>0) // 只有當濕度下限值大于0時,才能減1AlarmHL--;LcdGotoXY(1,4); // 重新刷新顯示更改后的濕度下限值 LcdPrintNum2(AlarmHL);LcdGotoXY(1,5); // 重新定位閃爍的光標位置DelayMs(350);}if(KeyUp_P==0) // 判斷 “加按鍵“ 是否被按下{if(AlarmHL<99) // 只有當濕度下限值小于99時,才能加1AlarmHL++;LcdGotoXY(1,4); // 重新刷新顯示更改后的濕度下限值LcdPrintNum2(AlarmHL);LcdGotoXY(1,5); // 重新定位閃爍的光標位置DelayMs(350); // 延時} }LcdGotoXY(1,8);DelayMs(10); // 去除按鍵按下的抖動while(!KeySet_P); // 等待按鍵釋放DelayMs(10); // 去除按鍵松開的抖動/*設置濕度的上限值****************************************************************/while(KeySet_P) // “設置鍵”沒有被按下,則一直處于濕度上限的設置{if(KeyDown_P==0) // 判斷 “減按鍵“ 是否被按下{if(AlarmHH>0) // 只有當濕度上限值大于0時,才能減1AlarmHH--;LcdGotoXY(1,7); // 重新刷新顯示更改后的濕度上限值LcdPrintNum2(AlarmHH);LcdGotoXY(1,8); // 重新定位閃爍的光標位置DelayMs(350);}if(KeyUp_P==0) // 判斷 “加按鍵“ 是否被按下{if(AlarmHH<99) // 只有當濕度上限值小于99時,才能加1AlarmHH++;LcdGotoXY(1,7); // 重新刷新顯示更改后的濕度上限值 LcdPrintNum2(AlarmHH);LcdGotoXY(1,8); // 重新定位閃爍的光標位置DelayMs(350); // 延時} }/*將液晶顯示改為設置PM2.5的頁面****************************************************/LcdWriteCmd(0x01); // 設置界面的顯示框架LcdGotoXY(0,0);LcdPrintStr(" PM2.5 Set ");LcdGotoXY(1,0);LcdPrintStr(" ug/m3 ");LcdGotoXY(1,4); // 顯示當前的報警值LcdPrintNum1(AlarmPM); LcdGotoXY(1,6); // 光標定位到第1行第6列DelayMs(10); // 去除按鍵按下的抖動while(!KeySet_P); // 等待按鍵釋放DelayMs(10);while(KeySet_P) // “設置鍵”沒有被按下,則一直處于光強下限的設置{if(KeyDown_P==0) // 判斷 “減按鍵“ 是否被按下{if(AlarmPM>1) // 只有gAlarmPM大于1才能減1 AlarmPM--; LcdGotoXY(1,4); // 液晶光標定位LcdPrintNum1(AlarmPM); // 刷新改變后的報警值LcdGotoXY(1,6);DelayMs(200); // 延時一下}if(KeyUp_P==0) // 判斷 “加按鍵“ 是否被按下{if(AlarmPM<999) // 只有gAlarmPM小于999才能加1AlarmPM++; LcdGotoXY(1,4); // 液晶光標定位LcdPrintNum1(AlarmPM); // 刷新改變后的報警值LcdGotoXY(1,6);DelayMs(200); // 延時一下} }/*完成設置,退出前的處理**********************************************************/LcdWriteCmd(0x0C); // 取消光標閃爍LcdShowInit(); // 液晶顯示為檢測界面的DelayMs(10); // 去除按鍵按下的抖動while(!KeySet_P); // 等待按鍵釋放DelayMs(10); // 去除按鍵松開的抖動Sector_Erase(0x2000); // 存儲之前必須先擦除EEPROM_Write(0x2000,AlarmTL); // 把溫度下限存入到EEPROM的0x2000這個地址EEPROM_Write(0x2001,AlarmTH); // 把溫度上限存入到EEPROM的0x2001這個地址EEPROM_Write(0x2002,AlarmHL); // 把濕度下限存入到EEPROM的0x2002這個地址EEPROM_Write(0x2003,AlarmHH); // 把濕度上限存入到EEPROM的0x2003這個地址EEPROM_Write(0x2004,AlarmPM/100); // 把PM2.5存入到EEPROM的0x2004和0x2005這兩個地址EEPROM_Write(0x2005,AlarmPM%100);EA=1;} }/*********************************************************/ // 報警判斷 /*********************************************************/ void AlarmJudge(void) {/*溫度*/if(temp>AlarmTH) // 溫度是否過高{LedTH_P=0;LedTL_P=1;}else if(temp<AlarmTL) // 溫度是否過低{LedTL_P=0;LedTH_P=1;}else // 溫度正常{LedTH_P=1;LedTL_P=1;}/*濕度*/if(humi>AlarmHH) // 濕度是否過高{LedHH_P=0;LedHL_P=1;}else if(humi<AlarmHL) // 濕度是否過低{LedHL_P=0;LedHH_P=1;}else // 濕度正常{LedHH_P=1;LedHL_P=1;}/*PM2.5*/if(pm>AlarmPM)LedPM_P=0;elseLedPM_P=1;/*蜂鳴器*/if((LedHH_P==0)||(LedHL_P==0)||(LedTH_P==0)||(LedTL_P==0)||(LedPM_P==0)) // 蜂鳴器判斷,只要至少1個報警燈亮,蜂鳴器就報警Buzzer_P=0;else Buzzer_P=1; }/*********************************************************/ // 報警值初始化 /*********************************************************/ void AlarmInit(void) {AlarmTL=EEPROM_Read(0x2000); // 從EEPROM的0x2000這個地址讀取溫度的報警下限AlarmTH=EEPROM_Read(0x2001); // 從EEPROM的0x2001這個地址讀取溫度的報警上限AlarmHL=EEPROM_Read(0x2002); // 從EEPROM的0x2002這個地址讀取濕度的報警下限 AlarmHH=EEPROM_Read(0x2003); // 從EEPROM的0x2003這個地址讀取濕度的報警上限AlarmPM=EEPROM_Read(0x2004)*100+EEPROM_Read(0x2005); // 讀取PM2.5報警值if((AlarmTL==0)||(AlarmTL>100)) // 如果溫度下限報警值讀出來異常(等于0或大于100),則重新賦值AlarmTL=20;if((AlarmTH==0)||(AlarmTH>100)) // 如果溫度上限報警值讀出來異常(等于0或大于100),則重新賦值AlarmTH=35;if((AlarmHL==0)||(AlarmHL>100)) // 如果溫度下限報警值讀出來異常(等于0或大于100),則重新賦值AlarmHL=40;if((AlarmHH==0)||(AlarmHH>100)) // 如果溫度上限報警值讀出來異常(等于0或大于100),則重新賦值AlarmHH=85;if((AlarmPM==0)||(AlarmPM>1300)) // 如果讀取到的報警值異常,則重新賦值AlarmPM=200; }/*********************************************************/ // 主函數 /*********************************************************/ void main(void) {uchar i; // 循環變量uint ret; // 保存PM2.5測量結果LcdInit(); // 液晶功能初始化LcdShowInit(); // 液晶顯示初始化UartInit(); // 串口初始化AlarmInit(); // 報警值初始化while(1){/*PM2.5的讀取*/ret=0; // 清零測量結果for(i=0;i<10;i++) // 將最新的20個測量結果求和{ret=ret+Value[i];}ret=ret/10; // 再除以20求得平均值pm=((ret*5)/1024.0)*180; // 將讀取到的電壓值轉換為灰塵濃度值 LcdGotoXY(0,7); // 液晶定位到第0行第6列LcdPrintNum1(pm); // 顯示測量結果/*溫濕度讀取*/EA=0;ReadDhtData(); // 檢測溫濕度數據;EA=1;LcdGotoXY(1,2); // 定位到要顯示溫度的地方LcdPrintNum2(temp); // 顯示溫度值LcdGotoXY(1,11); // 定位到要顯示濕度的地方LcdPrintNum2(humi); // 顯示濕度值// 報警判斷AlarmJudge(); /*按鍵掃描和延時*/for(i=0;i<30;i++){KeyScanf(); // 按鍵判斷DelayMs(10);}} }/*********************************************************/ // 串口中斷服務程序 /*********************************************************/ void UartInt(void) interrupt 4 {uchar VoutH,VoutL;if(RI==1){RI=0;if(SBUF==0xAA) // 起始位{while(!RI);VoutH=SBUF; // Vout(H)RI=0;while(!RI);VoutL=SBUF; // Vout(L)RI=0;while(!RI); // Vref(H)RI=0;while(!RI); // Vref(L)RI=0;while(!RI); // 校驗位RI=0;while(!RI); // 停止位RI=0;Value[gIndex]=VoutH*256+VoutL;gIndex++;if(gIndex==20)gIndex=0;}} }四、PCB原理圖
項目的仿真和程序代碼工程和原理圖已經放在下面公眾號里面,可以關注公眾號:Kevin的學習站,輸入關鍵字:“51單片機PM2.5+溫濕度”,就可以免費獲取啦!創作不易,但您的點贊、關注、收藏就是對我最大的鼓勵!
總結
以上是生活随笔為你收集整理的课设-51单片机+PM2.5+温湿度(程序+原理图+Protel仿真)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【乌拉喵.教程】不同负载下继电器的保护电
- 下一篇: 第41章 RS-485通讯实验—零死角玩