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

 

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

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

三、MCU选型:如何为编程爱好者设计一款好玩的智能硬件(三)——该选什么样的MCU呢?

四、温湿度传感器DHT11驱动封装(上):如何为编程爱好者设计一款好玩的智能硬件(四)——初尝试·把温湿度给收集了(上)!

五、温湿度传感器DHT11驱动封装(中):如何为编程爱好者设计一款好玩的智能硬件(五)——初尝试·把温湿度给收集了(中)!

 

六、温湿度传感器DHT11驱动封装(下):

  

  这几天该进程稍微中断,一方面是因为帮助一个好朋友处理保研的事情,另一方面是由于我在思考每天一篇是否会因为过于频繁而缺少干货。于是我决定每个月的模3余0的日子总结一下~特此通告!在四、五两篇中我们详细分析了DHT11的驱动流程,由于我们需要一个便于移植的版本,所以上次挖了个坑说会采用宏定义来尽量方便移植。因此,本节将首先讨论宏定义相关知识,而后封装一个稍微改进的DHT11驱动。

 

C Macros

 
理解宏:
 
  宏是一种预处理指令,它提供了一种机制,可以用来替换源代码中的字符串,宏是用“#define"语句定义的,下面是一个宏定义的例子:
    #define VERSION—STAMP "1.02"
  上例中所定义的这种形式的宏通常被称为标识符。在上例中,标识符VERSION_STAMP即代表字符串"1.02"——在编译预处理时,源代码中的每个VERSION_STAMP标识符都将被字符串“1.02”替换掉。

  以下是另一个宏定义的例子:
      #define CUBE(x)  ((x)*(x)*(x))
  上例中定义了一个名为CUBE的宏,它有一个参数x。CUBE宏有自己的宏体,即((x)*(x)*(x))——在编译预处理时,源代码中的每个CUBE(x)宏都将被((x)*(x)*(x))替换掉。

使用宏有以下几点好处:

     (1)在输入源代码时,可省去许多键入操作。   
    (2)因为宏只需定义一次,但可以多次使用,所以使用宏能增强程序的易读性和可靠性。
    (3)使用宏不需要额外的开销,因为宏所代表的代码只在宏出现的地方展开,因此不会引起程序中的跳转。
    (4)宏的参数对类型不敏感,因此你不必考虑将何种数据类型传递给宏。
 
容易出错的地方:

  需要注意的是,在宏名和括起参数的括号之间绝对不能有空格。此外,为了避免在翻译宏时产生歧义,宏体也应该用括号括起来。

你可能不知道的:

  宏也可使用一些特殊的运算符,例如字符串化运算符“#”和。连接运算符“##”。“#”运算符能将宏的参数转换为带双引号的字符串,请看下例:
      define DEBUG_VALUE(v)  printf(#v"is equal to %d.\n",v)
  你可以在程序中用DEBUG_VALUE宏检查变量的值,请看下例:
      int x=20;
      DEBUG_VALUE(x);
  上述语句将在屏幕上打印"x is equal to 20"。这个例子说明,宏所使用的“#”运算符是一种非常方便的调试工具。
  “##”运算符的作用是将两个独立的字符串连接成一个字符串。
 
这篇文章对宏的种类进行简单的介绍:http://blog.csdn.net/ocean181/article/details/6818687
这篇文章单独读宏里面的#进行介绍:http://blog.sina.com.cn/s/blog_550405a10100bqar.html
这篇文章讲了宏中的几个高级的用法:http://www.cnblogs.com/Anker/p/3418792.html (do while)
 
 
DHT11驱动封装 
 
DHT11.H
 1 #ifndef __DHT11_H__
 2 #define __DHT11_H__
 3 
 4 #include "delay.h"
 5 
 6 //some typedef need to do
 7 typedef unsigned char    t_uc;
 8 typedef unsigned short     t_us;
 9 typedef unsigned int      t_ui;
10 typedef char        t_c;
11 typedef short         t_s;
12 typedef int          t_i;
13 
14 #define DHT11_DELAY_US(x)     Delay_us((x))
15 #define DHT11_DELAY_MS(x)     Delay_ms((x))
16 #define DHT11_DATA_PIN        P0_0
17 #define DHT11_DATA_PIN_READ   DHT11_DATA_PIN          //read the data_pin's data function 
18 #define DHT11_DATA_PIN_SET    DHT11_DATA_PIN=1        
19 #define DHT11_DATA_PIN_CLEAR  DHT11_DATA_PIN=0
20 #define DHT11_DATA_PIN_IN     P0DIR&=~0x01
21 #define DHT11_DATA_PIN_OUT    P0DIR|=0x01
22 
23 
24 extern    t_i DHT11_GetData(t_us *humidity, t_us *temperature);
25 
26 
27 #endif

 

移植的时候只要对.H进行修改就行啦! 

  上面是封装的DHT11的驱动.H文件,可见为了达到方便移植到各个平台的目的,我们在红色部分重定义了数据类型,并且在绿色部分将几个外部依赖全部定义成宏。这样移植到不同平台的时候只需要根据特定的平台,稍微调动下红色部分重定义的数据类型,并且根据宏定义部分相应的功能进行封装和修改(这样.C文件是不需要动的,是不是很方便!)

  这里对部分宏定义进行说明:

  ① DHT11_DATA_PIN 是DHT11通信数据线连在单片机上的引脚  
  ② DHT11_DATA_PIN_READ是读取DHT11_DATA_PIN引脚上的数据函数,返回为该引脚的状态(高电平为1,低电平为0)
  ③ DHT11_DATA_PIN_SET和DHT11_DATA_PIN_CLEAR功能分别是置DHT11_DATA_PIN引脚为高电平和低电平功能函数
  ④ DHT11_DATA_PIN_IN和DHT11_DATA_PIN_OUT功能分别是设置DHT11_DATA_PIN引脚为输入和输出模式

    PS:

  这里第4行引入的外部文件提供delay延时相关函数,同样的第14、15行宏定义也将所要用到的延时微秒和毫秒的函数进行宏定义,总的来说就是方便移植~

 

DHT11.C

 1 #include "DHT11.H"
 2 
 3 
 4 t_uc COM(void)//read 8bits data from com
 5 {
 6     t_uc flag, temp; //flag and temp
 7     t_uc comdata;//the 8bits data read from com
 8     t_uc i;
 9     for(i = 0; i < 8; i++)
10     {
11         flag = 2;
12         while((!DHT11_DATA_PIN_READ) && flag++);
13         DHT11_DELAY_US(30);
14         temp = 0;
15         if(DHT11_DATA_PIN_READ)temp = 1;
16         flag = 2;
17         while((DHT11_DATA_PIN_READ) && flag++);
18         if(flag == 1)break;
19         comdata <<= 1;
20         comdata |= temp;
21     }
22     return comdata;
23 }
24 
25 t_i DHT11_GetData(t_us *humidity, t_us *temperature)//get the hunidity and temperature data
26 {
27     t_uc flag, temp; //flag and temp
28     t_uc ucharT_data_H_temp, ucharT_data_L_temp,
29          ucharRH_data_H_temp, ucharRH_data_L_temp,
30          ucharcheckdata_temp;
31 
32     //1、start
33     DHT11_DATA_PIN_OUT; //set the data pin as out mode
34     DHT11_DATA_PIN_CLEAR;
35     DHT11_DELAY_MS(19);  //>18MS
36     DHT11_DATA_PIN_SET;
37     DHT11_DATA_PIN_IN; //set the data pin as input mode
38     DHT11_DELAY_US(40);
39     
40     if(!DHT11_DATA_PIN_READ)
41     {
42         //2、wait
43         flag = 2;
44         while((!DHT11_DATA_PIN_READ) && flag++);
45         flag = 2;
46         while((DHT11_DATA_PIN_READ) && flag++);
47 
48         //3、read data
49         ucharRH_data_H_temp = COM();
50         ucharRH_data_L_temp = COM();
51         ucharT_data_H_temp = COM();
52         ucharT_data_L_temp = COM();
53         ucharcheckdata_temp = COM();
54 
55         //4、stop
56         DHT11_DATA_PIN_SET;
57         
58         temp = (ucharT_data_H_temp + ucharT_data_L_temp + ucharRH_data_H_temp + ucharRH_data_L_temp);
59         if(temp == ucharcheckdata_temp) //check the data ,then updata humidity and temperature
60         {
61             *humidity = ucharRH_data_H_temp;
62             *temperature = ucharT_data_H_temp;
63         }
64     }
65     else //fail return -1
66     {
67         return -1;
68     }
69     
70     return 0;
71 }

 .C文件是不用改的~和前两节讲解的相比,这里基本没变,只是将部分函数换成了宏

 

得说明的:

  前两节讲过从DHT11中读出来的40bits数据包括温湿度整数和小数部分各8bit表示+一个8bit的校验,上面.C文件第61、62行仅将整数部分提取

 

小结&接下来计划:

  至此,我们终于把DHT11给封装好了,接下来不会那么频繁更新(毕竟太快没干货!)。下次我将首先展示该驱动在CC2541(上面的便是基于它的)、STM32、51平台上的移植方法,然后开启一个新的驱动封装——显示器。至于要选择哪一款,暂时还没定,也先留个悬念吧~

 

 

链接

基于CC2541平台的实验工程

>只有DHT11.C和DHT11.H经过整理,其它可能有点乱,见谅<

http://pan.baidu.com/s/1hq6N168 

 

DHT11.C和DHT11.H文件

http://pan.baidu.com/s/1dD0ibDJ

http://pan.baidu.com/s/1qeHBK

 

 

  

@beautifulzzzz

  2015-9-17 持续更新中~

 
 
 
posted @ 2015-09-17 20:56  beautifulzzzz  阅读(1763)  评论(3编辑  收藏  举报