AHT10温湿度传感器在天空星STM32F407上的I2C驱动移植与数据采集实战最近在做一个环境监测的小项目需要用到温湿度传感器。选来选去发现AHT10这个小家伙挺不错精度够用价格也便宜关键是它用的是I2C接口接线简单特别适合咱们嵌入式初学者上手。今天我就带着大家手把手把AHT10的驱动移植到天空星STM32F407开发板上最后通过串口把温湿度数据打印出来。这篇文章就是给刚开始接触嵌入式或者想快速给项目加上环境传感器的朋友准备的。我会从最基础的硬件接线讲起然后一步步分析I2C通信的时序最后把完整的代码移植到工程里。跟着做下来你不仅能学会怎么用AHT10还能彻底搞懂I2C这个常用通信协议是怎么工作的。1. 认识一下AHT10小而精的数字温湿度传感器在动手写代码之前咱们先花几分钟了解一下要用的传感器。AHT10是一个数字式的温湿度传感器它把测量芯片、校准数据都集成在一个小小的封装里尺寸只有4mm x 5mm比一粒米大不了多少。它最大的特点就是“省事”。传感器内部已经完成了温湿度的测量和校准直接通过I2C总线输出数字信号咱们的STM32单片机拿到就能用不用自己再去处理模拟信号或者做复杂的换算。这里有几个关键参数你心里得有个数参数规格说明工作电压1.8V ~ 3.6V可以直接用STM32的3.3V供电工作电流0.25uA ~ 23uA非常省电适合电池供电设备湿度测量误差±2% RH日常环境监测完全够用温度测量误差±0.3℃精度相当不错输出接口I2C只需要两根线SDA, SCL引脚数量3 PinVCC, GND, SDA, SCL有些模块SDA和SCL共用AHT10的应用场景很广像智能家居里的温湿度监控、数据记录仪、气象站这些小项目都能用上。咱们这次用的模块通常长这样你可以对照手头的模块看看VCC接3.3V电源GND接地SDAI2C数据线SCLI2C时钟线有些模块为了节省引脚会把SDA和SCL做在一起通过一个电阻来切换方向但原理都是一样的。注意购买模块时记得找卖家要资料包里面应该有数据手册。咱们后续的通信时序、器件地址等信息都来自数据手册。2. 硬件连接把传感器接到开发板上硬件连接特别简单就是四根线。我以天空星STM32F407开发板为例一般来说我们会选择一组I2C接口来连接。在提供的代码里用的是GPIOB的第8和第9脚分别作为SCL和SDA。具体接线如下AHT10模块的VCC→ 开发板的3.3V引脚AHT10模块的GND→ 开发板的GND引脚AHT10模块的SCL→ 开发板的PB8引脚AHT10模块的SDA→ 开发板的PB9引脚提示I2C总线需要上拉电阻。通常模块上已经集成了4.7kΩ或10kΩ的上拉电阻如果你的模块没有就需要在SDA和SCL线上各接一个4.7kΩ的电阻到3.3V。接好线之后硬件部分就搞定了。是不是很简单嵌入式开发很多时候就是这样硬件连接不复杂难的是软件驱动和通信协议的理解。接下来咱们就进入重头戏——I2C通信。3. I2C通信基础与AHT10的读写时序I2C是一种非常常用的两线式串行总线一根是时钟线SCL一根是数据线SDA。它支持多个设备挂载在同一总线上每个设备都有一个唯一的地址。AHT10的器件地址是0x38。这里有个关键点要记住在I2C通信中实际发送的地址字节是7位器件地址加上1位读写位。所以对于写操作我们要发送(0x38 1) | 0 0x70对于读操作要发送(0x38 1) | 1 0x71。原文代码里直接用了0x381然后通过或操作加上读写位是一个意思。一次完整的AHT10数据采集时序稍微有点长我把它拆解成几个阶段你跟着我的思路走3.1 启动测量首先单片机主机要告诉AHT10从机“开始测量吧”这个过程如下主机发送起始信号Start Condition主机发送器件地址写位0x70等待AHT10应答ACK主机发送触发测量命令0xAC等待AHT10应答主机发送第一个数据字节0x33等待AHT10应答主机发送第二个数据字节0x00等待AHT10应答主机可以发送停止信号Stop Condition原文说这里“可不加”但通常建议加上。发送完这些AHT10就知道要开始测量了。它会进入忙碌状态这个时候你不能去打扰它。3.2 读取数据AHT10测量需要一点时间所以我们要等它忙完。怎么知道它忙完了呢需要不断地去读它的状态字。主机发送起始信号主机发送器件地址读位0x71等待AHT10应答读取状态字8位这是关键状态字的第7位bit7表示忙闲状态1忙0闲。我们需要循环读取直到bit7为0。主机发送应答ACK告诉AHT10“我收到了请继续发下一个字节。”连续读取6个数据字节字节1湿度数据的高8位字节2湿度数据的低8位字节3湿度数据最低4位 温度数据最高4位字节4温度数据的中间8位字节5温度数据的低8位主机在读完最后一个字节后发送非应答NACK然后发送停止信号。这个过程听起来复杂但其实就是一套固定的“打招呼-发命令-等回应-读数据”的流程。下面咱们看看具体的代码是怎么实现这些时序的。4. 手把手代码移植与解析拿到一个驱动代码最好的学习方式就是把它移植到自己的工程里然后一行行理解。咱们就按照这个方法来。4.1 建立工程文件首先在你的工程目录下比如User或BSP文件夹创建两个文件bsp_aht10.cbsp_aht10.h把原始文章里提供的代码分别复制进去。先别急着编译咱们一起来看看这些代码都干了啥。4.2 关键代码逐行解析打开bsp_aht10.h头文件这里主要做了几件事// 1. 引脚宏定义 #define RCC_AHT10 RCC_AHB1Periph_GPIOB #define PORT_AHT10 GPIOB #define GPIO_SDA GPIO_Pin_9 #define GPIO_SCL GPIO_Pin_8 // 2. 方便的宏函数 #define SDA_OUT() { /* 将SDA引脚配置为输出模式的代码 */ } #define SDA_IN() { /* 将SDA引脚配置为输入模式的代码 */ } #define SDA_GET() GPIO_ReadInputDataBit(PORT_AHT10, GPIO_SDA) // 读取SDA电平 #define SDA(x) GPIO_WriteBit(PORT_AHT10, GPIO_SDA, (x?Bit_SET:Bit_RESET)) // 设置SDA输出 #define SCL(x) GPIO_WriteBit(PORT_AHT10, GPIO_SCL, (x?Bit_SET:Bit_RESET)) // 设置SCL输出这些宏定义让后续的代码写起来更简洁。注意这里把I2C的引脚固定在了PB8和PB9。如果你的开发板把这组引脚用作其他用途或者想换到其他I2C接口比如I2C1是PB6、PB7只需要修改这里的宏定义就行了。再来看看bsp_aht10.c里的几个核心函数I2C基础时序函数这些函数模拟了I2C总线最底层的信号起始、停止、发送字节、读取字节、应答等待。这些都是标准操作几乎所有的I2C设备驱动都会用到。void IIC_Start(void) { SDA_OUT(); SDA(1); SCL(1); delay_us(4); // 起始条件SCL高电平时SDA从高变低 SDA(0); delay_us(4); SCL(0); }起始信号就像打电话时先拿起听筒告诉总线上的所有设备“注意我要开始通信了”传感器初始化函数AHT10_GPIO_Init这个函数不仅初始化了GPIO引脚还顺带发送了一个初始化校准命令0xE1 0x08 0x00给AHT10。这是确保传感器能正常工作的关键一步别忘了。核心数据读取函数AHT10_Read这是整个驱动的“大脑”它完整实现了我们上面讲的那套通信流程。我挑几个重点说说发送测量命令代码里先发送0xAC, 0x33, 0x00来触发测量。等待测量完成do { delay_ms(1); timeout; IIC_Start(); Send_Byte(0X381 | 1); // 发送读地址 } while( I2C_WaitAck() 1 timeout 5 );这里用了一个do...while循环不断尝试读取直到AHT10应答表示不忙了或者超时5ms。I2C_WaitAck() 1表示没有收到应答忙 0表示收到应答闲。读取并解析数据连续读取6个字节然后按照数据手册的格式拼合成20位的湿度数据和20位的温度数据。数据换算这是最容易出错的地方。AHT10输出的原始数据是20位的需要按公式换算成我们熟悉的温湿度值。// 湿度换算RH(%) (SRH / 2^20) * 100 dat (((buff[1]12) | (buff[2]4)) | (buff[3]4)); Humidity dat / 1048576.0 * 100.0; // 2^20 1048576 // 温度换算T(℃) (ST / 2^20) * 200 - 50 dat 0; dat ((buff[3] 0x0F) 16) | (buff[4] 8) | buff[5]; Temperature (dat / 1048576.0) * 200.0 - 50.0;注意buff[3]这个字节它高4位是湿度数据的最后4位低4位是温度数据的最高4位。所以拆分的时候要用(buff[3]4)取高4位给湿度用(buff[3] 0x0F)取低4位给温度。5. 在main函数中调用与测试驱动写好了最后就是在主函数里调用它。这个过程很简单就是一个标准的初始化-循环采集-打印输出的流程。#include board.h #include bsp_uart.h #include stdio.h #include bsp_aht10.h int main(void) { board_init(); // 开发板基础初始化时钟、系统滴答等 uart1_init(115200U); // 初始化串口1波特率115200用于打印数据 AHT10_GPIO_Init(); // 初始化AHT10的GPIO和传感器本身 printf(AHT10 Test Start!\r\n); while(1) { // 1. 采集温湿度数据 AHT10_Read(); // 2. 获取并打印温度数据 printf(Temperature %.2f C\r\n, Get_Temperature()); // 3. 获取并打印湿度数据 printf(Humidity %.2f %%RH\r\n, Get_Humidity()); printf(\r\n); // 打印空行分隔每次数据 delay_ms(1000); // 延时1秒然后进行下一次采集 } }把代码编译下载到天空星开发板打开串口助手波特率设为115200你就能看到每隔1秒输出一次的温湿度数据了。6. 调试中可能遇到的坑与解决办法第一次移植不成功很正常我当年也踩过不少坑。这里分享几个常见的没有数据输出或者全是0检查硬件连接这是第一要务确保VCC、GND、SDA、SCL四根线都接对了没有虚焊。用万用表量一下VCC是不是3.3V。检查上拉电阻如果模块本身没有上拉电阻一定要在SDA和SCL上各接一个4.7kΩ的电阻到3.3V。检查引脚冲突确认你用的PB8、PB9没有在其他地方被复用比如用作JTAG引脚。在天空星开发板上通常PB8、PB9是安全的。数据明显不对或者温度湿度值固定不变检查I2C地址确认发送的器件地址是0x70写和0x71读。可以用逻辑分析仪或者示波器抓一下I2C波形看看第一个字节对不对。检查时序延时代码里的delay_us(2)、delay_us(4)这些微小延时很重要太快或太慢都可能导致通信失败。如果用的是不同的主频可能需要调整这些延时。检查数据解析重点核对buff[3]这个字节的拆分计算是否正确。湿度取高4位温度取低4位别弄反了。只能读取一次第二次就失败了查看AHT10_Read()函数末尾它调用了AHT10Reset()和AHT10_GPIO_Init()。这个设计是为了每次读取后复位传感器并重新初始化。确保这两个函数执行成功。如果以上都检查了还是不行那就祭出终极武器——逻辑分析仪。把它接到SDA和SCL线上抓取实际的通信波形和上面讲的时序图一个一个对比哪里对不上问题就出在哪里。好了关于AHT10在STM32F407上的驱动移植和采集核心内容就是这些。整个过程走下来你会发现I2C设备驱动其实有固定的套路理解时序、编写底层信号函数、组合成完整操作、解析数据。掌握了这个套路以后再遇到其他I2C传感器比如气压计、光照传感器你就能举一反三快速上手了。