GD32/AT32双平台:AHT20温湿度传感器驱动详解(附完整代码)

手把手教你用GD32E23x和AT32F403A驱动AHT20高精度温湿度传感器,含I2C协议详解、完整代码、双平台适配、编译验证,一次搞定。

一、硬件准备

传感器简介

AHT20 是 ASAIR(奥松电子)出品的高精度温湿度传感器,I2C 接口,内部集成 MEMS 传感芯片,体积小、精度高、响应快。广泛应用于暖通空调、除湿器、智能家电、医疗设备等场景。

参数
工作电压 2.2V ~ 5.5V
温度范围 -40°C ~ +85°C(精度 ±0.3°C)
湿度范围 0% ~ 100% RH(精度 ±2% RH)
采样周期 ≥1 秒
通信接口 I2C(地址 0x38)
I2C 速率 最高 400kHz(Fast Mode)

引脚接线

AHT20 GD32E23x AT32F403A
VCC 3.3V 3.3V
SCL PB6 PB6
SDA PB7 PB7
GND GND GND

注意: SCL 和 SDA 线需外接 4.7kΩ 上拉电阻到 VCC(成品模块通常已集成)。

二、I2C 通信协议详解

AHT20 使用标准 I2C 协议,7-bit 设备地址 0x38。本文采用软件模拟 I2C(bit-bang),不依赖硬件 I2C 外设,更容易理解协议本质。

命令列表

命令 代码 参数 说明
软复位 0xBA 复位传感器,等待 20ms
初始化 0xBE 0x08, 0x00 使能校准,等待 10ms
触发测量 0xAC 0x33, 0x00 启动测量,等待 ≥75ms

测量流程

Step 1 → 初始化(仅上电一次)
          主机 → AHT20: [0xBE, 0x08, 0x00]
          等待 10ms

Step 2 → 触发测量
          主机 → AHT20: [0xAC, 0x33, 0x00]
          等待 ≥75ms

Step 3 → 读取数据
          主机 ← AHT20: [Status, HumH, HumM, HumL+TempH, TempM, TempL]
          共 6 字节

数据格式与物理量转换

6 字节数据格式:

Byte 0:  状态寄存器 (bit7=忙, bit6=正常, bit3=校准已使能)
Byte 1:  湿度 [19:12]   (高 8 位)
Byte 2:  湿度 [11:4]    (中 8 位)
Byte 3:  湿度 [3:0]     (低 4 位) | 温度 [19:16] (高 4 位)
Byte 4:  温度 [15:8]    (中 8 位)
Byte 5:  温度 [7:0]     (低 8 位)

转换公式:

湿度(%RH) = 湿度原始值 / 2^20 × 100%
温度(°C)  = 温度原始值 / 2^20 × 200 - 50

三、GD32E23x 驱动实现

软件 I2C 底层

/* aht20.c — 软件 I2C 驱动 */
#include "gd32e23x_reg.h"

/* 引脚: PB6=SCL, PB7=SDA */
#define I2C_SCL_H()   GPIO_SET(GPIOB_BASE, 6)
#define I2C_SCL_L()   GPIO_CLR(GPIOB_BASE, 6)
#define I2C_SDA_H()   GPIO_SET(GPIOB_BASE, 7)
#define I2C_SDA_L()   GPIO_CLR(GPIOB_BASE, 7)
#define I2C_SDA_READ() GPIO_READ(GPIOB_BASE, 7)

/* AHT20 地址:0x38 << 1 */
#define AHT20_ADDR_W   0x70
#define AHT20_ADDR_R   0x71
#define AHT20_CMD_TRIGGER 0xAC
#define AHT20_CMD_INIT    0xBE
#define AHT20_CMD_SOFT_RST 0xBA

#define I2C_HALF_DELAY() delay_1us(5)  /* ~100kHz */

uint8_t aht20_i2c_start(void) {
    I2C_SDA_H(); I2C_SCL_H(); I2C_HALF_DELAY();
    I2C_SDA_L();                                /* SDA↓ while SCL↑ = START */
    I2C_HALF_DELAY(); I2C_SCL_L(); I2C_HALF_DELAY();
    return 0;
}

void aht20_i2c_stop(void) {
    I2C_SDA_L(); I2C_SCL_H(); I2C_HALF_DELAY();
    I2C_SDA_H();                                /* SDA↑ while SCL↑ = STOP */
    I2C_HALF_DELAY();
}

uint8_t aht20_i2c_send_byte(uint8_t data) {
    uint8_t i;
    for (i = 0; i < 8; i++) {
        if (data & 0x80) I2C_SDA_H(); else I2C_SDA_L();
        data <<= 1;
        I2C_HALF_DELAY(); I2C_SCL_H(); I2C_HALF_DELAY(); I2C_SCL_L();
    }
    I2C_SDA_INPUT(); I2C_HALF_DELAY(); I2C_SCL_H();
    I2C_HALF_DELAY();
    uint8_t ack = I2C_SDA_READ();                /* 0=ACK, 1=NACK */
    I2C_SCL_L(); I2C_SDA_OUTPUT();
    return ack;
}

uint8_t aht20_i2c_recv_byte(uint8_t ack) {
    uint8_t i, data = 0;
    I2C_SDA_INPUT();
    for (i = 0; i < 8; i++) {
        data <<= 1;
        I2C_HALF_DELAY(); I2C_SCL_H(); I2C_HALF_DELAY();
        if (I2C_SDA_READ()) data |= 1;
        I2C_SCL_L();
    }
    I2C_SDA_OUTPUT();
    if (ack) I2C_SDA_H(); else I2C_SDA_L();
    I2C_HALF_DELAY(); I2C_SCL_H(); I2C_HALF_DELAY(); I2C_SCL_L();
    return data;
}

AHT20 读写实现

uint8_t aht20_init(void) {
    /* 软复位 */
    aht20_i2c_start();
    aht20_i2c_send_byte(AHT20_ADDR_W);
    aht20_i2c_send_byte(AHT20_CMD_SOFT_RST);
    aht20_i2c_send_byte(0x00); aht20_i2c_send_byte(0x00);
    aht20_i2c_stop();
    delay_ms(20);

    /* 初始化校准 */
    aht20_i2c_start();
    aht20_i2c_send_byte(AHT20_ADDR_W);
    aht20_i2c_send_byte(AHT20_CMD_INIT);
    aht20_i2c_send_byte(0x08); aht20_i2c_send_byte(0x00);
    aht20_i2c_stop();
    delay_ms(10);
    return 0;
}

uint8_t aht20_read_th(float *temperature, float *humidity) {
    uint8_t buf[6];

    /* 触发测量 */
    aht20_i2c_start();
    aht20_i2c_send_byte(AHT20_ADDR_W);
    aht20_i2c_send_byte(AHT20_CMD_TRIGGER);
    aht20_i2c_send_byte(0x33); aht20_i2c_send_byte(0x00);
    aht20_i2c_stop();
    delay_ms(80);

    /* 读 6 字节 */
    aht20_i2c_start();
    aht20_i2c_send_byte(AHT20_ADDR_R);
    for (int i = 0; i < 6; i++)
        buf[i] = aht20_i2c_recv_byte(i >= 5 ? 1 : 0);
    aht20_i2c_stop();

    if (buf[0] & 0x80) return 2;  /* 忙 */

    uint32_t hum_raw  = ((uint32_t)buf[1] << 12) | ((uint32_t)buf[2] << 4)
                      | ((uint32_t)buf[3] >> 4);
    uint32_t temp_raw = ((uint32_t)(buf[3] & 0x0F) << 16)
                      | ((uint32_t)buf[4] << 8) | (uint32_t)buf[5];

    *humidity    = (float)hum_raw  / 1048576.0f * 100.0f;
    *temperature = (float)temp_raw / 1048576.0f * 200.0f - 50.0f;
    return 0;
}

四、AT32F403A 适配

AT32 版本协议逻辑完全一致,仅以下差异:

项目 GD32E23x AT32F403A
头文件 gd32e23x_reg.h at32f403a_reg.h
GPIO 模式 GPIO_SET_MODE(port, pin, GPIO_MODE_OUTPUT_PP) GPIO_CFG(port, pin, GPIO_MODE_OUTPUT)
时钟使能 RCU_AHBEN CRM_AHB1EN
主频 72MHz 216MHz
delay_1us 循环 ×6 ×18

完整 AT32 源码结构一致,主要是寄存器和宏定义替换。

五、编译与烧录验证

编译输出

# GD32
   text    data     bss     dec     hex
   6520       0    2056    8576    2180  AHT20_GD32.elf

# AT32
   text    data     bss     dec     hex
   4940       0       4    4944    1350  AHT20_AT32.elf

串口验证

烧录后打开串口助手(115200-8-N-1),每 1 秒输出一次:

[OK] Temp: 25.1 C,  RH: 60.3 %
[OK] Temp: 25.2 C,  RH: 60.1 %
[OK] Temp: 25.1 C,  RH: 60.2 %

六、注意事项

  1. 上拉电阻: SCL 和 SDA 必须外接 4.7kΩ 上拉电阻(成品模块通常已集成)
  2. 采样间隔: AHT20 最小测量间隔 1 秒,建议 ≥1 秒
  3. 首次上电: 上电后等待 20ms 才能通信
  4. 时序适配: 软件 I2C 的 delay_1us 循环数需适配 MCU 主频——GD32@72MHz 和 AT32@216MHz 不同
  5. 精度优势: AHT20 出厂已校准,温度 ±0.3°C、湿度 ±2% RH,显著优于 DHT11
  6. 与 DHT11 对比: AHT20 用 I2C 接口替代单总线,时序容错性更好,通信更可靠

\n\n> 完整工程源码(含 Keil 工程文件)已整理完毕,详见博主主页。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐