第三章——代码逻辑分析

1.第一步,定义获取高低电平时间的函数

在分析数据手册的时候我们会发现,获取高低电平的时间非常重要,因此我们可以先定义这两个函数getLowTime(),和getHighTime()

uint32_t getLowTime() {
  uint32_t lastTime = micros();
  while (!digitalRead(sensorPin)) {
    if (micros() - lastTime > TIMEOUT) {
      break;
    }
  }
  return (micros() - lastTime);
}
uint32_t getHighTime() {
  uint32_t lastTime = micros();
  while (digitalRead(sensorPin)) {
    if (micros() - lastTime > TIMEOUT) {
      break;
    }
  }
  return (micros() - lastTime);
}

micros() 的返回值是 unsigned long 也就是 uint32_t,单位是 us。

同时我们同时我们要保证程序不会因为硬件或者其他问题,使程序被长时间 block,因此我们加入TIMEOUT的判断,同时在后面我们还需要定义另外两个宏定义,这在后面会用到,即DHT11_OKAYDHT11_ERROR

#define TIMEOUT 1e5
#define DHT11_OKAY 0x00
#define DHT11_ERROR 0x01

2.第二步,MCU 和 DHT11 握手通信

首先由 MCU 发出开始信号,发送 18ms 的低电平,40us 的高电平。

/*MCU发送开始信号, 并等待应答*/
pinMode(sensorPin, OUTPUT);
//拉低18ms
digitalWrite(sensorPin, LOW);
delay(18);
//拉高40us
digitalWrite(sensorPin, HIGH);
delayMicroseconds(40);

然后 MCU 等待 DHT11 的应答,应答信号是 80us 的低电平和 80us 的高电平,这边我们不需要精确的时间,只需要等待高低电平过去。

/*接受DHT11做出的应答*/
pinMode(sensorPin, INPUT_PULLUP);
getLowTime();
getHighTime();

3. 第三步,接受来自 DHT11 的数据

接收 40 位的数据包,我们将其分 5 为个字节接收。

这里我们不需要知道低电平的时间,等待低电平过去就可以。但是需要知道高电平的时间,通过50us 的阈值来判断这一位是‘0’还是‘1’,因为‘0’的高电平是 26-28us,‘1’的高电平是 70us,因此我们大概取个中间值 50us。

因为 5 个字节都被初始化为‘0’,所以我们只需要移位和按位求和把‘1’加进去就好了。

/*开始接受40bit数据*/
uint8_t buffer[5] = {0};
for (uint8_t i = 0; i < 5; i++) {
  for (uint8_t j = 0; j < 8; j++) {
    getLowTime();
    if (getHighTime() > 50)
      buffer[i] |= (0x80 >> j);
  }
}

结束通信。

/*结束通信*/
getLowTime();

4. 第四步,检查校验和并更新数据

在更新数据前我们要保证数据的准确性,检查一下校验和。

/*检查校验和*/
if (buffer[0] + buffer[1] + buffer[2] + buffer[3] != buffer[4])
  return DHT11_ERROR;

当数据准确无误后再更新数据,温度的小数位的精度是 0.1。

/*更新数据*/
humidity = buffer[0];
temperature = buffer[2] + buffer[3] * 0.1;
return DHT11_OKAY;