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

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

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

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

  • 相关阅读:
    Linux 常用指令【持续更新】
    我的 MyBatis 实现的 Dao 层
    Mybatis 点点滴滴
    Redis 初识
    Mybatis 映射关系
    Mybatis 类属性和字段映射小小分析
    Mybatis 中 sql 语句的占位符 #{} 和 ${}
    使用 Maven 管理项目
    Maven 项目依赖 pom 文件模板
    Maven 私服安装和启动
  • 原文地址:https://www.cnblogs.com/zjutlitao/p/4796016.html
Copyright © 2011-2022 走看看