今天分享一個基于51單片機的濕度監(jiān)測仿真和程序。
仿真所用的濕度傳感器為SHT11。SHT11是瑞士Scnsirion公司推出的一款數(shù)字溫濕度傳感器芯片。該芯片廣泛應(yīng)用于暖通空調(diào)、汽車、消費電子、自動控制等領(lǐng)域。
?
其主要特點如下: 高度集成,將溫度感測、濕度感測、信號變換、A/D轉(zhuǎn)換和加熱器等功能集成到一個芯片上;? ?
提供二線數(shù)字串行接口SCK和DATA,接口簡單,支持CRC傳輸校驗,傳輸可靠性高;? ?
測量精度可編程調(diào)節(jié),內(nèi)置A/D轉(zhuǎn)換器(分辨率為8~12位,可以通過對芯片內(nèi)部寄存器編程米選擇);
引腳 | 名稱 | 功能 |
1 | GND | 地線 |
2 | DATA | 串行數(shù)據(jù)線 |
3 | SCK | 串行時鐘線 |
4 | VDD | 電源正 |
NC | NC | 懸空 |
傳輸啟動
數(shù)據(jù)傳輸初始化:
當SCK時鐘為高電平時,DATA翻轉(zhuǎn)位低電平,緊接著SCK變?yōu)榈碗娖剑?br />
隨后在SCK時鐘高電平時,DATA翻轉(zhuǎn)為高電平。 ?
?
void start_sht11(void) //啟動 //-------------------------------------------------------- { DATA=1; SCK=0; //數(shù)據(jù)為1,SCK=0 _nop_(); SCK=1; //第一個脈沖 _nop_(); DATA=0; //數(shù)據(jù)跌落 _nop_ (); SCK=0; //完成一個脈沖 _nop_(); _nop_(); _nop_(); SCK=1; //再一個脈沖 _nop_(); DATA=1; //數(shù)據(jù)變?yōu)? _nop_(); SCK=0; //完成該脈沖 }
?
讀寫數(shù)據(jù)
控制器向傳感器發(fā)送命令的過程中,DATA在SCK上升沿有效,且在SCK高電平時必須保持穩(wěn)定;DATA在SCK下降沿之后改變。(參考上圖 DATA valid write 段)
從傳感器讀取數(shù)據(jù)讀取數(shù)據(jù)的過程中,DATA Tv在SCK變低以后有效,且維持到下一個SCK的下降沿。(參考上圖 DATA valid read 段)
?
char read(void) //讀一個字節(jié) 返回應(yīng)答信號 //---------------------------------------------------------------------------------- // reads a byte form the Sensibus and gives an acknowledge in case of "ack=1" { unsigned char i,val=0; temp_LL=0; temp_h=0; DATA=1; //釋放數(shù)據(jù)總線 for (i=0x80;i>0;i/=2) //位移8位 { SCK=1; //上升沿讀入 if (DATA) val=(val | i); //確定值 SCK=0; } DATA=0; //讀應(yīng)答信號,有應(yīng)答為1,為應(yīng)答為0 通過CPU下拉為應(yīng)答 SCK=1; //第9個脈沖 _nop_(); _nop_(); _nop_(); //pulswith approx. 5 us SCK=0; DATA=1; //釋放數(shù)據(jù)總線 temp_h=val; val=0; ////低8位///////////////////////////// DATA=1; //釋放數(shù)據(jù)總線 for (i=0x80;i>0;i/=2) //位移8位 { SCK=1; //上升沿讀入 if (DATA) val=(val | i); //確定值 SCK=0; } DATA=1;//0; //不需要應(yīng)答 通過CPU下拉為應(yīng)答 SCK=1; //第9個脈沖 _nop_(); _nop_(); _nop_(); //pulswith approx. 5 us SCK=0; DATA=1; //釋放數(shù)據(jù)總線 temp_LL=val; return val ; } //////////// char write(unsigned char value) //寫一個字節(jié) 返回應(yīng)答信號 //--------------------------------------------------------- { unsigned char i ; ack=0; for (i=0x80;i>0;i/=2) //釋放數(shù)據(jù)總線 { if (i & value) DATA=1; //寫入值 else DATA=0; SCK=1; //上升沿寫入 _nop_(); _nop_(); _nop_(); //延時 SCK=0; } DATA=1; //釋放數(shù)據(jù)總線 SCK=1; //第9個脈沖 if (DATA==1) ack=1; //讀應(yīng)答信號 SCK=0; return ack; //error=1 表示沒有應(yīng)答 }
?
傳感器復(fù)位
如果出現(xiàn)通訊中斷,當DATA保持高電平時,觸發(fā)SCK時鐘9次或更多,然后發(fā)送“傳輸啟動”時序就可以完成傳感器的復(fù)位。
?
////////////////////////////////// void sht_rest(void) //復(fù)位 { unsigned char i; DATA=1; SCK=0; //數(shù)據(jù)為1 時鐘為0 for(i=0;i<9;i++) //9 個脈沖為 復(fù)位 { SCK=1; SCK=0; } start_sht11(); //啟動 }
?
Proteus仿真如下圖。主要功能是利用SHT11進行濕度的采集,并通過LCD1602顯示?。在仿真中通過SHT11上面的操作按鈕就可以改變SHT11的溫度和濕度數(shù)值。該仿真沒有對溫度數(shù)據(jù)進行采集和現(xiàn)實。
完整的程序如下
#include#include #define uint unsigned int #define uchar unsigned char /*------------------------------------------------ 硬件端口定義 ------------------------------------------------*/ sbit RS = P2^0; //定義端口 sbit RW = P2^1; sbit EN = P2^2; sbit LED = P2^4 ; sbit RELAY = P1^0 ; #define DataPort P0 sbit DATA =P2^6; //數(shù)據(jù) sbit SCK=P2^7; //時鐘 uchar cnt = 0 ; unsigned char temp_h ; //全局應(yīng)答變量 unsigned char temp_LL ;//全局應(yīng)答變量 unsigned char error ; //全局錯誤變量 unsigned char ack ; //全局應(yīng)答變量 #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 TEMP_ML 0x03 //000 0001 1 溫度命令 #define HUMI_ML 0x05 //000 0010 1 溫度命令 unsigned int xianzhi_h=0;//濕度顯值 uchar key ; uchar key_buf ; uchar temp ; uchar frq_cnt = 0; bit updat = 0 ; uchar delay_cnt = 0 ; #define TH0_BUF 0x3c #define TL0_BUF 0xaf void text_jisuan_humi(void); /*------------------------------------------------ uS延時函數(shù),含有輸入參數(shù) unsigned char t,無返回值 unsigned char 是定義無符號字符變量,其值的范圍是 0~255 這里使用晶振12M,精確延時請使用匯編,大致延時 長度如下 T=tx2+5 uS ------------------------------------------------*/ void DelayUs2x(unsigned char t) { while(--t); } /*------------------------------------------------ mS延時函數(shù),含有輸入?yún)?shù) unsigned char t,無返回值 unsigned char 是定義無符號字符變量,其值的范圍是 0~255 這里使用晶振12M,精確延時請使用匯編 ------------------------------------------------*/ void DelayMs(unsigned char t) { while(t--) { //大致延時1mS DelayUs2x(245); DelayUs2x(245); } } /*------------------------------------------------ 判忙函數(shù) ------------------------------------------------*/ bit LCD_Check_Busy(void) { DataPort= 0xFF; RS_CLR; RW_SET; EN_CLR; _nop_(); EN_SET; return (bit)(DataPort & 0x80); } /*------------------------------------------------ 寫入命令函數(shù) ------------------------------------------------*/ void LCD_Write_Com(unsigned char com) { // while(LCD_Check_Busy()); //忙則等待 DelayMs(5); RS_CLR; RW_CLR; EN_SET; DataPort= com; _nop_(); EN_CLR; } /*------------------------------------------------ 寫入數(shù)據(jù)函數(shù) ------------------------------------------------*/ void LCD_Write_Data(unsigned char Data) { //while(LCD_Check_Busy()); //忙則等待 DelayMs(5); RS_SET; RW_CLR; EN_SET; DataPort= Data; _nop_(); EN_CLR; } /*------------------------------------------------ 清屏函數(shù) ------------------------------------------------*/ void LCD_Clear(void) { LCD_Write_Com(0x01); DelayMs(5); } /*------------------------------------------------ 寫入字符串函數(shù) ------------------------------------------------*/ void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s) { if (y == 0) { LCD_Write_Com(0x80 + x); //表示第一行 } else { LCD_Write_Com(0xC0 + x); //表示第二行 } while (*s) { LCD_Write_Data( *s); s ++; } } /*------------------------------------------------ 寫入字符函數(shù) ------------------------------------------------*/ void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data) { if (y == 0) { LCD_Write_Com(0x80 + x); } else { LCD_Write_Com(0xC0 + x); } LCD_Write_Data( Data); } /*------------------------------------------------ 初始化函數(shù) ------------------------------------------------*/ void LCD_Init(void) { LCD_Write_Com(0x38); /*顯示模式設(shè)置*/ DelayMs(5); LCD_Write_Com(0x38); DelayMs(5); LCD_Write_Com(0x38); DelayMs(5); LCD_Write_Com(0x38); LCD_Write_Com(0x08); /*顯示關(guān)閉*/ LCD_Write_Com(0x01); /*顯示清屏*/ LCD_Write_Com(0x06); /*顯示光標移動設(shè)置*/ DelayMs(5); LCD_Write_Com(0x0C); /*顯示開及光標設(shè)置*/ } /*------------------------------------------------ 定時器0中斷 ------------------------------------------------*/ void TIM0_ISR() interrupt 1 { TR0=0; TH0 = TH0_BUF ; TL0 = TL0_BUF ; if(cnt<5) { cnt ++ ; }else { cnt = 0 ; updat = 1 ; } TR0=1; } /*******************************基本驅(qū)動程 *************************************/ ///////////////// ////////////////////// char read(void) //讀一個字節(jié) 返回應(yīng)答信號 //---------------------------------------------------------------------------------- // reads a byte form the Sensibus and gives an acknowledge in case of "ack=1" { unsigned char i,val=0; temp_LL=0; temp_h=0; DATA=1; //釋放數(shù)據(jù)總線 for (i=0x80;i>0;i/=2) //位移8位 { SCK=1; //上升沿讀入 if (DATA) val=(val | i); //確定值 SCK=0; } DATA=0; //讀應(yīng)答信號,有應(yīng)答為1,為應(yīng)答為0 通過CPU下拉為應(yīng)答 SCK=1; //第9個脈沖 _nop_(); _nop_(); _nop_(); //pulswith approx. 5 us SCK=0; DATA=1; //釋放數(shù)據(jù)總線 temp_h=val; val=0; ////低8位///////////////////////////// DATA=1; //釋放數(shù)據(jù)總線 for (i=0x80;i>0;i/=2) //位移8位 { SCK=1; //上升沿讀入 if (DATA) val=(val | i); //確定值 SCK=0; } DATA=1;//0; //不需要應(yīng)答 通過CPU下拉為應(yīng)答 SCK=1; //第9個脈沖 _nop_(); _nop_(); _nop_(); //pulswith approx. 5 us SCK=0; DATA=1; //釋放數(shù)據(jù)總線 temp_LL=val; return val ; } //////////// char write(unsigned char value) //寫一個字節(jié) 返回應(yīng)答信號 //--------------------------------------------------------- { unsigned char i ; ack=0; for (i=0x80;i>0;i/=2) //釋放數(shù)據(jù)總線 { if (i & value) DATA=1; //寫入值 else DATA=0; SCK=1; //上升沿寫入 _nop_(); _nop_(); _nop_(); //延時 SCK=0; } DATA=1; //釋放數(shù)據(jù)總線 SCK=1; //第9個脈沖 if (DATA==1) ack=1; //讀應(yīng)答信號 SCK=0; return ack; //error=1 表示沒有應(yīng)答 } //////// void start_sht11(void) //啟動 //-------------------------------------------------------- { DATA=1; SCK=0; //數(shù)據(jù)為1,SCK=0 _nop_(); SCK=1; //第一個脈沖 _nop_(); DATA=0; //數(shù)據(jù)跌落 _nop_ (); SCK=0; //完成一個脈沖 _nop_(); _nop_(); _nop_(); SCK=1; //再一個脈沖 _nop_(); DATA=1; //數(shù)據(jù)變?yōu)? _nop_(); SCK=0; //完成該脈沖 } ////////////////////////////////// void sht_rest(void) //復(fù)位 { unsigned char i; DATA=1; SCK=0; //數(shù)據(jù)為1 時鐘為0 for(i=0;i<9;i++) //9 個脈沖為 復(fù)位 { SCK=1; SCK=0; } start_sht11(); //啟動 } //////////////////////////////// //測量溫度或者是溫度,返回校驗值 void convert_data(unsigned char ml) { unsigned int i; start_sht11(); //啟動 write(ml);//寫入測溫度 if(ack==1) { sht_rest() ;//復(fù)位 write(ml);//寫入測溫度 } for (i=0;i<55535;i++){ if(DATA==0) break; } read();//讀溫度 } /////////濕度采集處理////// void get_humidata(void) { error=0; ack=0; sht_rest() ; //復(fù)位 convert_data(HUMI_ML); text_jisuan_humi(); } ///////計算濕度////// void text_jisuan_humi(void) { float aa=0,bb=0,humi_zi; int abcd=0; abcd = temp_h ; abcd = abcd <<8|temp_LL; aa = (float) abcd ; bb=aa*aa*2.8/1000000; aa=0.0405*aa; aa=aa-4-bb; humi_zi=aa; humi_zi=(humi_zi-3.3)*10; xianzhi_h=(int)humi_zi; } void Timer_Init(void) { TMOD|=0x01; //置定時器0工作方式1 EA=1; //打開全局中斷 ET0=1; //打開 定時器0 中斷 TR0=1; } void main(void) { char delay_cnt = 0; LED = 0 ; RELAY = 0 ; LCD_Init(); //液晶初始化 LCD_Clear(); //清屏 LCD_Write_String(0,0,"Humi:00.0%"); //初始顯示內(nèi)容 Timer_Init(); //定時器初始化 while(1) { if(updat) //數(shù)更新 { updat = 0 ; get_humidata(); LCD_Write_Char(5,0,0x30+xianzhi_h/100%10); LCD_Write_Char(6,0,0x30+xianzhi_h/10%10); LCD_Write_Char(8,0,0x30+xianzhi_h%10); } } }
審核編輯:劉清
評論
查看更多