智能天气时钟项目(二):AHT20温湿度传感器驱动开发详解

前言

在嵌入式物联网项目中,温湿度采集是非常常见的需求。AHT20是一款高精度、低成本的数字温湿度传感器,广泛应用于智能家居、环境监测等领域。本文将详细介绍AHT20传感器的工作原理,并基于STM32F4平台,手把手教你实现一个完整的AHT20驱动代码。

一、AHT20传感器简介

1.1 产品概述

AHT20,新一代温湿度传感器在尺寸与智能 方面建立了新的标准:它嵌入了适于回流焊 的双列扁平无引脚SMD 封装,底面 3 x 3mm , 高度1.0mm。传感器输出经过标定的数字信 2 号,标准 I2C 格式。 AHT20 配有一个全新设计的 ASIC专用芯片、 一个经过改进的MEMS半导体电容式湿度传感 元件和一个标准的片上温度传感元件,其性 能已经大大提升甚至超出了前一代传感器的 可靠性水平,新一代温湿度传感器,经过改 进使其在恶劣环境下的性能更稳定。每一个传感器都经过校准和测试,在产品表 面印有产品批号。由于对传感器做了改良和 微型化改进,因此它的性价比更高,并且最 终所有设备都将得益于尖端的节能运行模式。其具备以下特点:

  • 供电电压:2.2V ~ 5.5V

  • 湿度测量范围:0% ~ 100% RH

  • 温度测量范围:-40℃ ~ +85℃

  • 湿度精度:±2% RH

  • 温度精度:±0.3℃

  • I2C通信速率:最高1MHz

  • I2C设备地址:0x38(7位地址)或0x70(写操作地址)

1.2 引脚说明

引脚 名称 功能描述
1 VDD 电源正极(2.2V-5.5V)
2 SDA I2C数据线
3 GND 电源地
4 SCL I2C时钟线
5 NC 悬空
6 NC 悬空

1.3 工作流程

AHT20的典型工作流程如下:

上电 → 初始化(发送初始化命令)→ 等待校准完成 → 触发测量 → 等待测量完成 → 读取数据 → 解析数据

二、I2C通信协议详解

AHT20使用标准I2C协议进行通信,7位设备地址为0x38(写操作地址0x70,读操作地址0x71)。

2.1 命令列表

命令 操作码 说明
初始化 0xBE, 0x08, 0x00 发送3字节初始化序列
触发测量 0xAC, 0x33, 0x00 启动温湿度测量
读取状态 0x71 读取传感器状态字
软复位 0xBA 软件复位传感器

2.2 状态字说明

AHT20的状态字占用1字节(8位),各比特位含义如下:

含义
Bit[7] 忙闲指示位:1=忙,0=空闲
Bit[6:5] 保留
Bit[4] 保留
Bit[3] 校准使能位:1=已校准,0=未校准
Bit[2:0] 保留

2.3 数据格式

测量完成后,传感器返回6字节数据(具体见下面的图片):

字节0:状态字
字节1:湿度数据高8位
字节2:湿度数据中8位
字节3:湿度数据低4位 + 温度数据高4位
字节4:温度数据中8位
字节5:温度数据低8位

三、STM32驱动代码实现

3.1 硬件初始化

首先需要初始化I2C2外设,使用GPIOA的Pin10(SCL)和Pin11(SDA):

cpp 复制代码
#include <stdbool.h>
#include <stdint.h>
#include "stm32f4xx.h"
#include "cpu_tick.h"

static bool aht20_write(uint8_t data[], uint32_t length);
static bool aht20_read(uint8_t data[], uint32_t length);
static bool aht20_is_ready(void);

bool aht20_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructre;
    GPIO_StructInit(&GPIO_InitStructre);
    GPIO_InitStructre.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructre.GPIO_OType = GPIO_OType_OD;      // 开漏输出
    GPIO_InitStructre.GPIO_PuPd = GPIO_PuPd_NOPULL;    // 无上下拉
    GPIO_InitStructre.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStructre.GPIO_Speed = GPIO_High_Speed;
    GPIO_Init(GPIOA, &GPIO_InitStructre);
    
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_I2C2);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_I2C2);
    
    I2C_InitTypeDef I2C_InitStructure;
    I2C_StructInit(&I2C_InitStructure);
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_ClockSpeed = 100ul * 1000ul;  // 100kHz
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_OwnAddress1 = 0x11;
    
    I2C_Init(I2C2, &I2C_InitStructure);
    I2C_Cmd(I2C2, ENABLE);
    
    cpu_delay_ms(40);  // 上电等待
    
    // 检查传感器是否就绪
    if(aht20_is_ready())
        return true;
    
    // 发送初始化命令
    if(!aht20_write((uint8_t []){0xBE, 0x08, 0x00}, 3))
        return false;
    
    // 等待初始化完成
    for(uint32_t t = 0; t < 100; t ++)
    {
        cpu_delay_ms(1);
        if(aht20_is_ready())
            return true;
    }
    return false;
}

3.2 I2C通信封装

为了提高代码的可读性和可维护性,定义了I2C操作宏:

cpp 复制代码
#define I2C_CHECK_EVENT(EVENT, TIMEOUT) \
    do { \
        uint32_t timeout = TIMEOUT; \
        while (!I2C_CheckEvent(I2C2, EVENT) && timeout > 0) { \
            cpu_delay_us(10); \
            timeout -= 10; \
        } \
        if (timeout <= 0) \
            return false; \
    } while (0)

static bool aht20_write(uint8_t data[], uint32_t length)
{
    I2C_AcknowledgeConfig(I2C2, ENABLE);
    I2C_GenerateSTART(I2C2, ENABLE);
    I2C_CHECK_EVENT(I2C_EVENT_MASTER_MODE_SELECT, 1000);
    
    I2C_Send7bitAddress(I2C2, 0x70, I2C_Direction_Transmitter);
    I2C_CHECK_EVENT(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED, 1000);
    
    for(uint32_t i = 0; i < length; i++)
    {
        I2C_SendData(I2C2, data[i]);
        I2C_CHECK_EVENT(I2C_EVENT_MASTER_BYTE_TRANSMITTING, 1000);
    }
    
    I2C_GenerateSTOP(I2C2, ENABLE);
    return true;
}    

static bool aht20_read(uint8_t data[], uint32_t length)
{
    I2C_AcknowledgeConfig(I2C2, ENABLE);
    I2C_GenerateSTART(I2C2, ENABLE);
    I2C_CHECK_EVENT(I2C_EVENT_MASTER_MODE_SELECT, 1000);
    
    I2C_Send7bitAddress(I2C2, 0x70, I2C_Direction_Receiver);
    I2C_CHECK_EVENT(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED, 1000);
    
    for(uint32_t i = 0; i < length; i++)
    {
        // 最后一个字节不需要ACK
        if(i == length - 1)
        {
            I2C_AcknowledgeConfig(I2C2, DISABLE);
        }        
        I2C_CHECK_EVENT(I2C_EVENT_MASTER_BYTE_RECEIVED, 1000);
        data[i] = I2C_ReceiveData(I2C2);
    }    
    I2C_GenerateSTOP(I2C2, ENABLE);
    
    return true;
}

3.3 状态读取与判断

cpp 复制代码
// 读取AHT20的状态字
static bool aht20_read_status(uint8_t *status)
{
    uint8_t cmd = 0x71;
    if(!aht20_write(&cmd, 1))
        return false;
    if(!aht20_read(status, 1))
        return false;
    return true;
}

// 判断传感器是否忙(Bit[7])
static bool aht20_is_busy(void)
{
    uint8_t status;
    if (!aht20_read_status(&status))
        return false;
    return (status & 0x80) != 0;
}

// 判断传感器是否校准就绪(Bit[3])
static bool aht20_is_ready(void)
{
    uint8_t status;
    if (!aht20_read_status(&status))
        return false;
    return (status & 0x08) != 0;
}

3.4 测量功能实现

cpp 复制代码
bool aht20_start_measurement(void)
{
    return aht20_write((uint8_t[]){0xAC, 0x33, 0x00}, 3);
}

bool aht20_wait_for_measurement(void)
{
    for (uint32_t t = 0; t < 200; t++)
    {
        cpu_delay_ms(1);
        if (!aht20_is_busy())
        {
            return true;
        }
    }
    return false;
}

bool aht20_read_measurement(float *temperature, float *humidity)
{
    uint8_t data[6];
    if (!aht20_read(data, 6))
        return false;
    
    // 解析湿度数据
    uint32_t raw_humidity = ((uint32_t)data[1] << 12) | 
                            ((uint32_t)data[2] << 4) | 
                            ((uint32_t)(data[3] & 0xF0) >> 4);
    
    // 解析温度数据
    uint32_t raw_temperature = ((uint32_t)(data[3] & 0x0F) << 16) | 
                               ((uint32_t)data[4] << 8) | 
                               ((uint32_t)data[5]);
    
    // 转换为实际物理量
    *humidity = (float)raw_humidity * 100.0f / (float)0x100000;
    *temperature = (float)raw_temperature * 200.0f / (float)0x100000 - 50.0f;
    
    return true;
}

四、使用示例

cpp 复制代码
#include "aht20.h"

int main(void)
{
    float temperature, humidity;
    
    // 系统初始化
    cpu_tick_init();  // 假设有滴答定时器初始化
    I2C_Cmd(I2C2, ENABLE);
    
    // 初始化AHT20
    if (aht20_init())
    {
        printf("AHT20初始化成功\r\n");
    }
    else
    {
        printf("AHT20初始化失败\r\n");
        while(1);
    }
    
    while(1)
    {
        // 触发测量
        if (aht20_start_measurement())
        {
            // 等待测量完成
            if (aht20_wait_for_measurement())
            {
                // 读取测量结果
                if (aht20_read_measurement(&temperature, &humidity))
                {
                    printf("温度: %.2f℃, 湿度: %.2f%%\r\n", temperature, humidity);
                }
            }
        }
        
        // 每秒采集一次
        cpu_delay_ms(1000);
    }
}

五、总结

本文详细介绍了AHT20温湿度传感器的特性和使用方法,并给出了完整的STM32驱动代码实现。通过本文的学习,你可以:

  • 理解AHT20的工作原理和通信协议

  • 掌握I2C通信的编程方法

  • 学会状态字解析和数据转换

  • 能够在实际项目中快速集成AHT20传感器

希望本文对你有所帮助。如果在实际使用中遇到问题,欢迎在评论区留言交流。

相关推荐
我爱吃土豆112 小时前
Gin响应形式
驱动开发·gin
_Emma_21 小时前
【QCOM】 Linux下qcom venus 编解码驱动框架分析
linux·驱动开发·视频编解码
春日见1 天前
TEST文件夹:Pytest,集成测试,单元测试
服务器·人工智能·驱动开发·单元测试·计算机外设·集成测试·pytest
清水白石0082 天前
Python 项目 CI/CD 信心模型:证据驱动部署,从“勇敢上线”到“零风险发版”实战指南
驱动开发·python·ci/cd
A.说学逗唱的Coke2 天前
【AI协同软件工程】规范驱动开发工具全景解析:OpenSpec、SpecKit与传统SDD工具深度对比指南
人工智能·驱动开发·软件工程
国医中兴2 天前
Flutter 三方库 pickled_cucumber 的鸿蒙化适配指南 - 玩转 BDD 行为驱动开发、Gherkin 自动化测试实战、鸿蒙级质量守护神
驱动开发·flutter·harmonyos
tdhao8882 天前
部署 VS2022 驱动开发环境-解决无法编译驱动的问题
驱动开发·visual studio
欲盖弥彰13143 天前
Linux设备驱动 -- TMP75AIDR驱动移植
linux·驱动开发·驱动·驱动移植·嵌入式linux驱动·tmp75aidr
ShawnLiaoking3 天前
华硕ROG LoadLibrary failed 126
驱动开发·显示器