单总线协议简介
1-wire 单总线是Maxim 全资子公司Dallas 的一项专有技术。与目前多数标准串行数据通信方式,如SPI/I2C/MICROWIRE 不同,它采用单根信号线,既传输时钟,又传输数据而且数据传输是双向的。它具有节省I/O 口线资源、结构简单、成本低廉、便于总线扩展和维护等诸多优点。1
单总线的数据传输速率一般为16.3Kbit/s,最大可达142 Kbit/s,通常情况下采用100Kbit/s以下的速率传输数据
1-Wire线端口为漏极开路构或三态门的端口,因此一般需要加上拉电阻Rp,通常选用5K~10KΩ。
有关单总线的详细介绍参考此文
参加蓝桥杯或者通单片机使用DS18B20的同学肯定是比较熟悉这个协议的,在获取温度的过程中使用的步骤是先初始化,然后写命令;然后再初始化,读命令获取温度。整个单总线的通信步骤:初始化1-wire器件、识别1-wire器件和交换数据。
由于它们是主从结构,只有主机呼叫从机时,从机才能应答,因此主机访问1-wire器件都必须严格遵循单总线命令序列,即1.初始化、2.ROM、3.命令功能命令。

今天使用的DHT11他只是采用单总线数据格式,具体的命令序列与DS18B20这些严格遵循初始化、ROM、命令功能命令三部曲的器件有所不同。DHT11只是采用单总线的通信步骤:初始化1-wire器件、识别1-wire器件和交换数据。

树莓派4B+DHT11(1-Wire协议)
DHT11简介
接口定义

数据格式
DATA脚 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分,具体格式在下面说明:
8bit湿度整数数据+8bit湿度小数数据
+8bi温度整数数据+8bit温度小数数据
+8bit校验和

数据传送正确时校验和数据等于“ 8bit 湿度整数数据 +8bit 湿度小数数据
+8bi 温度整数数据 +8bit 温度小数数据 ”所得结果的末8位。

通信流程
1.总线初始化;(拉低总线时长不小于18ms,然后释放,总线回到高电平)
2.DHT11应答;(DHT11应答将总线拉低)
3.输出数据帧(一次完整的数据传输为40bit,高位先出。)
数据帧格式:
通讯时序图:



硬件连接


开启树莓派1-Wire接口
此步骤和之前SPI、I2C操作一样,不再贴图,为了保险起见,建议开启口重启一下树莓派 sudo reboot。

编程实现
由于DHT11的读取相对简单,就是按照时序控制IO口的输入输出即可。
代码来自此文
笔者只是简单的根据时序图添加了注释
打开Geany,输入以下代码:

// An highlighted block
//
//mydht11.c
//
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
 
typedef unsigned char uint8;
typedef unsigned int  uint16;
typedef unsigned long uint32;
 
#define HIGH_TIME 32
 
int pinNumber =7;  //use gpio1 to read data(wPi编码)
uint32 databuf;
 
 
 //读取传感器的值
uint8 readSensorData(void)
{
    uint8 crc; 
    uint8 i;

    pinMode(pinNumber,OUTPUT); // 设置引脚为输出模式(用来控制总线电平)
    digitalWrite(pinNumber, 0); // 拉低总线(开始信号) 
    delay(25);//(起始信号要求为不少于18ms的低电平,这里作者使用了25ms)
    digitalWrite(pinNumber, 1); // 主机释放总线(总线恢复高电平)
    pinMode(pinNumber, INPUT); // 将引脚设置为输入模式,方便检测从机的应答信号
    pullUpDnControl(pinNumber,PUD_UP);//设置为上拉输入。
 
    delayMicroseconds(27);
    if(digitalRead(pinNumber)==0) //检测总线有没有被从机拉低,如果拉低就说明从机应答了。
       {
         while(!digitalRead(pinNumber)); //等待从机数据传输的标志(拉高电平)
 
	  for(i=0;i<32;i++)//读取32为温湿度数据
	   {
	   while(digitalRead(pinNumber)); //data clock start
	   while(!digitalRead(pinNumber)); //data start
          delayMicroseconds(HIGH_TIME);
          databuf*=2;//进位相当于位运算中的左移一位。
           if(digitalRead(pinNumber)==1) //1
 	       {
                databuf++;
 	       }
	    }
 
	  for(i=0;i<8;i++)//采集八位校验位数据
	   {
	   while(digitalRead(pinNumber)); //data clock start
	   while(!digitalRead(pinNumber)); //data start
          delayMicroseconds(HIGH_TIME);
          crc*=2;  
          if(digitalRead(pinNumber)==1) //1
	       {
                crc++;
	       }
	    }
	return 1;
       }
   else
        {
        return 0;
         }
}
 
int main (void)
{
 
  printf("Use GPIO1 to read data!\n");
 
  if (-1 == wiringPiSetup()) {
    printf("Setup wiringPi failed!");
    return 1;
  }
 
  pinMode(pinNumber, OUTPUT); // set mode to output
  digitalWrite(pinNumber, 1); // output a high level 
 
  printf("Enter OS-------\n");
  while(1) {
    pinMode(pinNumber,OUTPUT); // set mode to output
    digitalWrite(pinNumber, 1); // output a high level 
    delay(3000);
    if(readSensorData())
    {
       printf("Congratulations ! Sensor data read ok!\n");
       printf("RH:%d.%d\n",(databuf>>24)&0xff,(databuf>>16)&0xff); 
       printf("TMP:%d.%d\n",(databuf>>8)&0xff,databuf&0xff);
       databuf=0;
     }
    else
     {
        printf("Sorry! Sensor dosent ans!\n");
       databuf=0;
      }
  }
  return 0;
}

效果如下:

利用单总线与DS18B20通信获取温度
由于大佬们早已制作了DS18B20的读取文件,因此在利用DS18B20获取温度时,只需调用文件接口即可获得温度信息,也不需要想STM32和其他单片那样去对着时序图一点一点的初始化,写数据/指令,读数据了。c语言实现详细步骤参考此文;Python参考此文。

总结
有关单总线的使用就记录到此,有不妥之处欢迎批评指正。

树莓派4B入门学习笔记汇总
树莓派4B学习笔记——系统烧录及初次开机
树莓派4B学习笔记——点亮你的LED
树莓派4B学习笔记——IO输入检测
树莓派4B学习笔记——IO通信篇(I2C)
树莓派4B学习笔记——IO通信篇(SPI)
树莓派4B学习笔记——IO通信篇(1-Wire)
树莓派4B学习笔记——IO通信篇(UART)