zstack协议栈内驱动DHT11温湿度传感器的学习心得

准备

  • 实验设备:华清远见的zigbee模块,DHT11模块
  • 程序架构:zstack协议栈
  • 所用语言:c语言+部分汇编

实验前须知:

DHT11温湿度传感器是通过一根数据线传输的,那么所用的协议当然是单总线协议,这时,我们需要拿到它的规格协议书,获取规格书的方式很多,这里介绍两种获取可能找得到:

  1. 淘宝商店下可能获取得到,这个方式可能没用,但是有时候又挺有用的,因为很多商家卖传感器或其他板子设备常常提供规格书,甚至驱动程序。
  2. 上网搜索“立创商城”,找到官网,可以搜索得到,并能找到相应的规格书。
  3. 上网搜索“1688”,这是阿里巴巴的批发网。

个人认为这三种方式混着用,因为有时第1种没找到,可以试一下第2种,如果第2种也没有也可以试一下第3种。本实验用的方式是第2种,如下图显示是搜索得到的,点击数据手册即可看它的规格特性和相关驱动时序。

 

分析

接分析下来分析一下如何通过时序图来编写协议时序驱动:

首先打开它的规格书,阅读它的相关特性,当然有些东西只是为了看它能够应用于哪块领域等,这些就不多解释了。我们直接看它的重点部分,如下图,可以分析有一个供电脚和一个地脚,还有一个空脚(代表什么都不接,可以外漏),还有一个数据线。

向下拉文档可以看到一个简单例子的电路接线图,如下图所示,可以发现DHT11接线相当简单。

这里本人想说,并不是我截出来的图就只需看这部分,更希望的是,那些带有协议时序的传感器应该仔细阅读每一部分。不然驱动程序时,因自己疏忽而导致一些自己觉得无法解决的bug。

下图我截出部分串行通讯说明,可以知道mcu每次读将获取到40位数据,并高位先出。数据格式下图中有说明。这里如果学者理解透彻建议把这个规格书下载下来阅读。

当我们理解了计算温湿度时,我们就需要分析时序图了。上图是画出的重点,这将影响个人是否能够读懂主机和从机的通讯关系及延时问题。

当看了上面的表和下面的书序图后,可以归纳为:

先告知“主机”为我们的主控芯片,这里我们指的是zigbee的芯片CC2530,“从机”就是这个温湿度传感器DHT11。

当主机和从机连接后,默认数据线是被拉高的,只要两端哪个口是低电平,则整根线将是低电平,他们的关系呈现的是“线与”的关系。如下图所示:

规格书的操作步骤写得很清晰,如下图:

这里第四步骤也许会有人有疑惑,“0”的时候不该都是低电平吗,“1”的时候不该都是高电平吗,但是上图怎么会这样设置,你没有看错,它就是这么设置的。可要如何实现呢,本人设计思路是这样的:先由前面步骤知道,当主机拉低电平之后,从机相应数据时,先拉低83us再拉高87us,之后从机就开始发送40位数据了,伪代码设计如下:

 

//开始IO默认是1
IO = 0;//主机拉低信号

delay(20ms);//拉低时间延时大于18ms

IO = 1;//延时之后将要释放该数据线,即把该位拉高。

delay(40us);//因为主机拉高后,从机还需要响应大概10-35us的时间,之后将拥有83us拉低时间,因此,这个
            //延时宽度可以是40-100以内都没问题,当然我没计算那么精细,只要延时到这个时间就好了。

if (IO == 0)//此时,这个判断时间是从机拉低1-83us的时间段内

{
    while(IO == 0);//等待拉低结束,83us后
    while(IO == 0);//等待拉高结束,87us后
    。。。。//开始读取40位数据

}

接下来如何读取40位数据了,那我们仔细看0数据和1数据的区别,发现他们只有高电平时候延时时间不同,1数据的高电平延时时长比较长,而0数据高电平延时时长比较短,通过这一特性就可以区分读取了,如下图:

接下来开始伪代码编程:

//开始读之前,我们知道此时,主从机的时间段还正在低电平阶段,就是在54us的延时阶段。
//因此代码可以如下设计,先等待它到高电平
for(n=0;n<5;n++)
for(i=0;i<8;i++)
{
    while(IO == 0);
    delay(40us);//延时超过23-27us的话,如果是0数据,这个电平将为低电平,如果是1数据

            //这个电平将为高电平,前提是不能延时太久,最高延时到68-74us,但是为了得到稳定的值
           //取较为中间的时间段来读。
    data <<= 1
    if(IO == 1)
    {
        data |= 0x01;
        while(IO == 1);//等待高电平结束再重新去取下一位。
    }
    
    else
        data &= ~0x01;//此时已经是低电平了,可以直接读取下一位了。
}

好了,看到这里,将可以把cc2530的驱动代码带出了,这里可能直接套用需要改一下定义或声明的,因为本人复制并不全。

sensor.c程序:

#include "sensor.h"
#include <ioCC2530.h>
#include "OnBoard.h"

//#define DHT11_DATA P1_2
#define DHT11_DATA P0_4
//温湿度定义
uchar ucharFLAG,uchartemp;
uchar shidu_shi,shidu_ge,wendu_shi,wendu_ge=4;
uchar ucharT_data_H,ucharT_data_L,ucharRH_data_H,ucharRH_data_L,ucharcheckdata;
uchar ucharT_data_H_temp,ucharT_data_L_temp,ucharRH_data_H_temp,ucharRH_data_L_temp,ucharcheckdata_temp;
uchar ucharcomdata;



void Delay_10us(void)//出入栈空间需要大概2us左右,
{
  Delay_8us();
}
void Delay_1ms(void)
{
  uint i=86;
  while(i--)
  {
    Delay_10us();
  }
  Delay_1us();
}

void Delay_ms(uint Time)
{
  unsigned char i;
  while(Time--)
  {
    Delay_1ms();
  }
  
}


void fangDHT(void)
{
  P0DIR |= (1<<4);//io口设置为输出
  P0INP|= (1<<4);//设置为三态,这个必须要加,不加将读写不正常。手册只写对输入有效,但其实对输出也有效
  DHT11_DATA = 0;
  Delay_ms(22);
  DHT11_DATA = 1;
  //P0DIR &= ~(1<<4);
  //P0INP |= (1<<4);
  Delay_8us();
  Delay_8us();
  Delay_8us();
  if(DHT11_DATA == 0)
  {
    while(DHT11_DATA == 0);
    while(DHT11_DATA == 1);
    ucharRH_data_H = COM8bit();
    ucharRH_data_L = COM8bit();
    ucharT_data_H = COM8bit();
    ucharT_data_L = COM8bit();
    ucharcheckdata = COM8bit();
    if(ucharcheckdata == (ucharT_data_H + ucharT_data_L + ucharRH_data_H + ucharRH_data_L))
    {
      wendu_shi = ucharT_data_H/10;
      wendu_ge = ucharT_data_H%10;
      shidu_shi = ucharRH_data_H/10;
      shidu_ge = ucharRH_data_H%10;
    } 
  }
  else //没用成功读取,返回0
  {
       wendu_shi=0; 
       wendu_ge=0;
       shidu_shi=0; 
       shidu_ge=0;  
       HalUARTWrite(0,"no1\n",4);
  } 
  
  
}
     
uint8 COM8bit(void)
{
  uint8 i=0;
  uint8 data = 0;
  for(i=0;i<8;i++)
  {
    while(DHT11_DATA == 0);
    Delay_8us();
    Delay_8us();
    Delay_8us();
    Delay_8us();
    Delay_8us();
    Delay_8us();
    Delay_8us();
    Delay_8us();
    data <<= 0x01;
    if(DHT11_DATA == 1)
    {
      data |= 0x01;
      while(DHT11_DATA == 1);
    }
    else
    {
      data &= ~(0x01);
    }   
  }
  return data;
  
}

 sensor.h程序:

#ifndef __SENSOR_H__
#define __SENSOR_H__
#define uint unsigned int
#define uchar unsigned char

#define Delay_1us() {asm("MOV R1,#0AH");asm("DJNZ R1,$");} //经过测试预估的延迟,精度为1
#define Delay_4us() {asm("MOV R2,#28H");asm("DJNZ R2,$");}
#define Delay_8us() {asm("MOV R2,#50H");asm("DJNZ R2,$");}
//void Delay_us(void);

void Delay_10us(void);
void Delay_1ms(void);
void Delay_ms(uint Time);


extern void fangDHT(void);

extern uchar shidu_shi,shidu_ge,wendu_shi,wendu_ge;
#endif

 首先,该延时函数默认是配合cc2530的32M晶振同时默认配置的zstack分频系数,具体分频不清楚,本人偷懒了,直接通过的示波器估计出延时函数,不过中间过程需要时间才能把延时函数写出来。如果到了其他平台驱动,可能就不准了。这里要注意的是,测试延时函数应该在任务事件上测试,而不是main函数开头测试,因为main函数开头还未设置分频,建议在任务事件上或者在最后一个任务初始化函数即SampleApp下的函数测试延时函数。还有如果是设计微妙延时建议不要使用Delay_10us,使用Delay_8us的函数更精确有效哟。下面是本人测试这个驱动的任务事件

实体图如下:

串口测试如下:因为前面是没接上温湿度传感器,所以读出是00,00同时报出no1,接上之后就正常了。

 

这里特别强调,单总线同I2C一样,都需要较精确的延时时序,本人测试了该zstack协议栈的延时函数,发现误差相当大,建议不要使用,因此本人才自己自定义延时函数,测试下来挺精确的基本误差能够0.02左右,也就是延时时间越大,误差最多在3-4微妙左右,你们可以自行测试。编写延时函数测试方式一般就用一个io口做一个高电平和低电平转换,中间加延时函数,通过示波器查看周期即可估值了。个人推荐汇编指令延时比较精确,所以尽量用汇编而不用函数,函数出入栈的延时是很难把握的。

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值