AHT10温湿度传感器I2C驱动移植与数据采集实战(基于立创开发板)

news2026/3/17 12:17:22
AHT10温湿度传感器I2C驱动移植与数据采集实战基于立创开发板最近在做一个环境监测的小项目需要用到温湿度传感器。选来选去最终敲定了AHT10这款传感器。它体积小、精度高关键是采用I2C接口接线简单非常适合咱们嵌入式开发。今天我就以立创开发板主控为瑞萨R7FA6E2BB3CNE为例手把手带你从零开始把AHT10的驱动移植到你的工程里并成功读取到温湿度数据。整个过程我会拆解成几个清晰的步骤先了解传感器再分析通信协议接着配置硬件引脚然后编写底层I2C时序模拟代码最后完成数据读取和换算。即使你是第一次接触I2C或者AHT10跟着做下来也一定能搞定。1. 认识AHT10你的数字环境感知器在动手写代码之前咱们先花几分钟了解一下AHT10到底是个啥。这能帮你更好地理解后续的通信流程。AHT10是一款集成了温度和湿度测量功能的数字传感器。它内部有一个专门处理信号的芯片ASIC、一个电容式的湿度传感元件和一个温度传感元件。它最大的好处是直接输出校准好的数字信号咱们的MCU通过I2C总线读取就行省去了模拟信号放大、AD转换的麻烦。它的几个关键参数在选型和设计电路时很重要工作电压1.8V ~ 3.6V。这意味着它可以直接用咱们开发板的3.3V供电非常方便。测量范围与精度湿度测量范围0-100%RH典型误差±2%RH。温度测量范围-40℃ ~ 85℃典型误差±0.3℃。通信接口标准I2C。这是我们今天重点要搞定的部分。封装非常小巧只有4mm x 5mm适合集成到各种产品中。简单来说AHT10就是一个通过I2C“报数”的传感器我们MCU的任务就是按照正确的“暗号”协议去问它然后听懂它回复的“数字密码”原始数据并翻译成我们能理解的温度和湿度值。2. 硬件连接与引脚配置硬件连接是第一步也是最简单的一步。AHT10模块通常只有三个引脚VCC, GND, SDA, SCL我们需要把它正确地接到开发板上。根据原始资料连接关系如下AHT10模块引脚立创开发板对应引脚VCC3V3GNDGNDSCLP408SDAP409注意这里使用的是软件模拟I2C即用两个普通的GPIO口来模拟I2C的时序所以SCL和SDA可以接到任意两个空闲的GPIO上不一定是硬件I2C外设专用的引脚。我们例子中用的是P408和P409。接下来我们需要在开发环境里配置这两个引脚。如果你使用的是立创开发板配套的图形化配置工具如RASC操作步骤如下打开引脚配置界面。找到P408和P409这两个引脚。将它们都配置为**通用输出Output**模式。因为模拟I2C需要MCU主动控制引脚输出高低电平。记得按下Ctrl S保存配置。点击生成代码按钮将配置更新到你的工程中。这样硬件和底层引脚初始化就准备好了。3. 编写驱动从I2C时序到数据解析这是最核心的部分。我们将创建两个文件bsp_aht10.c和bsp_aht10.h把所有的驱动代码都放在里面。3.1 头文件定义与宏首先来看头文件bsp_aht10.h。这里主要定义了一些类型别名、延时宏、引脚宏以及最重要的——引脚操作宏。#ifndef BSP_CODE_BSP_AHT10_H_ #define BSP_CODE_BSP_AHT10_H_ #include hal_data.h #include stdio.h // 一些常用的类型定义让代码更简洁 #ifndef u8 #define u8 uint8_t #endif #ifndef u16 #define u16 uint16_t #endif #ifndef u32 #define u32 uint32_t #endif // 延时函数宏直接调用开发板提供的软件延时函数 #ifndef delay_ms #define delay_ms(x) R_BSP_SoftwareDelay(x, BSP_DELAY_UNITS_MILLISECONDS) #endif #ifndef delay_us #define delay_us(x) R_BSP_SoftwareDelay(x, BSP_DELAY_UNITS_MICROSECONDS) #endif // 定义我们使用的SCL和SDA引脚 #define Module_SCL_PIN BSP_IO_PORT_04_PIN_08 // SCL 对应 P408 #define Module_SDA_PIN BSP_IO_PORT_04_PIN_09 // SDA 对应 P409 // 关键SDA引脚方向控制宏 // SDA在通信过程中需要在输出主机发送和输入主机接收模式间切换 #define SDA_IN() { \ fsp_err_t err R_IOPORT_PinCfg(g_ioport_ctrl, Module_SDA_PIN, \ (uint32_t) IOPORT_CFG_PORT_DIRECTION_INPUT); \ if(err ! FSP_SUCCESS) { \ printf(SDA设置为输入模式失败!\r\n); \ } \ } #define SDA_OUT() { \ fsp_err_t err R_IOPORT_PinCfg(g_ioport_ctrl, Module_SDA_PIN, \ ((uint32_t) IOPORT_CFG_DRIVE_HIGH \ | (uint32_t) IOPORT_CFG_NMOS_ENABLE \ | (uint32_t) IOPORT_CFG_PORT_DIRECTION_OUTPUT \ | (uint32_t) IOPORT_CFG_PORT_OUTPUT_HIGH)); \ if(err ! FSP_SUCCESS) { \ printf(SDA设置为输出模式失败!\r\n); \ } \ } // SCL和SDA引脚输出电平控制 #define SCL(BIT) R_IOPORT_PinWrite(g_ioport_ctrl, Module_SCL_PIN, BIT) #define SDA(BIT) R_IOPORT_PinWrite(g_ioport_ctrl, Module_SDA_PIN, BIT) // 读取SDA引脚输入电平 static inline bsp_io_level_t SDA_GET(void) { bsp_io_level_t p_pin_value; fsp_err_t err R_IOPORT_PinRead(g_ioport_ctrl, Module_SDA_PIN, p_pin_value); if(err ! FSP_SUCCESS) { printf(读取SDA引脚电平失败!\r\n); } return p_pin_value; } // 对外提供的函数声明 void AHT10_Init(void); uint8_t AHT10_Get_TempHumi(float *Temperature, float *Humidity); #endif /* BSP_CODE_BSP_AHT10_H_ */这里需要特别关注SDA_IN()和SDA_OUT()这两个宏。因为I2C协议中SDA线是双向的主机在发送数据时要控制它输出在接收数据时要把它设置为输入以读取从机电平。很多初学者调试不通问题就出在忘了切换SDA的方向。3.2 模拟I2C底层时序I2C通信有一套严格的时序规则就像两个人对话要遵守的礼节。我们用GPIO模拟就要用代码精确地再现这些时序。这些函数是驱动的基础写在bsp_aht10.c里。起始信号StartSCL为高电平时SDA产生一个下降沿。这告诉总线上的所有设备“主机要开始通信了”。static void IIC_Start(void) { SDA_OUT(); // 先确保SDA是输出模式 SDA(1); SCL(1); delay_us(4); // 保持一段时间确保信号稳定 SDA(0); // 在SCL高电平期间拉低SDA产生起始条件 delay_us(4); SCL(0); // 拉低SCL为后续发送数据位做准备 }停止信号StopSCL为高电平时SDA产生一个上升沿。表示“本次通信结束”。static void IIC_Stop(void) { SDA_OUT(); SCL(0); SDA(0); delay_us(4); SCL(1); // 先将SCL拉高 SDA(1); // 然后在SCL高电平期间拉高SDA产生停止条件 delay_us(4); }发送一个字节数据在SCL低电平期间变化在SCL高电平期间保持稳定从最高位MSB开始发送。static void Send_Byte(uint8_t dat) { int i 0; SDA_OUT(); SCL(0); // 拉低时钟开始数据传输 for( i 0; i 8; i ) { // 取出最高位判断是1还是0然后设置SDA电平 SDA( (dat 0x80) 7 ); delay_us(1); SCL(1); // 拉高SCL从机在此时刻采样SDA数据 delay_us(2); SCL(0); // 拉低SCL为发送下一位做准备 delay_us(2); dat1; // 数据左移准备发送下一位 } }接收一个字节主机控制SCL产生时钟在SCL高电平期间读取SDA线上的数据。static uint8_t Read_Byte(void) { unsigned char i, receive0; SDA_IN(); // 非常重要接收前要把SDA设置为输入模式 for(i0;i8;i ) { SCL(0); delay_us(2); SCL(1); // 拉高SCL此时从机将数据放到SDA线上 delay_us(2); receive1; // 左移为接收新数据位腾出空间 if( SDA_GET() ) // 读取SDA电平 { receive|1; // 如果为高电平则该位为1 } delay_us(1); } SCL(0); return receive; }应答ACK与等待应答I2C协议规定接收方在收到一个字节后需要发送一个应答信号ACK拉低SDA。I2C_WaitAck函数就是主机发送完地址或命令后等待从机AHT10回应的过程。static unsigned char I2C_WaitAck(void) { char ack 0; unsigned char ack_flag 10; // 超时计数 SDA(1); delay_us(1); SCL(1); delay_us(1); SDA_IN(); // 切换为输入准备读取从机的应答信号 delay_us(2); // 循环等待SDA被从机拉低应答 while( (SDA_GET()1) ( ack_flag ) ) { ack_flag--; delay_us(3); } if( ack_flag 0 ) // 超时未收到应答 { IIC_Stop(); return 1; // 返回错误 } else { SCL(0); SDA_OUT(); // 等待完毕切回输出模式 } SDA(0); return ack; // 返回0表示成功收到应答 }把这些底层时序函数理解透彻I2C通信就掌握了一大半。它们就像乐高积木后面复杂的读写操作都是由它们组合而成的。3.3 AHT10的初始化与数据读取有了基础的I2C通信能力我们就可以和AHT10“对话”了。首先需要初始化传感器。器件地址AHT10的固定I2C地址是0x38。但在I2C协议中实际发送的地址字节是7位地址加上1位读写方向位。所以我们需要将0x38左移一位得到0x70。当最后一位为0时表示写为1时表示读。初始化函数主要是发送一个校准命令让传感器做好测量准备。void AHT10_Init(void) { // 先拉高SCL和SDA让总线处于空闲状态 SCL(1); SDA(1); delay_ms(100); // 上电后等待传感器稳定 // 发送初始化校准命令序列0xE1, 0x08, 0x00 IIC_Start(); Send_Byte(0x70); // 器件地址 写 I2C_WaitAck(); Send_Byte(0xe1); // 初始化命令 I2C_WaitAck(); Send_Byte(0x08); I2C_WaitAck(); Send_Byte(0x00); I2C_WaitAck(); IIC_Stop(); delay_ms(50); // 等待校准完成 }核心数据读取函数这个过程稍复杂我结合代码和注释一步步说。static uint8_t AHT10_Read(float *Temperature, float *Humidity) { char timeout 0; unsigned char buff[6] {0}; // 用于存放读取到的6个字节原始数据 // 1. 启动测量 IIC_Start(); Send_Byte(0X381 | 0); // 发送器件地址写命令 (0x70) I2C_WaitAck(); Send_Byte(0XAC); // 发送触发测量命令 I2C_WaitAck(); Send_Byte(0X33); // 命令参数1 I2C_WaitAck(); Send_Byte(0X00); // 命令参数2 I2C_WaitAck(); IIC_Stop(); // 发送完启动命令先停止 // 2. 等待测量完成AHT10需要时间进行测量 do{ delay_ms(1); timeout; // 重新发起通信尝试读取状态字 IIC_Start(); Send_Byte(0X381 | 1); // 器件地址读命令 }while( I2C_WaitAck() 1 timeout 5 ); // 如果设备忙无应答则重试 if(timeout 5) // 超时 return 1; // 3. 连续读取6个字节数据 buff[0] Read_Byte(); // 第1字节状态字 IIC_Send_Ack(0); // 主机发送应答(ACK) buff[1] Read_Byte(); // 第2字节湿度高8位 IIC_Send_Ack(0); buff[2] Read_Byte(); // 第3字节湿度低8位 IIC_Send_Ack(0); buff[3] Read_Byte(); // 第4字节湿度低4位 温度高4位 IIC_Send_Ack(0); buff[4] Read_Byte(); // 第5字节温度中8位 IIC_Send_Ack(0); buff[5] Read_Byte(); // 第6字节温度低8位 IIC_Send_Ack(1); // 最后一个字节主机发送非应答(NACK)通知从机停止发送 IIC_Stop(); delay_ms(20); // 4. 数据换算这是关键 // AHT10的湿度数据是20位的存储在buff[1], buff[2], buff[3]的高4位 uint32_t humidity_raw ( (uint32_t)buff[1] 12 ) | ( (uint32_t)buff[2] 4 ) | ( buff[3] 4 ); // 换算公式湿度(%) (原始值 / 2^20) * 100 *Humidity humidity_raw / 1048576.0f * 100.0f; // 2^20 1048576 // AHT10的温度数据也是20位的存储在buff[3]的低4位, buff[4], buff[5] uint32_t temp_raw ( (uint32_t)(buff[3] 0x0F) 16 ) | ( (uint32_t)buff[4] 8 ) | buff[5]; // 换算公式温度(℃) (原始值 / 2^20) * 200 - 50 *Temperature (temp_raw / 1048576.0f) * 200.0f - 50.0f; // 5. 可选进行一次软复位为下次测量做准备 AHT10Reset(); AHT10_Init(); return 0; }数据换算部分是理解传感器输出的核心。AHT10将温湿度模拟量转换成了一个20位的数字量。公式虽然看起来有点复杂但记住1048576就是2的20次方代表满量程。湿度的范围是0-100%所以乘以100温度的量程是-50~150℃跨度200℃所以先按200倍缩放再减去50的偏移。最后我们封装一个更友好的函数供主程序调用uint8_t AHT10_Get_TempHumi(float *Temperature, float *Humidity) { float Temp_Temperature 0; float Temp_Humidity 0; if(AHT10_Read(Temp_Temperature, Temp_Humidity) ! 0) { printf(AHT10 读取错误!!\r\n); return 1; } if(Temperature ! NULL) { *Temperature Temp_Temperature; } if(Humidity ! NULL) { *Humidity Temp_Humidity; } return 0; }4. 实战验证在应用中读取数据驱动写好了最后一步就是把它用起来。在你的主应用程序文件例如app.c中包含头文件初始化传感器然后在循环中读取即可。#include bsp_aht10.h #include bsp_uart.h // 用于printf打印 #include stdio.h void Run(void) { // 初始化调试串口方便打印数据 UART0_Debug_Init(); printf(系统启动...\r\n); // 初始化AHT10传感器 AHT10_Init(); printf(AHT10 初始化成功\r\n); while(1) { float Temperature 0; float Humidity 0; // 调用函数获取温湿度 if(!AHT10_Get_TempHumi(Temperature, Humidity)) { printf(温度 %.2f ℃\r\n, Temperature); printf(湿度 %.2f %%\r\n, Humidity); } else { printf(读取温湿度失败\r\n); } delay_ms(1000); // 每秒读取一次 } }编译、下载程序到开发板打开串口调试助手波特率根据你的bsp_uart初始化配置而定你应该能看到每秒打印一次的温湿度数据了。5. 常见问题与调试心得第一次移植很可能不会一帆风顺这里分享几个我踩过的坑没有应答ACK这是最常见的问题。首先检查硬件连接VCC、GND是否接对SCL、SDA是否接反上拉电阻是否加上模块一般自带如果没有需要在SDA和SCL线上各接一个4.7kΩ上拉到3.3V。其次检查代码SDA_IN()和SDA_OUT()切换是否正确延时时间是否太短AHT10的地址0x70是否发送正确读取的数据全是0或255检查数据读取阶段的ACK/NACK发送顺序是否正确。读取最后一个字节后必须发送NACK然后发送停止信号。另外检查Read_Byte函数中SDA_IN()是否在循环前正确调用。数据值明显不对大概率是数据换算公式用错了。务必确认你读取的6个字节buff[0]到buff[5]分别对应什么数据并严格按照AHT10数据手册的公式计算。本文使用的公式是经过验证的。时序问题I2C对时序有一定要求但AHT10不算特别严格。如果通信不稳定可以适当增加delay_us中的延时参数尤其是SCL高电平期间的延时。整个移植过程其实就是理解协议、搭建底层、组装逻辑的过程。当你看到串口里稳定输出房间的温湿度时那种成就感就是嵌入式开发的乐趣所在。希望这篇教程能帮你顺利搞定AHT10如果你在移植中遇到其他问题不妨多看看时序波形用逻辑分析仪抓一下SDA和SCL的线和数据手册那才是解决问题的终极法宝。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2419375.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…