Keil转STM32CubeIDE笔记五
对I2C进行基本的学习认识,并学会通过查阅数据手册来配置AHT20温湿度传感器来测量室内的温湿度,并结合蓝牙通信,将读取到的温湿度发送给移动端。
I2C通信应用——温湿度传感器AHT20读取室内温湿度
一,实验准备阶段
1,实验目标
对I2C进行基本的学习认识,并学会通过查阅数据手册来配置AHT20温湿度传感器来测量室内的温湿度,并结合蓝牙通信,将读取到的温湿度发送给移动端。
2,I2C的基本认识
I2C 是一种串行、多主从设备的通信协议,广泛用于单片机与传感器、EEPROM、显示屏等外设的短距离通信。以下是其核心机制与实战要点:
一、物理层特性
-
双线制总线
- SCL(时钟线):主机生成时钟,控制数据传输节奏。
- SDA(数据线):双向传输数据,高电平表示 1,低电平表示 0。
- 上拉电阻:SCL/SDA 需外接上拉(4.7kΩ~10kΩ),确保总线空闲时为高电平(如 STM32 的 PB6/PB7 默认开漏输出,需外部上拉)。
-
设备寻址
- 7 位地址(主流):共 128 个地址(0x00~0x7F),其中 0x00 为广播地址,0x78~0x7F 保留。
▶ 代码中需左移 1 位(如设备地址 0x48 → 发送 0x90(写)/0x91(读))。 - 10 位地址:扩展寻址范围,较少用。
- 7 位地址(主流):共 128 个地址(0x00~0x7F),其中 0x00 为广播地址,0x78~0x7F 保留。
二、协议层核心时序
1. 起始 / 停止条件
- Start:SCL 高电平时,SDA 由高→低(主机发起通信)。
- Stop:SCL 高电平时,SDA 由低→高(主机结束通信)。
plaintext
SCL: ______ ______ ______ | | | | | | SDA: __|____|____|____|____|____|__ Start→Data→Stop
2. 数据传输
- 字节格式:8 位数据 + 1 位 ACK(从机响应)。
▶ 高位在前,每个字节后需从机返回 ACK(低电平),否则视为失败(如 STM32 的HAL_I2C_Mem_Read返回HAL_TIMEOUT)。 - 读 / 写操作:
- 写流程:Start → 设备地址(W) → 寄存器地址 → 数据 → Stop。
- 读流程:Start → 设备地址(W) → 寄存器地址 → Start → 设备地址(R) → 数据 → NACK → Stop。
二,初始化配置
1,新建工程
点击File,选择STM32 Project,芯片依旧选择经典的STM32F103C8T6型号,工程名为I2C_AHT20。
、

2,引脚选择配置
本期项目需要用到I2C通信和串口通信,所以只需要打开对应的引脚即可。
STM32F103C8T6中一共有两对I2C和三对串口,在这里我选择的是I2C1和USART2,对应的引脚分别是PB6(SCL)PB7(SDA)和PA2(TX)PA3(RX),当然SWD调试端口还是一样要打开。配置好后就是这个样子

3,I2C1及USART2参数配置
单击左边的connectivity,双击我们选择的I2C1,并将模式选择为I2C

之后为I2C1开启中断和DMA请求。
中断有I2C事件中断和I2C错误中断两种,我们全部开启即可,参数保持默认。

然后为I2C的发送和接收都添加上DMA请求,参数保持默认。

开启中断和DMA的目的是利用DMA来传输数据,减轻CPU的负担,防止系统堵塞。
最后就是配置USART2的模式及参数,这里依旧是选择异步传输模式,下面的参数中,只需要将波特率更改为9600Bits/s,其余参数皆保持不变。

4,保存配置
全部配置好后,点击左上方的Save保存配置,然后等待CubeMX为我们生成好初始化代码。
三,代码实现部分
1,项目基本思路
在我们配置AHT20传感器时,会分为很多步骤,为了使项目整体逻辑分明,环环相扣,所以我们可以引入状态机思想,人话就是,可以定义一个变量,当变量为不同的值时,需要做出不同的行为,这样也便于我们对应数据手册的读取流程书写代码。
代码的思路是非常简单的,基本就是分为三步
- 根据数据手册读取传感器
- 将读取传感器的步骤与状态机一一对应
- 将读取到的数据通过蓝牙发送到移动端
2,传感器的读取流程
1,模块化编程思想
由于本次的传感器配置部分代码较多,所以建议单独为这部分另外创建一个.c文件和对应的.h文件,这么做的好处是将代码模块化,使main主函数更加简洁清晰。
所以在写代码前可以如图右键点击Inc文件夹,并选择新建一个.h文件,命名为AHT20.h,.c文件同理创建在Src文件夹下方,并将用于读取传感器的代码写在这里,将用到的函数声明在.h文件中。

2,数据手册的获取及认识
之后我们还需要去寻找对应的传感器的数据手册,寻找数据手册的渠道有很多,这里简单分享几个,第一个是我比较常用的,就是嘉立创的立创商城,还有就是半岛小芯网站,搜索对应的模块名称即可将数据手册下载到本地,当然还可以在我们购买器件时找商家获取也是可行的。
而对于一般的数据手册基本都分为这么几部分,
-
基础信息区(快速入门)
-
电气与物理层(硬件设计核心)
-
功能与协议层(软件 / 固件开发核心)
-
应用与可靠性(量产关键)
-
附录与索引(高效查阅)
我们在软件编程中需要用到的就是第三点功能与协议部分,这里一般是对寄存器的介绍,功能模块描述,时序图等。
而对于我们这款的AHT20温湿度传感器的数据手册,我们在编程中需要运用到的部分便是传感器读取流程这一点,根据数据手册的步骤一步一步的配置AHT20便可正确使用。

3, 代码实现部分
1,初始化AHT20
在写代码之前 ,可以将经常要用的数据宏定义一下,这样方便后续的操作,也便于理解。例如我这里就将AHT20传感器的地址宏定义一下,这样之后的aht_address就代表0x70了,代码阅读起来也更直观。
#define aht20_address 0x70

按照数据手册流程的第一步,先读取0x71地址上的校准位,然后发送初始化命令
void aht20_Init(){ //初始化状态
uint8_t mingling[3]={0xBE,0x00,0x08}; //定义初始化命令
uint8_t Readbyte;
HAL_Delay(40);
HAL_I2C_Master_Receive_IT(&hi2c1, aht20_address, &Readbyte, 1);//直接读取地址,然后返回校验位储存在Readbyte中
if((Readbyte & 0x08) ==0x00){
HAL_I2C_Master_Transmit_IT(&hi2c1, aht20_address, mingling, 3);//发送初始化命令
}
}

void aht20_transmit(){ //发送测量指令
static uint8_t receive[3]={0xAC,0x33,0x00};
HAL_Delay(10);
HAL_I2C_Master_Transmit_DMA(&hi2c1, aht20_address, receive, 3);
}

uint8_t receiveData[7]={0};
void aht20_get(){ //等待数据测量完毕
HAL_I2C_Master_Receive_DMA(&hi2c1, aht20_address, receiveData, sizeof(receiveData));
}
当我们读取到六个字节后,就需要对温湿度进行处理了,查看数据手册中的表可以看到,从receiveData[1]到receiveData[3]的后四位,这些是湿度数据,而 receiveData[3]的前四位到receiveData[5]这些数据是温度数据,所以我们需要将温湿度数据分别合并储存起来。

uint32_t a,b;
if((receiveData[0] & 0x80) == 0x00){
a=((uint32_t)receiveData[3]>>4)+((uint32_t)receiveData[2]<<4)+((uint32_t)receiveData[1]<<12);
b=((uint32_t)receiveData[5])+((uint32_t)receiveData[4]<<8)+(((uint32_t)receiveData[3] & 0x0F)<<16);
之后将前面合并储存的数据通过给出的公式转换即可

void aht20_Read(float *SRH,float *ST){ //读取数据并对温湿度进行转换
*SRH=(a/1048576.0)*100.0f;
*ST=(b/1048576.0)*200.0f-50.0;
}
然后将所有在aht20.c文件中出现的函数在aht20.h中进行声明
#ifndef INC_AHT20_H_
#define INC_AHT20_H_
#include "i2c.h"
void aht20_Init();
void aht20_transmit();
void aht20_get();
void aht20_Read(float *SRH,float *ST);
#endif /* INC_AHT20_H_ */
2, 设置状态机
定义一个变量state来表示状态机的状态,并设置五个不同的状态
extern uint8_t state =0; //状态机状态判断: 0:发送测量命令 1:命令发送中 2 :测量完成 3:读取中 4:读取完成并串口发送数据
再定义两个32位的变量来表示温度和湿度,然后定义一个8位的数组来存储之后要通过串口发送的数据,数组的长度尽量大一些。
float RH,T;
char Data[50];
在main函数里调用初始化AHT20的函数
aht20_Init(); //初始化aht20
在while里配置状态机各个状态的函数,并一一对应环环相扣。
while (1)
{
switch(state){
case 0:
aht20_transmit(); //发送测量指令
state=1;
HAL_Delay(10);
break;
case 2:
HAL_Delay(80);
aht20_get(); //等待数据测量完毕
state=3;
break;
case 4:
aht20_Read(&RH,&T); //读取数据中并对温湿度进行转换
sprintf(Data,"温度:%.1f℃,湿度:%.1f%%\r\n",T,RH); //将温湿度数据传入Data数组中,并通过串口以一秒间隔发送
HAL_UART_Transmit(&huart2, (uint8_t*)Data, strlen(Data), HAL_MAX_DELAY);
HAL_Delay(1000);
state=0;
break;
}
}
这里可以注意到没有从1状态进入到2状态,也没有从 3状态进入到4状态的步骤,这是因为我们在写读取AHT20的时候,在aht20_transmit()函数和aht20_get()函数中使用到了
HAL_I2C_Master_Transmit_DMA(&hi2c1, aht20_address, receive, 3);
HAL_I2C_Master_Receive_DMA(&hi2c1, aht20_address, receiveData, sizeof(receiveData));
这两个函数分别是发送测试指令和接收测试数据的作用,所以我们必须等待发送或者接受完毕才能进入下一环节的操作,不然可能会出现接收数据不完整导致结果出现不符合预期的现象,因此我们这里要调用两个对应的中断回调函数,来确保只有完全执行这两个函数完毕后才进入下一状态。
回调函数的具体操作如下,注意进入回调函数第一步先判断触发中断源是否匹配!
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c){ //回调函数,当执行完 HAL_I2C_Master_Transmit_DMA()函数后触发中断跳转过来将state状态机状态变为状态2
if(hi2c==&hi2c1){
state=2;
}
}
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c){ //回调函数,当执行完HAL_I2C_Master_Receive_DMA()函数后触达中断跳转过来将state状态机状态变为状态3
if(hi2c==&hi2c1){
state=4;
}
}
至此,状态机所有的状态都匹配完毕且能够一步步向下运行。
3,数据发送移动端
我们将数据通过指针传递到RH和T变量来
float RH,T;
aht20_Read(&RH,&T);
然后使用sprintf函数将两个数据写入字符串中,然后使用串口发送函数,将温湿度数据以一秒间隔发送,这里需要的是sprintf的用法,以及下面Data字符串的类型。
sprintf(Data,"温度:%.1f℃,湿度:%.1f%%\r\n",T,RH); //将温湿度数据传入Data数组中,并通过串口以一秒间隔发送
HAL_UART_Transmit(&huart2, (uint8_t*)Data, strlen(Data), HAL_MAX_DELAY);
HAL_Delay(1000);
(sprintf 是 C 语言中将格式化数据写入字符串的核心函数,在嵌入式开发(如 STM32 日志打印、协议数据组包)中高频使用。)
由于这里使用了sprintf和strlen两个函数,所以需要在头部声明对应的库
#include <stdio.h>
#include <string.h>
#include "aht20.h"
4,运行编译代码
点击上方小锤子图标运行代码,之后点击Run图标将程序烧入单片机中
四,硬件连接
硬件连接部分比较简单,这里AHT20的四个引脚可以直接插在PB6和PB7位置上,然后GND和VIN通过面包板孔位直接连接即可,也可以使用杜邦线进行连接

蓝牙模块的连接直接使用杜邦线,注意TX和RX要与单片机的PA2和PA3反接,然后GND共地即可

五,实验现象
打开蓝牙调试app,连接蓝牙模块,注意要将接数据的类型调成文本模式而不是HEX模式,如果此时代码和接线都没问题的话,我们应该就可以接收到以每间隔一秒发出一次的消息。

如何判断温湿度检测的数据是否正确呢,不妨做个简单的小实验,可以将手指轻轻按在AHT20模块上,观察数据的变化,这里我们可以发现,温度在逐渐升高,湿度也一直在上升直至100%(不一定湿度会变成100%,只是博主手汗比较多!!!),实验现象如预料一样,实验圆满完成!

六,实验小结
本次实验运用到了I2C通信和蓝牙通信,其中的知识点也相比前几期的要更丰富些,简单总结下来大概有,I2C通信的基本原理,AHT20温湿度传感器的配置,数据手册的获取及阅读,sprintf函数的使用,状态机概念及应用,可以说是我有史以来写的内容最丰富的一篇,因此可能不能再像前几期一样面面俱到了,有些地方一笔带过实属无奈,否则篇幅过长显得啰嗦,这次的小实验实用性还是很强的,大多模块还有实验都离不开I2C通信和串口通信,所以这一部分我也花了很多心思学习研究,而像AHT20这样的有意思的模块还有很多,小伙伴们不妨在学会了I2C通信后可以考虑玩玩其他类似的模块呢?
知而不行,是为不知,行而不知,可以致知。希望小伙伴们不光看了,最好还能多动手自己操作操作,这样才能更好地体会到嵌入式有趣的地方!!!
更多推荐



所有评论(0)