51单片机-05-DHT11 温湿度传感器 | DS1302 实时时钟

一、DHT11 温湿度传感器

1.1 规格参数

|--------------|-----------------------------------------------|
| 参数 | 规格 |
| 温度范围 | 0°C ~ 50°C,精度 ±2°C,分辨率 1°C |
| 湿度范围 | 20%RH ~ 90%RH,精度 ±5%RH,分辨率 1%RH |
| 工作电压 | 3V ~ 5.5V |
| 接口类型 | GPIO 单总线(1根 DATA 线),高位先发(MSB First) |
| 数据格式 | 40 bit = 5 字节:湿度整数 + 湿度小数 + 温度整数 + 温度小数 + 校验和 |

|-----------------------|------------------------------------------------------------------------------------------------------------|
| 对比 vs DS18B20 | DS18B20:温度专测,-55~125°C,精度 ±0.5°C,12位分辨率(0.0625°C),高位先发 DHT11:温湿度双测,0~50°C,精度 ±2°C,1°C分辨率,高位先发(MSB First) |

1.2 数据格式(40 bit)

|-------------------|------------|----------------------------------------------|
| 字节 | 内容 | 示例 |
| byte[0] | 湿度整数部分 | 0x3C = 60(表示60%RH) |
| byte[1] | 湿度小数部分 | DHT11小数位恒为0x00 |
| byte[2] | 温度整数部分 | 0x1A = 26(表示26°C) |
| byte[3] | 温度小数部分 | DHT11小数位恒为0x00 |
| byte[4] | 校验和 | byte[0]+byte[1]+byte[2]+byte[3] 的低8位 |

1.3 DHT11 通信时序详解

① 主机发送起始信号

|-----------------------------------------------------|
| 起始信号(master_start) |
| ****1.****主机将 DATA 引脚拉低,持续至少 18ms(代码用 Delay_ms(20)) |
| ****2.****主机释放总线(拉高),等待 20~40us |
| ****3.****DHT11 检测到起始信号,准备响应 |

② DHT11 应答信号

|--------------------------------------------------|
| 应答信号(dht11_response) |
| ****1.****DHT11 将总线拉低约 80us(主机检测低电平,确认 DHT11 存在) |
| ****2.****DHT11 将总线拉高约 80us(主机检测高电平,准备接收数据) |
| ****3.****DHT11 再次将总线拉低,开始发送第1个bit |

③ DHT11 数据传输(高位先发)

|---------------|-----------------------|---------------|----------------------|
| 发送bit | DHT11动作 | 时间参数 | 判断方法 |
| 发送 0 | 拉低 50us + 拉高 26~28us | 高电平时间 < 50us | 延时60us后采样,若还是高=1,低=0 |
| 发送 1 | 拉低 50us + 拉高 60~70us | 高电平时间 > 50us | 同上,高电平持续更长 |

|------------------|-------------------------------------------------------------------------------------------|
| 关键 判断0或1 | 每bit先等低电平结束,然后延时60us,此时采样:若为高电平 → 1(高电平维持60~70us还未结束);若为低电平 → 0(高电平只持续26~28us,60us时已结束) |

1.4 宏定义(P2.0 接 DATA)

cs 复制代码
#define DHT11_PIN_HIGH  (P2 |= (1 << 0))       // 释放/拉高

#define DHT11_PIN_DOWN  (P2 &= ~(1 << 0))      // 拉低

#define DHT11_PIN_CHECK ((P2 & (1 << 0)) != 0) // 读引脚:非0=高,0=低

1.5 完整代码详解

master_start() --- 发送起始信号

cs 复制代码
void master_start(void) 
{

    DHT11_PIN_DOWN;     // 拉低

    Delay_ms(20);       // 持续20ms(大于最小18ms)

    DHT11_PIN_HIGH;     // 释放总线

    Delay_30us(1);      // 等待30us让DHT11检测到起始

}

dht11_response() --- 等待应答

cs 复制代码
int dht11_response(void)
 {

    int time = 0;

    // 等待DHT11拉低(应答低电平,最多等4*30us=120us)

    while (DHT11_PIN_CHECK && time < 4) { Delay_30us(1); time++; }

    if (time >= 4) return -1;   // 超时:DHT11无响应

    // 等待DHT11拉高(应答高电平)

    time = 0;

    while (!DHT11_PIN_CHECK && time < 4) { Delay_30us(1); time++; }

    if (time >= 4) return -2;

    // 等待DHT11拉低,准备开始发数据

    while (DHT11_PIN_CHECK);

    return 1;

}

dht11_read() --- 读取40bit数据

cs 复制代码
int dht11_read(void) 
{

    int i, j;

    unsigned char dht_dat[5] = {0};

    unsigned char checksum = 0;

    master_start();

    if (dht11_response() != 1) return -1;



    for (j = 0; j < 5; j++)
 {          // 5字节

        for (i = 0; i < 8; i++) 
{       // 每字节8位,高位先收

            while (!DHT11_PIN_CHECK);    // 等低电平结束(50us低)

            Delay_30us(2);               // 延时60us后采样

            if (DHT11_PIN_CHECK)         // 仍为高 = 1

                dht_dat[j] |= (1 << (7 - i));  // 高位先存

            while (DHT11_PIN_CHECK);     // 等高电平结束

        }

    }

// 校验:前4字节之和 == 第5字节

cs 复制代码
 checksum = dht_dat[0]+dht_dat[1]+dht_dat[2]+dht_dat[3];

    if (checksum != dht_dat[4]) return -2;

    humity = dht_dat[0];  // 湿度整数

    temp   = dht_dat[2];  // 温度整数

    return 1;

}

main.c --- 每秒上报温湿度

cs 复制代码
int main(void) 
{

    uart_init();

    while (1) 
{

        if (dht11_read() == 1) 
{

            uart_sendstr("temp:");

            uart_sendnum(temp);      // 发送温度数字

            uart_sendstr(" humity:");

            uart_sendnum(humity);    // 发送湿度数字

            uart_sendstr("\r\n");

        }

        Delay_ms(1000);              // 每1秒读一次

    }

}

二、DS1302 实时时钟芯片

2.1 芯片概述

|---------------|-------------------------------------------------|
| 特性 | 说明 |
| 芯片型号 | DS1302(Dallas 公司,低功耗实时时钟 RTC) |
| 工作电压 | 2V ~ 5.5V |
| 通信接口 | 3线同步串行:CE(使能)+ SCLK(时钟)+ I/O(数据,双向) |
| 时间功能 | 秒、分、时、日、月、周、年(带自动闰年补偿,最高2100年) |
| 数据格式 | BCD码(Binary Coded Decimal):如 26分钟 = 0x26,不是0x1A |
| 内置RAM | 31字节静态 RAM,掉电不丢失(备用电池供电) |
| 数据顺序 | LSB 先发(低位先行) |

2.2 引脚定义(接 P3 口)

|------------------|------------|------------|----------------------|
| 引脚 | 名称 | 连接 | 说明 |
| CE / RST | 芯片使能 | P3^5 | 高电平开始通信,低电平结束通信 |
| SCLK | 串行时钟 | P3^6 | 上升沿写入数据,下降沿读取数据 |
| I/O | 数据输入输出 | P3^4 | 双向,LSB先行,写命令+写数据/读数据 |
| VCC2 | 主电源 | VCC | 正常供电 |
| VBAT | 备用电池 | 锂电池 | 掉电后维持时钟运行 |
| GND | 地 | GND | |
| X1/X2 | 晶振 | 32.768kHz | 外接晶振,提供精准时钟源 |

cs 复制代码
sbit DS1302_RST = P3^5;   // CE 引脚

sbit DS1302_CLK = P3^6;   // SCLK 引脚

sbit DS1302_IO  = P3^4;   // I/O 引脚

2.3 寄存器地址表

|----------------------|--------------|---------------|-------------------|
| 寄存器(读地址/写地址) | 存储内容 | BCD范围 | 说明 |
| 0x81 / 0x80 | 秒 Seconds | 00~59 | bit7=CH,CH=1时钟停止 |
| 0x83 / 0x82 | 分 Minutes | 00~59 | |
| 0x85 / 0x84 | 时 Hours | 1~12 / 0~23 | bit7=12/24模式 |
| 0x87 / 0x86 | 日 Date | 01~31 | |
| 0x89 / 0x88 | 月 Month | 01~12 | |
| 0x8B / 0x8A | 周 Day | 1~7 | 1=周日 |
| 0x8D / 0x8C | 年 Year | 00~99 | 如2025写0x25 |
| 0x8F / 0x8E | 写保护 WP | --- | 0x00=关保护;0x80=开保护 |

|-------------------|--------------------------------------------------------------------------------------------------|
| BCD码 格式说明 | 所有时间值均为BCD码格式。例:2025年3月18日 14:10:33 周二 写法为:{0x33, 0x10, 0x14, 0x18, 0x03, 0x02, 0x25}(秒分时日月周年顺序) |

2.4 写时序(ds1302_write_byte)

|------------------------------------------------------|
| 写操作流程(LSB先行) |
| ****1.****CE(RST)拉低,延时 nop()(确保初始状态) |
| ****2.****SCLK 拉低,延时 nop() |
| ****3.****CE(RST)拉高,使能芯片,开始通信 |
| ****4.****循环8次发送命令字节(地址),每位在 SCLK 下降沿前设置 I/O,然后产生上升沿 |
| ****5.****循环8次发送数据字节,同样在 SCLK 下降沿前设置,产生上升沿 |
| ****6.****CE(RST)拉低,结束通信 |

cs 复制代码
void ds1302_write_byte(u8 addr, u8 dat)
 {

    u8 i;

    DS1302_RST = 0; _nop_();  // CE拉低

    DS1302_CLK = 0; _nop_();  // SCLK拉低

    DS1302_RST = 1; _nop_();  // CE拉高,使能

    // 发送命令字节(地址),LSB先行

    for (i = 0; i < 8; i++)
 {

        DS1302_IO = addr & 0x01;  // 取最低位

        addr >>= 1;               // 右移准备下一位

        DS1302_CLK = 1; _nop_();  // 上升沿:DS1302 读取数据

        DS1302_CLK = 0; _nop_();  // 下降沿:准备下一位

    }

    // 发送数据字节,方式相同

    for (i = 0; i < 8; i++) 
{

        DS1302_IO = dat & 0x01;

        dat >>= 1;

        DS1302_CLK = 1; _nop_();

        DS1302_CLK = 0; _nop_();

    }

    DS1302_RST = 0; _nop_();      // CE拉低,结束

}

2.5 读时序(ds1302_read_byte)

|--------------------------------------------------|
| 读操作流程 |
| ****1.****CE 拉低 → SCLK 拉低 → CE 拉高(同写操作初始化) |
| ****2.****先发送命令字节(地址,LSB先行),SCLK 产生8个时钟 |
| ****3.****切换为读模式:在 SCLK 上升沿之前读取 I/O 上的数据(MSB先移入) |
| ****4.****循环8次读取数据字节(每次读一位,拼合为完整字节) |
| ****5.****CE 拉低,结束 |

cs 复制代码
u8 ds1302_read_byte(u8 addr) 
{

    u8 i, value = 0, temp;

    DS1302_RST=0; _nop_(); DS1302_CLK=0; _nop_(); DS1302_RST=1; _nop_();

    // 先发命令字节(写地址,LSB先行)

    for (i = 0; i < 8; i++) 
{

        DS1302_IO = addr & 0x01; addr >>= 1;

        DS1302_CLK = 1; _nop_(); DS1302_CLK = 0; _nop_();

    }

    // 读数据字节:上升沿前读取 I/O,串行移入 value

    for (i = 0; i < 8; i++)
 {

        temp = DS1302_IO;                    // 读当前位

        value = (temp << 7) | (value >> 1);  // 从高位移入(LSB先出 → 右移拼接)

        DS1302_CLK = 1; _nop_(); DS1302_CLK = 0; _nop_();

    }

    DS1302_RST = 0;

    return value;

}

2.6 初始化与读取时间

cs 复制代码
// 时间数组:{秒, 分, 时, 日, 月, 周, 年}(BCD格式)

u8 gWRITE_RTC_ADDR[7] = {0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};

u8 gREAD_RTC_ADDR[7]  = {0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};

u8 gDS1302_TIME[7]    = {0x33,0x10,0x14,0x18,0x03,0x02,0x25};

                        // 秒33 分10 时14 日18 月03 周二 年25(2025)



void ds1302_init(void)
 {

    u8 i;

    ds1302_write_byte(0x8E, 0x00);   // 关闭写保护

    for (i = 0; i < 7; i++)           // 写入初始时间
{

    ds1302_write_byte(gWRITE_RTC_ADDR[i], gDS1302_TIME[i]);
}

    ds1302_write_byte(0x8E, 0x80);   // 开启写保护

}



void ds1302_read_time(void)
 {

    u8 i;

    for (i = 0; i < 7; i++)
{           // 读取当前时间到数组

        gDS1302_TIME[i] = ds1302_read_byte(gREAD_RTC_ADDR[i]);
}

}

2.7 main.c --- 每秒串口打印时间

cs 复制代码
int main(void)
 {

    ds1302_init();        // 初始化(写入初始时间)

    uart_init();

    while (1) 
{

        ds1302_read_time();           // 读取当前时间

        uart_sendstr(gDS1302_TIME);   // 串口发送

        delay_ms(1000);               // 每秒刷新

    }

}

三、传感器协议横向对比

|-----------------|--------------|-------------|--------------|---------------|----------------------|
| 传感器 | 接口类型 | 信号线 | 数据顺序 | 数据格式 | 通信特点 |
| DS18B20 | GPIO 单总线 | 1根 DQ | LSB先行 | 原始值×0.0625=°C | 异步,需精确us延时 |
| DHT11 | GPIO 单总线 | 1根 DATA | MSB先行 | 直接整数,1°C精度 | 起始18ms低电平,高电平时长区分0/1 |
| DS1302 | 3线同步串行 | CE+SCLK+I/O | LSB先行 | BCD码 | SCLK上升沿锁存,写命令+写/读数据 |
| UART | 异步串行 | TX+RX | LSB先行 | 自定义 | 波特率对齐,无时钟线 |

四、知识树

第五天总结

├── DHT11 温湿度传感器

│ ├── 接口:GPIO单总线(1根DATA),MSB高位先发

│ ├── 数据:5字节(湿度整数+湿度小数+温度整数+温度小数+校验和)

│ ├── 起始:主机拉低18ms → 释放 → DHT11拉低80us → 拉高80us

│ ├── 数据0:低50us + 高26~28us

│ ├── 数据1:低50us + 高60~70us

│ └── 判断:等低结束 → 延时60us → 采样(高=1,低=0)

└── DS1302 实时时钟

├── 接口:3线同步串行(CE+SCLK+I/O),LSB先行

├── 数据:BCD码(如分钟26 = 0x26)

├── 初始化:写0x8E=0x00关保护 → 写7个时间寄存器 → 写0x8E=0x80开保护

├── 写时序:CE拉高 → 发命令字节 → 发数据字节 → CE拉低

└── 读时序:CE拉高 → 发命令字节 → SCLK上升沿读数据 → CE拉低

相关推荐
沐欣工作室_lvyiyi2 小时前
基于腾讯云的智能家居监控系统的设计开发(论文+源码)
单片机·云计算·毕业设计·智能家居·腾讯云
辰哥单片机设计2 小时前
STM32心率血氧手环(机智云)
stm32·单片机·嵌入式硬件
沐欣工作室_lvyiyi2 小时前
基于单片机的机房火灾预警系统(论文+源码)
单片机·嵌入式硬件·云平台·火灾预警
天天爱吃肉82182 小时前
【新能源汽车NTC+VCU温度采集全链路解析:原理、试验与测不准根源定位】
功能测试·嵌入式硬件·机器学习·信息可视化·汽车
somi714 小时前
51单片机-02-中断系统
单片机·嵌入式硬件·51单片机·中断
逐步前行16 小时前
STM32_SysTick_寄存器操作
stm32·嵌入式硬件·fpga开发
DLGXY16 小时前
STM32(二十四)——PWR电源控制
stm32·单片机·嵌入式硬件
csg110716 小时前
PIC单片机高阶实战(五):PIC32MX系列的数据存储
单片机·嵌入式硬件·物联网
GreenGoblin17 小时前
12.DSP学习记录之SCI
单片机·dsp开发·电机控制