STM32F103基于I2C协议的AHT20温湿度传感器数据采集

一、I2C总线通信协议基础

1.1 什么是I2C协议

I2C(Inter-Integrated Circuit)是一种同步、多主从、半双工的串行通信总线,由Philips公司开发。它只需要两根线即可实现设备间的通信:

  • SDA:串行数据线
  • SCL:串行时钟线

1.2 软件I2C vs 硬件I2C

硬件I2C
  • 定义:使用STM32芯片内部专用的I2C外设控制器
  • 特点
    • 有专门的I2C引脚(如PB6-SCL, PB7-SDA)
    • 硬件自动处理时序、ACK/NACK响应
    • 支持中断和DMA传输
    • 编程简单,调用HAL库函数即可
  • 优点
    • 效率高,不占用CPU资源
    • 时序精确,稳定性好
    • 支持高速模式(400kHz)
  • 缺点
    • 引脚固定,配置不够灵活
    • 调试相对复杂

二、AHT20温湿度传感器介绍

2.1 AHT20特性

  • 高精度:温度精度±0.3℃,湿度精度±2%RH
  • 数字输出:I2C接口
  • 工作电压:2.2V-5.5V
  • 低功耗:测量期间1.2mA,空闲状态0.2μA

2.2 AHT20器件地址

AHT20的7位器件地址为0x70

三、硬件设计

3.1 硬件连接

STM32F103 AHT20 说明
PB6 SCL I2C时钟线
PB7 SDA I2C数据线
3.3V VCC 电源正极
GND GND 电源地
PA9 TX 串口输出(接USB转TTL)

四、软件实现

4.1 工程配置

使用STM32CubeMX配置:

  • 系统时钟:72MHz
  • I2C1:标准模式(100kHz)
  • USART1:115200波特率,8数据位,无校验
  • GPIO:PB6(SCL),PB7(SDA),PA9(TX)

4.2 核心代码实现

主程序 (main.c)
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
#include <stdio.h>
#include <string.h>

I2C_HandleTypeDef hi2c1;
UART_HandleTypeDef huart1;

// AHT20相关定义
#define AHT20_ADDRESS 0x70      // 器件地址
#define AHT20_CMD_INIT 0xBE     // 初始化命令
#define AHT20_CMD_MEASURE 0xAC  // 测量命令
#define AHT20_CMD_RESET 0xBA    // 软复位命令

void SystemClock_Config(void);

/**
 * @brief AHT20初始化函数
 * @retval 1-成功, 0-失败
 */
uint8_t AHT20_Init(void)
{
    uint8_t cmd[3] = {AHT20_CMD_INIT, 0x08, 0x00};
    uint8_t status = 0;
    
    // 发送初始化命令
    if(HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS << 1, cmd, 3, 100) != HAL_OK)
    {
        return 0;
    }
    
    HAL_Delay(10);  // 等待初始化完成
    
    // 读取状态字
    if(HAL_I2C_Master_Receive(&hi2c1, (AHT20_ADDRESS << 1) | 0x01, &status, 1, 100) != HAL_OK)
    {
        return 0;
    }
    
    // 检查校准位(bit[3]应该为1)
    if((status & 0x08) == 0)
    {
        return 0; // 校准失败
    }
    
    return 1; // 初始化成功
}

/**
 * @brief AHT20软复位
 * @retval 1-成功, 0-失败
 */
uint8_t AHT20_Reset(void)
{
    if(HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS << 1, &AHT20_CMD_RESET, 1, 100) != HAL_OK)
    {
        return 0;
    }
    HAL_Delay(20);
    return 1;
}

/**
 * @brief 读取AHT20温湿度数据
 * @param temperature: 温度值(摄氏度)
 * @param humidity: 湿度值(百分比)
 * @retval 1-成功, 0-失败
 */
uint8_t AHT20_Read_Data(float *temperature, float *humidity)
{
    uint8_t data[6];
    uint8_t cmd[3] = {AHT20_CMD_MEASURE, 0x33, 0x00};
    uint32_t temp_raw, humi_raw;
    
    // 发送测量命令
    if(HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS << 1, cmd, 3, 100) != HAL_OK)
    {
        return 0;
    }
    
    // 等待测量完成(最大80ms)
    HAL_Delay(80);
    
    // 读取6字节数据
    if(HAL_I2C_Master_Receive(&hi2c1, (AHT20_ADDRESS << 1) | 0x01, data, 6, 100) != HAL_OK)
    {
        return 0;
    }
    
    // 检查状态位,确保数据有效
    if((data[0] & 0x80) == 0x80)
    {
        return 0; // 设备忙
    }
    
    // 转换湿度数据(20bit)
    humi_raw = ((uint32_t)data[1] << 12) | ((uint32_t)data[2] << 4) | ((uint32_t)data[3] >> 4);
    *humidity = (float)humi_raw * 100.0 / (1 << 20);
    
    // 转换温度数据(20bit)
    temp_raw = (((uint32_t)data[3] & 0x0F) << 16) | ((uint32_t)data[4] << 8) | data[5];
    *temperature = (float)temp_raw * 200.0 / (1 << 20) - 50.0;
    
    return 1;
}

/**
 * @brief 重定向printf到串口
 */
int _write(int file, char *ptr, int len)
{
    HAL_UART_Transmit(&huart1, (uint8_t*)ptr, len, 1000);
    return len;
}

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_I2C1_Init();
    MX_USART1_UART_Init();
    
    float temperature, humidity;
    char buffer[100];
    uint32_t last_tick = 0;
    
    printf("STM32F103 AHT20 Temperature & Humidity Monitor\r\n");
    printf("Initializing AHT20...\r\n");
    
    // 初始化AHT20
    if(AHT20_Init())
    {
        printf("AHT20 Initialize Success!\r\n");
    }
    else
    {
        printf("AHT20 Initialize Failed! Trying reset...\r\n");
        AHT20_Reset();
        HAL_Delay(100);
        if(AHT20_Init())
        {
            printf("AHT20 Initialize Success after reset!\r\n");
        }
        else
        {
            printf("AHT20 Initialize Failed! Check hardware connection.\r\n");
            while(1);
        }
    }
    
    printf("Start Reading Temperature and Humidity...\r\n");
    printf("Time(s)\tTemperature(C)\tHumidity(%%) \r\n");
    
    while (1)
    {
        // 每隔2秒读取一次数据
        if(HAL_GetTick() - last_tick >= 2000)
        {
            last_tick = HAL_GetTick();
            
            if(AHT20_Read_Data(&temperature, &humidity))
            {
                // 输出格式化的数据
                sprintf(buffer, "%.1f\t%.2f\t\t%.2f\r\n", 
                       (float)last_tick/1000.0, temperature, humidity);
                printf("%s", buffer);
            }
            else
            {
                printf("Read Data Failed!\r\n");
            }
        }
        
        HAL_Delay(100);
    }
}
I2C配置 (i2c.c)
#include "i2c.h"

I2C_HandleTypeDef hi2c1;

void MX_I2C1_Init(void)
{
    hi2c1.Instance = I2C1;
    hi2c1.Init.ClockSpeed = 100000;  // 100kHz标准模式
    hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
    hi2c1.Init.OwnAddress1 = 0;
    hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
    hi2c1.Init.OwnAddress2 = 0;
    hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
    hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
    
    if (HAL_I2C_Init(&hi2c1) != HAL_OK)
    {
        Error_Handler();
    }
}

void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if(i2cHandle->Instance==I2C1)
    {
        __HAL_RCC_GPIOB_CLK_ENABLE();
        __HAL_RCC_I2C1_CLK_ENABLE();
        
        // PB6 - SCL, PB7 - SDA
        GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;  // 开漏输出
        GPIO_InitStruct.Pull = GPIO_PULLUP;      // 上拉电阻
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    }
}
串口配置 (usart.c)
#include "usart.h"

UART_HandleTypeDef huart1;

void MX_USART1_UART_Init(void)
{
    huart1.Instance = USART1;
    huart1.Init.BaudRate = 115200;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_TX_RX;
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    
    if (HAL_UART_Init(&huart1) != HAL_OK)
    {
        Error_Handler();
    }
}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if(uartHandle->Instance==USART1)
    {
        __HAL_RCC_USART1_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();
        
        // PA9 - TX, PA10 - RX
        GPIO_InitStruct.Pin = GPIO_PIN_9;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
        
        GPIO_InitStruct.Pin = GPIO_PIN_10;
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
}

五、程序运行结果

编译下载程序后,通过串口调试助手可以看到如下输出:
在这里插入图片描述

六、常见问题及解决方法

6.1 初始化失败

  • 问题:AHT20初始化失败
  • 解决
    1. 检查硬件连接是否正确
    2. 确认电源电压为3.3V
    3. 检查I2C上拉电阻(通常4.7kΩ)
    4. 尝试软件复位AHT20

6.2 读取数据失败

  • 问题:读取温湿度数据失败
  • 解决
    1. 检查I2C时序配置
    2. 增加测量等待时间
    3. 检查状态字是否正常

七、总结

本项目成功实现了基于STM32F103的AHT20温湿度传感器数据采集系统,主要特点包括:

  1. 硬件I2C通信:使用STM32硬件I2C外设,通信稳定可靠
  2. 精确数据采集:按照AHT20数据手册协议正确读取和转换数据
  3. 实时串口输出:通过USART将数据实时发送到上位机
  4. 完善的错误处理:包含初始化检测、状态字检查等错误处理机制

此实验可以广泛应用于各种需要温湿度监测的嵌入式系统中,如环境监测、智能家居、农业物联网等领域。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐