核心目标:掌握 I2C 通信原理,驱动真实 I2C 温湿度传感器(AHT10),整合前 7 天知识点(GPIO、定时器、串口、中断、函数封装),实现 “定时采集温湿度 + 串口上传数据 + 电脑指令控制采集频率” 的完整嵌入式系统 —— 这是从 “零散知识点” 到 “实际应用” 的关键跨越。
一、I2C 通信原理:嵌入式 “多设备互联” 的常用接口(30 分钟)
I2C(Inter-Integrated Circuit)是两线制同步通信接口,仅需 SCL(时钟线)和 SDA(数据线)就能实现多设备通信,核心优势是 “布线简单、支持多从机”,广泛用于传感器、EEPROM 等外设。
1. 核心概念(新手必懂)
- 通信线:SCL(Serial Clock,时钟线,主机输出时钟)、SDA(Serial Data,数据线,双向传输数据);
- 主从结构:STM32 作为 “主机”,传感器(AHT10)作为 “从机”,主机控制通信节奏;
- 从机地址:每个 I2C 从机有唯一地址(AHT10 默认地址 0x38),主机通过地址识别要通信的设备;
- STM32 引脚对应(F103C8T6):I2C1 的 SCL=PB6,SDA=PB7(需配置为 I2C 功能,开启上拉电阻)。
2. HAL 库 I2C 核心函数(重点记)
- 主机发送数据:
HAL_I2C_Master_Transmit(&hi2c1, 从机地址, 发送缓冲区, 数据长度, 超时时间); - 主机接收数据:
HAL_I2C_Master_Receive(&hi2c1, 从机地址, 接收缓冲区, 数据长度, 超时时间); - 传感器初始化:通过 I2C 发送 “初始化指令”,让传感器进入工作模式(不同传感器指令不同,参考 datasheet)。
3. 嵌入式场景意义
I2C 是传感器通信的 “主流接口”(比 SPI 布线少,比单总线稳定),学会 I2C 驱动后,可快速适配大多数温湿度、光照、气压传感器,是嵌入式外设开发的核心技能。
二、硬件准备(实操基础)
- 核心硬件:STM32F103C8T6 开发板、AHT10 温湿度传感器模块、USB-TTL 模块、杜邦线;
- 接线方式(I2C1 通信):
- AHT10 VCC → 开发板 3.3V(务必接 3.3V,接 5V 会烧毁传感器);
- AHT10 GND → 开发板 GND;
- AHT10 SCL → 开发板 PB6(I2C1_SCL);
- AHT10 SDA → 开发板 PB7(I2C1_SDA);
- 串口接线(同第七天):开发板 PA9→TTL Tx,PA10→TTL Rx,GND→GND(用于数据上传和指令接收)。
三、实操:完整温湿度采集系统(70 分钟,核心整合)
目标:开发板通过 I2C 驱动 AHT10 采集温湿度,1 秒采集一次(定时器控制),串口上传数据到电脑;电脑发送指令 “F5”(500ms 采集一次)、“F10”(1 秒)、“F20”(2 秒),可修改采集频率。
步骤 1:CubeIDE 配置(I2C + 定时器 + 串口 + GPIO)
- 新建工程 “AHT10_TempHumi_System”,选择 STM32F103C8T6;
- 配置 I2C1:
- 左侧 “Connectivity→I2C1”:Mode 选 “I2C”;
- 参数默认:时钟频率 100kHz(标准模式),其他保持默认;
- 配置引脚:PB6 设为 “I2C1_SCL”,PB7 设为 “I2C1_SDA”(CubeIDE 自动开启上拉电阻);
- 配置串口 USART1(同第七天):异步模式,波特率 9600,PA9=TX,PA10=RX;
- 配置定时器 TIM3(定时核心):
- 预分频器 7199、自动重载值 9999(默认 1 秒中断一次,后续通过指令修改 ARR 实现频率切换);
- 使能 TIM3 中断;
- 生成初始化代码。
步骤 2:编写 AHT10 传感器驱动(封装读取逻辑)
在main.c的 “USER CODE BEGIN 0” 区域添加传感器驱动函数(基于 AHT10 datasheet 指令):
c
/* USER CODE BEGIN 0 */
#include <string.h>
// AHT10传感器地址(默认0x38,7位地址)
#define AHT10_ADDR 0x38<<1 // I2C通信时需左移1位(8位数据格式,最低位是读写位)
// AHT10初始化指令(参考datasheet)
#define AHT10_INIT_CMD 0xE1 // 初始化+正常模式
// AHT10采集指令
#define AHT10_MEASURE_CMD 0xAC // 触发温湿度采集
// 全局变量:存储温湿度数据
float temperature = 0.0; // 温度(℃)
float humidity = 0.0; // 湿度(%RH)
// 函数1:AHT10初始化
uint8_t AHT10_Init(I2C_HandleTypeDef *hi2c) {
uint8_t init_buf[3] = {AHT10_INIT_CMD, 0x00, 0x00}; // 初始化指令+参数
// 发送初始化指令(主机→AHT10)
if (HAL_I2C_Master_Transmit(hi2c, AHT10_ADDR, init_buf, 3, 100) != HAL_OK) {
return 1; // 初始化失败
}
HAL_Delay(10); // 传感器初始化需要时间
return 0; // 初始化成功
}
// 函数2:读取AHT10温湿度数据
uint8_t AHT10_ReadData(I2C_HandleTypeDef *hi2c) {
uint8_t measure_buf[3] = {AHT10_MEASURE_CMD, 0x33, 0x00}; // 采集指令+参数
uint8_t recv_buf[6] = {0}; // 接收缓冲区(AHT10返回6字节数据)
// 1. 发送采集指令
if (HAL_I2C_Master_Transmit(hi2c, AHT10_ADDR, measure_buf, 3, 100) != HAL_OK) {
return 1; // 指令发送失败
}
HAL_Delay(80); // 传感器采集需要时间( datasheet 要求≥75ms)
// 2. 接收采集结果(6字节数据)
if (HAL_I2C_Master_Receive(hi2c, AHT10_ADDR, recv_buf, 6, 100) != HAL_OK) {
return 1; // 数据接收失败
}
// 3. 解析数据(根据AHT10 datasheet的格式换算)
// 湿度数据:recv_buf[1]<<12 | recv_buf[2]<<4 | (recv_buf[3]&0xF0)>>4
uint32_t humi_raw = (recv_buf[1] << 12) | (recv_buf[2] << 4) | ((recv_buf[3] & 0xF0) >> 4);
humidity = (humi_raw / 1048576.0) * 100.0; // 换算为湿度(0-100%RH)
// 温度数据:(recv_buf[3]&0x0F)<<16 | recv_buf[4]<<8 | recv_buf[5]
uint32_t temp_raw = ((recv_buf[3] & 0x0F) << 16) | (recv_buf[4] << 8) | recv_buf[5];
temperature = (temp_raw / 1048576.0) * 200.0 - 50.0; // 换算为温度(-40~85℃)
return 0; // 读取成功
}
/* USER CODE END 0 */
步骤 3:编写系统核心逻辑(定时采集 + 串口上传 + 指令控制)
- 定义全局变量(
main.c的 “USER CODE BEGIN 2” 区域):
c
/* USER CODE BEGIN 2 */
uint32_t timer_count = 0; // 定时器计数
uint16_t arr_val = 9999; // TIM3自动重载值(默认1秒:(7199+1)*(9999+1)/72e6=1s)
uint8_t recv_buf[10] = {0}; // 串口接收缓冲区(存储电脑指令)
uint8_t send_buf[60] = {0}; // 串口发送缓冲区
/* USER CODE END 2 */
- 主函数初始化(
main函数中):
c
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART1_UART_Init();
MX_TIM3_Init();
/* USER CODE BEGIN 2 */
// 1. 初始化AHT10传感器
if (AHT10_Init(&hi2c1) == 0) {
sprintf((char*)send_buf, "AHT10传感器初始化成功!\r\n");
HAL_UART_Transmit(&huart1, send_buf, strlen((char*)send_buf), 100);
} else {
sprintf((char*)send_buf, "AHT10传感器初始化失败!\r\n");
HAL_UART_Transmit(&huart1, send_buf, strlen((char*)send_buf), 100);
}
// 2. 启动定时器中断和串口中断接收
HAL_TIM_Base_Start_IT(&htim3);
HAL_UART_Receive_IT(&huart1, recv_buf, 3); // 接收3字节指令(如F5\r、F10\r)
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
- 定时器中断回调(定时采集 + 串口上传):
c
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM3) {
// 1. 读取温湿度数据
if (AHT10_ReadData(&hi2c1) == 0) {
// 2. 格式化数据并串口上传
sprintf((char*)send_buf, "温湿度数据:温度=%.1f℃,湿度=%.1f%%RH\r\n", temperature, humidity);
HAL_UART_Transmit(&huart1, send_buf, strlen((char*)send_buf), 100);
} else {
sprintf((char*)send_buf, "温湿度读取失败!\r\n");
HAL_UART_Transmit(&huart1, send_buf, strlen((char*)send_buf), 100);
}
}
}
- 串口中断回调(解析电脑指令,修改采集频率):
c
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
// 解析指令:F5→500ms,F10→1s,F20→2s
if (strcmp((char*)recv_buf, "F5\r") == 0) {
arr_val = 4999; // 500ms:(7199+1)*(4999+1)/72e6=0.5s
sprintf((char*)send_buf, "采集频率已修改为500ms一次\r\n");
} else if (strcmp((char*)recv_buf, "F10\r") == 0) {
arr_val = 9999; // 1s
sprintf((char*)send_buf, "采集频率已修改为1s一次\r\n");
} else if (strcmp((char*)recv_buf, "F20\r") == 0) {
arr_val = 19999; // 2s:(7199+1)*(19999+1)/72e6=2s
sprintf((char*)send_buf, "采集频率已修改为2s一次\r\n");
} else {
sprintf((char*)send_buf, "指令错误!支持指令:F5(500ms)、F10(1s)、F20(2s)\r\n");
}
// 应用新的自动重载值(修改定时器定时时长)
__HAL_TIM_SET_AUTORELOAD(&htim3, arr_val);
// 发送反馈信息
HAL_UART_Transmit(&huart1, send_buf, strlen((char*)send_buf), 100);
// 清空接收缓冲区,重新开启中断接收
memset(recv_buf, 0, sizeof(recv_buf));
HAL_UART_Receive_IT(&huart1, recv_buf, 3);
}
}
步骤 4:下载测试(完整系统验证)
- 电脑端打开串口助手,配置:波特率 9600、8N1、对应 COM 口;
- 下载程序后,串口助手收到初始化信息:
AHT10传感器初始化成功!; - 默认 1 秒收到一条温湿度数据:
温湿度数据:温度=25.3℃,湿度=45.2%RH; - 发送指令 “F5”(回车),收到反馈:
采集频率已修改为500ms一次,数据上传频率加快; - 发送指令 “F20”(回车),反馈:
采集频率已修改为2s一次,数据上传频率变慢。
四、第八天必掌握的 3 个关键知识点(10 分钟自测)
- I2C 通信核心:两线制(SCL/SDA)、主从结构、从机地址,HAL 库核心函数
HAL_I2C_Master_Transmit/HAL_I2C_Master_Receive; - 传感器驱动逻辑:“发送指令→等待采集→接收数据→解析换算”,必须参考 datasheet 的指令和数据格式;
- 系统整合思路:定时器控制采集周期、I2C 读取传感器、串口实现数据上传和指令交互,各模块通过全局变量和回调函数联动。
总结
今天完成了第一个 “完整嵌入式应用系统”!这个系统整合了前 8 天的所有核心知识点:GPIO(硬件接线)、I2C(传感器通信)、定时器(定时任务)、串口(双向交互)、中断(回调响应)、函数封装(传感器驱动)—— 这就是实际嵌入式项目的典型架构。


被折叠的 条评论
为什么被折叠?



