# AHT20+BMP280 **Repository Path**: RyanSunRep/aht20--bmp280 ## Basic Information - **Project Name**: AHT20+BMP280 - **Description**: 做了个AHT20和BMP280的基于IIC接口的驱动程序,能够显示温湿度和气压数值 - **Primary Language**: C - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2026-07-14 - **Last Updated**: 2026-07-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AHT20+BMP280 # 介绍 做了个AHT20和BMP280的基于IIC接口的驱动程序,能够显示温湿度和气压数值 ​ # 一、前言 aht20是一个温度&湿度传感器,bmp280是一个温度&气压传感器。 前一阵子在某宝几乎0成本购入了一个野生的aht20+bmp280一体模块,询问店家发现店家手上只有一个驱动例程,没有别的资料。观察了这个模块,我发现它应该是用的i2c总线连接了两个从设备做成了个一体的模块,所以我就从官网上找到了这两个模块的数据手册,抱着学习的心自己写了一个更加通俗易懂的驱动,经过运行调试之后测得数据没有问题。 # 二、思路 aht20和bmp280都可以使用i2c进行通信,所以我们只要知道i2c的通信原理,然后照着它们的数据手册一步步配置寄存器并使用hal库中封装好的i2c通信函数就可以把驱动敲出来了。 # 三、实践分析 ## 一、aht20模块 ### 1、上电 我们从数据手册中可以得知,首先通电延时100ms ### 2、读状态字 我们需要使用 `HAL_I2C_Master_Receive(&hi2c1, 0x71, data, 1, 1000);` 可以来读取到一个状态字并且存放在data变量中(因为设备通电初始化完成后会默认指向0x38地址的寄存器。为什么接收是0x71?因为i2c的从机地址占7位,但是它需要传8位地址,最后一位是读取操作时为1,所以是01110001,也就是0x71)。 ### 3、发送测量命令 然后我们等待10ms后发送命令0xAC(触发测量),此命令参数有两个字节,第一个字节为0x33,第二个字节为0x00,等待80ms待测量完成。 uint8_t send[3] = {0xAC,0x33,0x00};//读取数据指令 HAL_I2C_Master_Transmit(&hi2c1, aht20_device_address,send,3,1000);//发送指令 HAL_Delay(80);//延时 ### 4、读取状态字 然后我们继续根据手册看 “若读取状态字Bit[7]为0,表示测量完成,然后可以连续读取六个字节;否则继续等待。 这里我测试过了,正常情况下延时80ms是可以读到数据的,所以直接写了个if没有处理else。 HAL_I2C_Master_Receive(&hi2c1, 0x71, data, 6, 1000);//读取数据 HAL_Delay(80); if((data[0] & 0x80) == 0x00) { uint32_t shidu,wendu; shidu = ((uint32_t)data[1] << 12) + ((uint32_t)data[2] << 8) + (data[3] >> 4); *humidity = (shidu * 100.0f)/(1<<20); wendu = (((uint32_t)data[3]&0x0f) << 16) + ((uint32_t)data[4] << 8) + (data[5]); *temperature = ((wendu * 200.0f)/(1<<20)) - 50.0; } ### 5、读取数据 我们根据手册可知: 收到的第一个字节是状态位,后面第2到6个字节收到的是温湿度数据,其中第二个字节、第三个字节和第四个字节的高四位是湿度数据,第四个字节的低四位、第五个字节和第六个字节是温度数据。我们只需要将对应字节拿出进行拼接即可。 uint32_t shidu,wendu; shidu = ((uint32_t)data[1] << 12) + ((uint32_t)data[2] << 8) + (data[3] >> 4); wendu = (((uint32_t)data[3]&0x0f) << 16) + ((uint32_t)data[4] << 8) + (data[5]); 注意移位操作时将数据长度拉长再移位,避免数据丢失。 ### 6、数据处理 没啥好说的我们继续看文档 *humidity = (shidu * 100.0f)/(1<<20); *temperature = ((wendu * 200.0f)/(1<<20)) - 50.0; ## 二、bmp280模块 ### 1、上电读取设备id 根据官方文档可知我们需要商上电读取0xD0,看id是否为0x58,若不是0x58我们就给它发送初始化指令。 void bmp280_init() { uint8_t data; uint8_t idAddress = 0xD0; uint8_t reset[2] = {0xE0,0x86};//0xE0 寄存器地址 0x86 复位命令 HAL_I2C_Master_Transmit(&hi2c1, bmp280_device_address,&idAddress,1,1000); HAL_I2C_Master_Receive(&hi2c1, bmp280_device_address , &data, 1, 1000); if(data != 0x58) { HAL_I2C_Master_Transmit(&hi2c1, bmp280_device_address,reset,2,1000); } } 参考: ### 2、发送测量命令     格式如图 uint8_t data[6]; uint8_t Compensation_parameter[24]; uint8_t StartReadData[2] = {0xF4,0x47};//0xF4 寄存器地址 000 温度 000 气压 00 工作模式 这里是010 001 11 uint8_t ReadData = 0xF7;//数据存储寄存器的起始地址,一共6个 uint8_t Compensation = 0x88;//校准参数的设定,存储在0x88寄存器起始地址,一共26个寄存器 HAL_I2C_Master_Transmit(&hi2c1, bmp280_device_address,StartReadData,2,1000);//发送命令 HAL_Delay(2); HAL_I2C_Master_Transmit(&hi2c1, bmp280_device_address,&ReadData,1,1000);//指定数据寄存器0xF7 HAL_Delay(1); HAL_I2C_Master_Receive(&hi2c1, bmp280_device_address, data, 6, 1000);//读取数据寄存器,并连续读取6个寄存器的数据 HAL_Delay(6); ### 3、读取校准数据 文档有点小问题,经过检验,下面这个是存储压强数据寄存器地址 下面这个是存储温度的寄存器地址 代码: //读取校准数据 HAL_I2C_Master_Transmit(&hi2c1, bmp280_device_address,&Compensation,1,1000); HAL_Delay(1); HAL_I2C_Master_Receive(&hi2c1, bmp280_device_address, Compensation_parameter, 24, 1000); HAL_Delay(26); uint16_t dig_T1 = (((uint16_t)Compensation_parameter[1])<<8) + ((uint16_t)Compensation_parameter[0]); int16_t dig_T2= (((int16_t)Compensation_parameter[3])<<8) + ((int16_t)Compensation_parameter[2]); int16_t dig_T3= (((int16_t)Compensation_parameter[5])<<8) + ((int16_t)Compensation_parameter[4]); uint16_t dig_P1 = (((uint16_t)Compensation_parameter[7])<<8) + ((uint16_t)Compensation_parameter[6]); int16_t dig_P2= (((int16_t)Compensation_parameter[9])<<8) + ((int16_t)Compensation_parameter[8]); int16_t dig_P3= (((int16_t)Compensation_parameter[11])<<8) + ((int16_t)Compensation_parameter[10]); int16_t dig_P4= (((int16_t)Compensation_parameter[13])<<8) + ((int16_t)Compensation_parameter[12]); int16_t dig_P5= (((int16_t)Compensation_parameter[15])<<8) + ((int16_t)Compensation_parameter[14]); int16_t dig_P6= (((int16_t)Compensation_parameter[17])<<8) + ((int16_t)Compensation_parameter[16]); int16_t dig_P7= (((int16_t)Compensation_parameter[19])<<8) + ((int16_t)Compensation_parameter[18]); int16_t dig_P8= (((int16_t)Compensation_parameter[21])<<8) + ((int16_t)Compensation_parameter[20]); int16_t dig_P9= (((int16_t)Compensation_parameter[23])<<8) + ((int16_t)Compensation_parameter[22]); HAL_Delay(1); ### 4、数据处理         这里是完完全全跟着官方文档给的公式敲的: 代码: double adc_T,adc_P; double var1,var2; int32_t t_fine; adc_P = (((uint32_t)data[0])<<12) + (((uint32_t)data[1])<<4) + ((((uint32_t)data[2])>>4)&0x0f); adc_T = (((uint32_t)data[3])<<12) + (((uint32_t)data[4])<<4) + ((((uint32_t)data[5])>>4)&0x0f); var1 = (((double)adc_T)/16384.0 - ((double)dig_T1)/1024.0) * ((double)dig_T2); var2 = ((((double)adc_T)/131072.0 - ((double)dig_T1)/8192.0) * (((double)adc_T)/131072.0 - ((double) dig_T1)/8192.0)) * ((double)dig_T3); t_fine = (signed int)(var1 + var2); *temp = (t_fine)/5120.0; HAL_Delay(1); var1 = ((double)t_fine/2.0) - 64000.0; var2 = var1 * var1 * ((double)dig_P6) / 32768.0; var2 = var2 + var1 * ((double)dig_P5) * 2.0; var2 = (var2/4.0) + (((double)dig_P4) * 65536.0); var1 = (((double)dig_P3) * var1*var1 / 524288.0 + ((double)dig_P2) * var1)/524288.0; var1 = (1.0 + var1 / 32768.0) * ((double)dig_P1); HAL_Delay(1); double p = 1048576.0 - (double)adc_P; p = (p-(var2/4096.0)) * 6250.0 / var1; var1 = ((double)dig_P9) * p * p / 2147483648.0; var2 = p * ((double)dig_P8) / 32768.0; *press = p + (var1 + var2 + ((double)dig_P7)) / 16.0; ## 三、结果 第一第二行是aht20读出的温度与湿度数据,第三第四行是bmp280读出的温度与气压数据。我们还可以根据这个气压数据算出我们此时身处的海拔。 ## 四、总结 第一次写还是挺有挑战的,这篇分享的是我的学习成果和自己敲的代码,仅供交流学习。 (原本发csdn的,同步到gitee) ​