如何为编程爱好者设计一款好玩的智能硬件(五)——初尝试·把温湿度给收集了(中)!

 

一、我的构想:如何为编程爱好者设计一款好玩的智能硬件(一)——即插即用、积木化、功能重组的智能硬件模块构想

二、别人家的孩子:如何为编程爱好者设计一款好玩的智能硬件(二)——别人是如何设计硬件积木的!

三、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 持续更新中~

posted @ 2015-09-09 23:25  beautifulzzzz  阅读(5133)  评论(18编辑  收藏  举报