如何為編程愛好者設計一款好玩的智能硬件(五)——初嘗試·把溫濕度給收集了(中)!


 

一、我的構想:如何為編程愛好者設計一款好玩的智能硬件(一)——即插即用、積木化、功能重組的智能硬件模塊構想

二、別人家的孩子:如何為編程愛好者設計一款好玩的智能硬件(二)——別人是如何設計硬件積木的!

三、MCU選型:如何為編程愛好者設計一款好玩的智能硬件(三)——該選什么樣的MCU呢?

四、溫濕度傳感器DHT11驅動封裝(上):如何為編程愛好者設計一款好玩的智能硬件(四)——初嘗試·把溫濕度給收集了(上)!

 

五、溫濕度傳感器DHT11驅動封裝(中):

   

  先打個預防針——本篇可能比較枯燥!與上一篇圖文代碼並茂挖空心思講DHT11的驅動程序的寫法不同,本篇將到處都是代碼!更坑的是會出現基於AT89C52平台的DHT11驅動C語言代碼、基於CC2541平台的DHT11的C語言驅動代碼、以及基於STM32平台的DHT11的C語言驅動代碼。然后最終根據這三個平台的不同情況抽象出一個盡量和平台無關、方便移植的DHT11底層驅動代碼,便於我們今后編寫”X-積木OS”。

認個慫:

  之所以用這種從普遍情況中找共性的笨方法寫DHT11平台無關的驅動,是因為我水平有限,感覺直接從上(datasheet中的通信協議)到下(和平台無關的驅動底層封裝)力不從心——不過我也正在努力學習linux系統的c程序架構方式,只是現在還不敢拿出來試手。

 

開始正文:

  為了方便展示DHT11底層驅動在各個平台上的共性(驅動協議)和平台特性,我將用不同的顏色表示平台共性中通信協議的不同過程:

※ 紅色表示:協議中啟動讀數據部分
※ 綠色表示:協議中DHT11應答MCU部分
※ 藍色表示:MCU讀取DHT11的40bit數據部分
※ 黑色表示:協議中終止讀取數據部分

  接着會在代碼后面談其平台特性,並提出針對這些平台特性的解決方法。

 

CC2541平台上DHT11驅動底層C語言封裝:

  1 #include <ioCC2540.h>
  2 
  3 typedef unsigned char uchar;
  4 typedef unsigned int  uint;
  5 
  6 #define DATA_PIN P0_0
  7 
  8 //溫濕度定義
  9 uchar ucharFLAG, uchartemp;
 10 uchar shidu_shi, shidu_ge, wendu_shi, wendu_ge = 4;
 11 uchar ucharT_data_H, ucharT_data_L, ucharRH_data_H, ucharRH_data_L, ucharcheckdata;
 12 uchar ucharT_data_H_temp, ucharT_data_L_temp, ucharRH_data_H_temp, ucharRH_data_L_temp, ucharcheckdata_temp;
 13 uchar ucharcomdata;
 14 
 15 //延時函數
 16 void Delay_us() //1 us延時
 17 {
 18     asm("nop");
 19     asm("nop");
 20     asm("nop");
 21     asm("nop");
 22     asm("nop");
 23     asm("nop");
 24     asm("nop");
 25     asm("nop");
 26     asm("nop");
 27 }
 28 
 29 void Delay_10us() //10 us延時
 30 {
 31     Delay_us();
 32     Delay_us();
 33     Delay_us();
 34     Delay_us();
 35     Delay_us();
 36     Delay_us();
 37     Delay_us();
 38     Delay_us();
 39     Delay_us();
 40     Delay_us();
 41 }
 42 
 43 void Delay_ms(uint Time)//n ms延時
 44 {
 45     unsigned char i;
 46     while(Time--)
 47     {
 48         for(i = 0; i < 100; i++)
 49             Delay_10us();
 50     }
 51 }
 52 
 53 //溫濕度傳感
 54 void COM(void)    // 溫濕寫入
 55 {
 56     uchar i;
 57     for(i = 0; i < 8; i++)
 58     {
 59         ucharFLAG = 2;
 60         while((!DATA_PIN) && ucharFLAG++);
 61         Delay_10us();
 62         Delay_10us();
 63         Delay_10us();
 64         uchartemp = 0;
 65         if(DATA_PIN)uchartemp = 1;
 66         ucharFLAG = 2;
 67         while((DATA_PIN) && ucharFLAG++);
 68         if(ucharFLAG == 1)break;
 69         ucharcomdata <<= 1;
 70         ucharcomdata |= uchartemp;
 71     }
 72 }
 73 
 74 void DHT11(void)   //溫濕傳感啟動
 75 {
 76 DATA_PIN = 0;                 //1、啟動 77 Delay_ms(19); //>18MS 78 DATA_PIN = 1;  79     P0DIR &= ~0x01; //重新配置IO口方向
 80     Delay_10us();
 81     Delay_10us();
 82     Delay_10us();
 83     Delay_10us();
 84     if(!DATA_PIN)
 85     {
 86 ucharFLAG = 2;              //2、等待 87 while((!DATA_PIN) && ucharFLAG++); 88 ucharFLAG = 2; 89 while((DATA_PIN) && ucharFLAG++);  90 COM();                   //3、讀取 91 ucharRH_data_H_temp = ucharcomdata; 92 COM(); 93 ucharRH_data_L_temp = ucharcomdata; 94 COM(); 95 ucharT_data_H_temp = ucharcomdata; 96 COM(); 97 ucharT_data_L_temp = ucharcomdata; 98 COM(); 99 ucharcheckdata_temp = ucharcomdata; 100 DATA_PIN = 1;               //4、終止 101         uchartemp = (ucharT_data_H_temp + ucharT_data_L_temp + ucharRH_data_H_temp + ucharRH_data_L_temp);
102         if(uchartemp == ucharcheckdata_temp)
103         {
104             ucharRH_data_H = ucharRH_data_H_temp;
105             ucharRH_data_L = ucharRH_data_L_temp;
106             ucharT_data_H = ucharT_data_H_temp;
107             ucharT_data_L = ucharT_data_L_temp;
108             ucharcheckdata = ucharcheckdata_temp;
109         }
110         wendu_shi = ucharT_data_H / 10;
111         wendu_ge = ucharT_data_H % 10;
112 
113         shidu_shi = ucharRH_data_H / 10;
114         shidu_ge = ucharRH_data_H % 10;
115     }
116     else //沒用成功讀取,返回0
117     {
118         wendu_shi = 0;
119         wendu_ge = 0;
120 
121         shidu_shi = 0;
122         shidu_ge = 0;
123     }
124 
125 P0DIR |= 0x01; //IO口需要重新配置
126 }

 

這里具有平台特性的是:

① 延時函數,不同平台延時函數必定不一樣

  ——>  采用宏定義延時函數,這樣只需修改宏而整個驅動程序內部不用修改

② CC2541的引腳的輸出和輸入特性需要配置,由於單線通信,所以MCU端的引腳就必須在輸入屬性和輸出屬性直接進行適時的切換:如上面代碼中第79行當MCU發送啟動信號之后便將該引腳置為輸入屬性,在125行,當本次數據傳輸結束時,又重新將該引腳置為輸入等待下次啟動

  ——>  待解決

③ 不同平台引腳定義方式不同,同一平台因為"X-積木"組合方式不同所以每個引腳都可能成為該引腳(不能僅僅用宏定義寫死)

  ——>  采用宏函數形式,做個動態的宏,以達到能表示全部引腳的功能。

 

AT89C52平台上DHT11驅動底層C語言封裝:

  1 //****************************************************************//
  2 //                    DHT11使用范例
  3 //單片機 : AT89S52 或 STC89C52RC
  4 // 功能  :串口發送溫濕度數據 晶振 11.0592M 波特率 9600
  5 //硬件連接: P2.0口為通訊口連接DHT11,DHT11的電源和地連接單片機的電源和地,單片機串口加MAX232連接電腦
  6 //****************************************************************//
  7 
  8 #include <reg51.h>
  9 #include <intrins.h>
 10 //
 11 typedef unsigned char  U8;       /* defined for unsigned 8-bits integer variable       無符號8位整型變量  */
 12 typedef signed   char  S8;       /* defined for signed 8-bits integer variable          有符號8位整型變量  */
 13 typedef unsigned int   U16;      /* defined for unsigned 16-bits integer variable       無符號16位整型變量 */
 14 typedef signed   int   S16;      /* defined for signed 16-bits integer variable       有符號16位整型變量 */
 15 typedef unsigned long  U32;      /* defined for unsigned 32-bits integer variable       無符號32位整型變量 */
 16 typedef signed   long  S32;      /* defined for signed 32-bits integer variable       有符號32位整型變量 */
 17 typedef float          F32;      /* single precision floating point variable (32bits) 單精度浮點數(32位長度) */
 18 typedef double         F64;      /* double precision floating point variable (64bits) 雙精度浮點數(64位長度) */
 19 //
 20 #define uchar unsigned char
 21 #define uint unsigned int
 22 #define   Data_0_time    4
 23 
 24 //----------------------------------------------//
 25 //----------------IO口定義區--------------------//
 26 //----------------------------------------------//
 27 sbit  P2_0  = P2 ^ 0 ;
 28 
 29 //----------------------------------------------//
 30 //----------------定義區--------------------//
 31 //----------------------------------------------//
 32 U8  U8FLAG, k;
 33 U8  U8count, U8temp;
 34 U8  U8T_data_H, U8T_data_L, U8RH_data_H, U8RH_data_L, U8checkdata;
 35 U8  U8T_data_H_temp, U8T_data_L_temp, U8RH_data_H_temp, U8RH_data_L_temp, U8checkdata_temp;
 36 U8  U8comdata;
 37 U8  outdata[5];  //定義發送的字節數
 38 U8  indata[5];
 39 U8  count, count_r = 0;
 40 U8  str[5] = {"RS232"};
 41 U16 U16temp1, U16temp2;
 42 SendData(U8 *a)
 43 {
 44     outdata[0] = a[0];
 45     outdata[1] = a[1];
 46     outdata[2] = a[2];
 47     outdata[3] = a[3];
 48     outdata[4] = a[4];
 49     count = 1;
 50     SBUF = outdata[0];
 51 }
 52 
 53 void Delay(U16 j)
 54 {
 55     U8 i;
 56     for(; j > 0; j--)
 57     {
 58         for(i = 0; i < 27; i++);
 59 
 60     }
 61 }
 62 void  Delay_10us(void)
 63 {
 64     U8 i;
 65     i--;
 66     i--;
 67     i--;
 68     i--;
 69     i--;
 70     i--;
 71 }
 72 
 73 void  COM(void)
 74 {
 75 
 76     U8 i;
 77 
 78     for(i = 0; i < 8; i++)
 79     {
 80 
 81         U8FLAG = 2;
 82         while((!P2_0) && U8FLAG++);
 83         Delay_10us();
 84         Delay_10us();
 85         Delay_10us();
 86         U8temp = 0;
 87         if(P2_0)U8temp = 1;
 88         U8FLAG = 2;
 89         while((P2_0) && U8FLAG++);
 90         //超時則跳出for循環
 91         if(U8FLAG == 1)break;
 92         //判斷數據位是0還是1
 93 
 94         // 如果高電平高過預定0高電平值則數據位為 1
 95 
 96         U8comdata <<= 1;
 97         U8comdata |= U8temp;      //0
 98     }//rof
 99 
100 }
101 
102 //--------------------------------
103 //-----濕度讀取子程序 ------------
104 //--------------------------------
105 //----以下變量均為全局變量--------
106 //----溫度高8位== U8T_data_H------
107 //----溫度低8位== U8T_data_L------
108 //----濕度高8位== U8RH_data_H-----
109 //----濕度低8位== U8RH_data_L-----
110 //----校驗 8位 == U8checkdata-----
111 //----調用相關子程序如下----------
112 //---- Delay();, Delay_10us();,COM();
113 //--------------------------------
114 void RH(void)
115 {
116 //主機拉低18ms                    //1、啟動 117 P2_0 = 0; 118 Delay(180); 119 P2_0 = 1; 120     //總線由上拉電阻拉高 主機延時20us
121     Delay_10us();
122     Delay_10us();
123     Delay_10us();
124     Delay_10us();
125     //主機設為輸入 判斷從機響應信號
126     P2_0 = 1;
127     //判斷從機是否有低電平響應信號 如不響應則跳出,響應則向下運行
128     if(!P2_0)         //T !
129     {
130 U8FLAG = 2;                  //2、等待 131 //判斷從機是否發出 80us 的低電平響應信號是否結束 132 while((!P2_0) && U8FLAG++); 133 U8FLAG = 2; 134 //判斷從機是否發出 80us 的高電平,如發出則進入數據接收狀態 135 while((P2_0) && U8FLAG++); 136 //數據接收狀態                 //3、接收數據 137 COM(); 138 U8RH_data_H_temp = U8comdata; 139 COM(); 140 U8RH_data_L_temp = U8comdata; 141 COM(); 142 U8T_data_H_temp = U8comdata; 143 COM(); 144 U8T_data_L_temp = U8comdata; 145 COM(); 146 U8checkdata_temp = U8comdata; 147 P2_0 = 1;                   //4、停止 148         //數據校驗
149 
150         U8temp = (U8T_data_H_temp + U8T_data_L_temp + U8RH_data_H_temp + U8RH_data_L_temp);
151         if(U8temp == U8checkdata_temp)
152         {
153             U8RH_data_H = U8RH_data_H_temp;
154             U8RH_data_L = U8RH_data_L_temp;
155             U8T_data_H = U8T_data_H_temp;
156             U8T_data_L = U8T_data_L_temp;
157             U8checkdata = U8checkdata_temp;
158         }//fi
159     }//fi
160 
161 }
162 
163 //----------------------------------------------
164 //main()功能描述:  AT89C51  11.0592MHz     串口發
165 //送溫濕度數據,波特率 9600
166 //----------------------------------------------
167 void main()
168 {
169     U8  i, j;
170 
171     //uchar str[6]={"RS232"};
172     /* 系統初始化 */
173     TMOD = 0x20;      //定時器T1使用工作方式2
174     TH1 = 253;        // 設置初值
175     TL1 = 253;
176     TR1 = 1;          // 開始計時
177     SCON = 0x50;      //工作方式1,波特率9600bps,允許接收
178     ES = 1;
179     EA = 1;           // 打開所以中斷
180     TI = 0;
181     RI = 0;
182     SendData(str) ;   //發送到串口
183     Delay(1);         //延時100US(12M晶振)
184     while(1)
185     {
186 
187         //------------------------
188         //調用溫濕度讀取子程序
189         RH();
190         //串口顯示程序
191         //--------------------------
192 
193         str[0] = U8RH_data_H;
194         str[1] = U8RH_data_L;
195         str[2] = U8T_data_H;
196         str[3] = U8T_data_L;
197         str[4] = U8checkdata;
198         SendData(str) ;  //發送到串口
199         //讀取模塊數據周期不易小於 2S
200         Delay(20000);
201     }//elihw
202 
203 }// main
204 
205 void RSINTR() interrupt 4 using 2
206 {
207     U8 InPut3;
208     if(TI == 1) //發送中斷
209     {
210         TI = 0;
211         if(count != 5) //發送完5位數據
212         {
213             SBUF = outdata[count];
214             count++;
215         }
216     }
217 
218     if(RI == 1)     //接收中斷
219     {
220         InPut3 = SBUF;
221         indata[count_r] = InPut3;
222         count_r++;
223         RI = 0;
224         if (count_r == 5) //接收完4位數據
225         {
226             //數據接收完畢處理。
227             count_r = 0;
228             str[0] = indata[0];
229             str[1] = indata[1];
230             str[2] = indata[2];
231             str[3] = indata[3];
232             str[4] = indata[4];
233             P0 = 0;
234         }
235     }
236 }

 

這里具有平台特性的是:

① 延時函數

  ——>  針對CC2541的解決方案適用

② 與CC2541的引腳的輸出和輸入特性需要配置不同,AT89C52的引腳不用配置,直接具有輸入輸出屬性,因此不必切換

  ——>  有的平台要切換,有的平台不用切換,因此需要用相應的宏判斷條件來搞了

③ 不同平台引腳定義方式不同,同一平台因為"X-積木"組合方式不同所以每個引腳都可能成為該引腳(不能僅僅用宏定義寫死)

  ——>  采用既定解決方案(同上)

④ 不同平台數據類型不同

  ——>  完全自定義一套數據類型,通過宏配置到不同平台(該宏一定不要和某些平台上的語言片段重合,一定要特殊並且能表達一定意思)

 

 STM32平台上DHT11驅動底層C語言封裝:

  1 /**
  2 **文件名稱:DHT11.c
  3 **文件說明:文件為溫濕度傳感器DHT11的驅動程序
  4 **/
  5 #include "../drive/drive.h"
  6 
  7 void GPIO_DHT_Out_Mode(void)
  8 {
  9     GPIO_InitTypeDef GPIO_InitStructure;
 10 
 11     GPIO_InitStructure.GPIO_Pin = DHT11_PORT;
 12     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 13     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //開漏輸出
 14     GPIO_Init(GPIOA, &GPIO_InitStructure);
 15 }
 16 void GPIO_DHT_Input_Mode(void)
 17 {
 18     GPIO_InitTypeDef GPIO_InitStructure;
 19 
 20     GPIO_InitStructure.GPIO_Pin = DHT11_PORT;
 21     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 22     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入
 23     GPIO_Init(GPIOA, &GPIO_InitStructure);
 24 }
 25 //----------------------------------------------------------------------------------------------
 26 //--- name    :    DHT11WriteStart
 27 //--- 功能    :    向DHT11寫入一個讀取數據的引導碼
 28 //----------------------------------------------------------------------------------------------
 29 void DHT11WriteStart()
 30 {
 31     GPIO_DHT_Out_Mode();
 32     P10 = 1;
 33     P10 = 0;
 34     Delay_ms(25);//拉低電平至少18ms
 35     P10 = 1;
 36     Delay_us(30);
 37 }
 38 //----------------------------------------------------------------------------------------------
 39 //--- name        :    DHT11ReadByte
 40 //--- 功能        :    從DHT11中讀取到一個字節
 41 //--- 返回值    :    讀取到一個字節的數據
 42 //----------------------------------------------------------------------------------------------
 43 u8 DHT11ReadByte(void)
 44 {
 45     u8 temp = 0, i, j = 0;
 46     for(i = 0; i < 8; i++)
 47     {
 48         temp <<= 1;
 49         while(0 == DHT11);//等待變高電平
 50         while(1 == DHT11)//計算高電平時長
 51         {
 52             Delay_us(1);
 53             j++;
 54         }
 55         if(j >= 30)      //超過30us確認為1
 56         {
 57             temp = temp | 0x01;
 58             j = 0;
 59         }
 60         j = 0;
 61     }
 62     return temp;
 63 }
 64 //----------------------------------------------------------------------------------------------
 65 //--- name        :    DHT11Read(u8 *RH_temp,u8 *RL_temp,u8 *TH_temp,u8 *TL_temp,u8 *CK_temp)
 66 //--- 功能        :    從DHT11中讀取數據
 67 //--- 說明        :    測試過程中發現溫度數值不變,小數值都是零,此模塊未測試成功!
 68 //----------------------------------------------------------------------------------------------
 69 void DHT11Read(u8 *RH_temp, u8 *RL_temp, u8 *TH_temp, u8 *TL_temp, u8 *CK_temp)
 70 {
 71     //uchar TH_temp,TL_temp,RH_temp,RL_temp,CK_temp;
 72     //uchar TL_temp,RL_temp,CK_temp;
 73     //u8 untemp;
 74     while(1)
 75     {
 76 DHT11WriteStart();//給讀取前導信號            //1、啟動
 77         GPIO_DHT_Input_Mode();//設置端口為輸入狀態
 78         if(!DHT11)
 79         {
 80 while(0 == DHT11);//低電平的響應信號,80us     //2、等待  81 while(1 == DHT11);//緊接着是80us的高電平數據准備信號  82             //             *CK_temp = DHT11ReadByte();
 83             //             *TL_temp = DHT11ReadByte();
 84             //             *TH_temp = DHT11ReadByte();
 85             //             *RL_temp = DHT11ReadByte();
 86             //             *RH_temp = DHT11ReadByte();
 87 
 88 *RH_temp = DHT11ReadByte();//濕度高8位       //3、讀取數據 89 *RL_temp = DHT11ReadByte();//濕度低8位 90 *TH_temp = DHT11ReadByte();//溫度高8位 91 *TL_temp = DHT11ReadByte();//溫度低8位 92 *CK_temp = DHT11ReadByte();//校驗和
 93             GPIO_DHT_Out_Mode();
 94 P10 = 1;                        //4、END  95             //數據校驗
 96             //untemp= *RH_temp+RL_temp+*TH_temp+TL_temp;
 97             return;
 98         }
 99         DriveDelay(0x3ff);
100     }
101 }
102 /*********************************************************************************************
103 ***    代碼段    :    DHT11__DEBUG
104 ***    說明      :    該代碼段是用於測試溫濕度傳感器DHT11所用,正常情況下不加入編譯。
105 ***                如果用戶要測試該模塊,可以將#undef改為#define(在文件頭處)
106 *********************************************************************************************/
107 #undef    DHT11__DEBUG
108 
109 #define    DHT11__DEBUG
110 
111 #ifdef    DHT11__DEBUG
112 
113 int main(void)
114 {
115     u8 TH_temp, TL_temp, RH_temp, RL_temp, CK_temp;
116     char DisBuf[20];
117 #ifdef DEBUG
118     debug();
119 #endif
120     SysInit();
121     //     SysTickInit();
122     InitLcd();
123     LcdDisText(0x80, "hello world!!!");
124 
125     while(1)
126     {
127         DHT11Read(&RH_temp, &RL_temp, &TH_temp, &TL_temp, &CK_temp);
128         sprintf(DisBuf, "%d-%d-%d-%d-%d", RH_temp, RL_temp, TH_temp, TL_temp, CK_temp);
129         //         Delay_ms(500);
130         LcdDisText(0x80 + 0x40, (u8 *)DisBuf);
131         LcdDisText(0x80 + 0x40 + 15, "h");
132     }
133 }
134 #endif

 

平台特性補充說明:

  ① STM32和CC2541類似引腳有輸入輸出兩種模式,因此采用函數GPIO_DHT_Out_Mode和函數GPIO_DHT_Input_Mode來切換不同模式

  ② 雖然STM32是32位單片機,但是其單總線數據傳輸時讀一個字節的數據還是和52、CC2541類似,並沒有特殊情況

 

小結&接下來計划:

  從上面DHT11在CC2541、AT89C52和STM32三種不同平台上的實現可以看出:所有驅動程序萬變不離其宗,部分變化只是在系統上微調。而如果想封裝一個和平台盡量無關的DHT11底層驅動函數,就需要充分發揮宏定義的作用,將所有平台特性元素全部采用宏定義,並抽出平台共性模型建立底層驅動函數。因此,明天同一時間、同一地點我將詳細介紹C語言中宏定義的知識,並最終封裝成我們想要的平台無關的溫濕度傳感器底的層驅動文件。

 

 

鏈接: http://pan.baidu.com/s/1dDlUyyd

 

[三個關鍵文件鏈接]

CC2541:http://pan.baidu.com/s/1o6vf2Fw 

AT89C52: http://pan.baidu.com/s/1gdrpYmB

STM32:http://pan.baidu.com/s/1bnz5Czt

 

 

@beautifulzzzz

  2015-9-9 持續更新中~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM