目录
3.根据手册写初始化过程,在aht20.c中定义一个初始化函数AHT20_Init
4.根据AHT20数据手册,在aht20.c文件中定义AHT20读取温湿度的函数:AHT20_Read
之前我们学习了STM32中最常用的通信方式:串口,这次,我们来学习另一种常见的通信方式:IIC通信 ,并且使用IIC通信与stm32板上的AHT20传感器进行交互,获取周围环境的温度与湿度。
一.IIC通信原理
除了共地线,还有一条可以传递数据的数据线(SDA:Serial Data)和另一条用于同步时钟脉冲的时钟线(SCL:Serial Clock) 。串口两根线可以同时进行双向通信(全双工通信 ),而IIC的SDA也允许双向通信,但同一时刻只能一端发送,不能同时进行(半双工通信 ),即同一时刻只能进行一个方向的通信 ,为了避免冲突,IIC采用主从模式 ,一台设备为主机,另一条/多台为从机。只能主机先发起通信,从机才能根据主机的指令回复相应的信息。因为一问一答的主从模式,使得IIC可以支持多设备通信,多用于板载小传感器芯片 。像IIC这种支持多个设备进行通信的通信协议,我们称其为总线协议 。在IIC总线上,每个从机都有其唯一的设备地址。IIC选择与异步通信相对的另一种模式------同步通信 ,即主机通过时钟线发送固定频率的脉冲信号,来作为IIC总线上所有设备通信的统一时钟源。

主机在发送开始与结束信号时,时钟线为高电平主机才控制数据线。正常情况下,比如数据发送方为从机,当时钟线为低电平,从机控制数据线设置为高/低电平;当时钟线为高电平时,主机读取数据线刚才设置的电平。按照IIC协议的约定,数据的接收方需要发送一个ACK信号(应答信号)确认自己已经收到数据。
二.Cubemx
1.打开USART2,用于后面串口查看数据

2.设置IIC

3.这次不将代码全部写在main.c文件,,而是专门为AHT20写一下驱动文件(aht20.h+aht20.c)
勾选为每一个外设生成一对.c/.h文件,可以在其它文件中include相应的头文件,就能拿到huart2或hi2c1这类外设操作句柄了。

保存并生成代码后发现生成gpio,i2c,usart的.c和.h文件

4.定义aht20.c与.h文件
aht20.h文件:写#include"i2c.h"(为了使用I2C相关函数)

aht20.c文件:写#include"aht20.h"(包含了i2c.h文件)

三.代码实现
1.查阅AHT20数据手册

2.IIC读取与发送函数
IIC读取函数:(主机接收)
HAL_I2C_Master_Receive(hi2c, DevAddress, pData, Size, Timeout);
第一个参数:外设操作句柄的指针,即hi2c1的指针
第二个参数:读取的从机地址,即宏定义AHT20_ADDRESS
第三个参数:用于接收数据的变量的指针,即&readBuffer
第四个参数:读取多少位数据,按照手册,读一位数据,即为1
第五个参数:超时时间,这里填永久等待,即HAL_MAX_DELAY
因此,该函数为:HAL_I2C_Master_Receive(&hi2c1,AHT20_ADDRESS,&readBuffer,1,HAL_MAX_DELAY);
IIC发送函数:(主机发送)HAL_I2C_Master_Transmit(hi2c, DevAddress, pData, Size, Timeout);
第一个参数:外设操作句柄的指针,即hi2c1的指针
第二个参数:发送的从机地址,即宏定义AHT20_ADDRESS
第三个参数:用于发送数据的变量的指针,即sendBuffer(数组名为首元素地址)
第四个参数:发送多少位数据,按照手册,发送3字节数据,即为3
第五个参数:超时时间,这里填永久等待,即HAL_MAX_DELAY
因此,该函数为:
HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, sendBuffer, 3,HAL_MAX_DELAY)
注意辨别什么时候使用Transmit,什么时候使用Receive(当题目无明显体现谁向谁发送消息时):命令 / 指令 = 单片机 (主机) 发给传感器 (从机) → Transmit;
读数 / 状态 = 传感器发给单片机 → Receive
3.根据手册写初始化过程,在aht20.c中定义一个初始化函数AHT20_Init
(1)数据手册初始化过程

(2)进行define宏定义,定义一下AHT20的设备地址是0x70

(3)初始化函数

cpp
//初始化
void AHT20_Init()
{
uint8_t readBuffer;//用于接收状态信息
HAL_Delay(40);//按照手册先延时40ms
HAL_I2C_Master_Receive(&hi2c1,AHT20_ADDRESS,&readBuffer,1,HAL_MAX_DELAY);
if((readBuffer & 0x08) == 0x00)
{
uint8_t sendBuffer[3] = {0xBE, 0x08, 0x00};
HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, sendBuffer,3 , HAL_MAX_DELAY);
}
}
4.根据AHT20数据手册,在aht20.c文件中定义AHT20读取温湿度的函数:AHT20_Read
(1)数据手册:

(2)AHT20_Read函数:

cpp
void AHT20_Read(float* Temperature, float* Humidity)
{
uint8_t sendBuffer[3] = {0xAC, 0x33, 0x00};
uint8_t readBuffer[6] = {0};
HAL_I2C_Master_Transmit_(&hi2c1, AHT20_ADDRESS, sendBuffer, 3, HAL_MAX_DELAY);
HAL_Delay(75);
HAL_I2C_Master_Receive_IT(&hi2c1, AHT20_ADDRESS, readBuffer,6, HAL_MAX_DELAY);
if((readBuffer[0] & 0x80) == 0x00)
{
uint32_t data = 0;
//注意类型转化:uint8_t->uint32_t
data = ((uint32_t)readBuffer[3] >> 4 ) + ((uint32_t)readBuffer[2] << 4) + ((uint32_t)readBuffer[1] << 12);
*Humidity = data* 100.0f / ( 1<<20 ) ;//注意要乘100.0f,保证运算结果为小数
data = (((uint32_t)readBuffer[3] & 0x0F) << 16) + ((uint32_t)readBuffer[4] << 8) + (uint32_t)readBuffer[5];
*Temperature = data * 200.0f / (1 << 20) - 50;
}
}
5.aht20.h文件中实现函数声明
(1)在aht20.h文件中对刚才定义的函数进行声明

cpp
void AHT20_Init();
void AHT20_Read(float* Temperature, float* Humidity);
6.main.c文件剩下代码:
(1)在main.c文件中引入aht20.h,stdio.h和string.h头文件

(2)在main函数中初始化,同时引入3个变量

(3)在while循环中写入读取温湿度函数,且把文字 + 数字拼成字符串存进message数组中,
注意:由于stm32CubeIDE没有启用编译器对浮点数输出的支持,按照报错指示来解决该问题
最后通过串口发送函数HAL_UART_Transmit发送出来

cpp
AHT20_Read(&temperature, &humidity);
sprintf(message, "温度:%.lf ℃,湿度:%.lf %%\r\n",temperature,humidity);
//注意强制类型转化:
HAL_UART_Transmit(&huart2, (uint8_t*)message, strlen(message), HAL_MAX_DELAY);
HAL_Delay(1000);
四.整体代码:
1.aht20.h
cpp/* * aht20.h * * Created on: May 24, 2026 * Author: 25306 */ #ifndef INC_AHT20_H_ #define INC_AHT20_H_ #include "i2c.h" void AHT20_Init(); void AHT20_Read(float* Temperature, float* Humidity); //void AHT20_Measure(); // //void AHT20_Get(); // //void AHT20_Analysis(float* Temperature, float* Humidity); #endif /* INC_AHT20_H_ */2.aht20.c
cpp/* * aht20.c * * Created on: May 24, 2026 * Author: 25306 */ #include"aht20.h" #define AHT20_ADDRESS 0x70 //uint8_t readBuffer[6] = {0}; //初始化 void AHT20_Init() { uint8_t readBuffer;//用于接收状态信息 HAL_Delay(40);//按照手册先延时40ms HAL_I2C_Master_Receive(&hi2c1,AHT20_ADDRESS,&readBuffer,1,HAL_MAX_DELAY); if((readBuffer & 0x08) == 0x00) { uint8_t sendBuffer[3] = {0xBE, 0x08, 0x00}; HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, sendBuffer,3 , HAL_MAX_DELAY); } } void AHT20_Read(float* Temperature, float* Humidity) { uint8_t sendBuffer[3] = {0xAC, 0x33, 0x00}; uint8_t readBuffer[6] = {0}; HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, sendBuffer, 3,HAL_MAX_DELAY); HAL_Delay(75); HAL_I2C_Master_Receive(&hi2c1, AHT20_ADDRESS, readBuffer,6,HAL_MAX_DELAY ); if((readBuffer[0] & 0x80) == 0x00) { uint32_t data = 0; //注意类型转化:uint8_t->uint32_t data = ((uint32_t)readBuffer[3] >> 4 ) + ((uint32_t)readBuffer[2] << 4) + ((uint32_t)readBuffer[1] << 12); *Humidity = data* 100.0f / ( 1<<20 ) ;//注意要乘100.0f,保证运算结果为小数 data = (((uint32_t)readBuffer[3] & 0x0F) << 16) + ((uint32_t)readBuffer[4] << 8) + (uint32_t)readBuffer[5]; *Temperature = data * 200.0f / (1 << 20) - 50; } } //void AHT20_Measure() //{ // static uint8_t sendBuffer[3] = { 0xAC, 0X33, 0X00 }; // HAL_I2C_Master_Transmit_DMA(&hi2c1, AHT20_ADDRESS, sendBuffer, 3); //} // //void AHT20_Get() //{ // HAL_I2C_Master_Receive_DMA(&hi2c1, AHT20_ADDRESS, readBuffer, 6); //} // //void AHT20_Analysis(float* Temperature, float* Humidity) //{ // if((readBuffer[0] & 0x80) == 0x00) // { // uint32_t data = 0; // data = ((uint32_t)readBuffer[3] >> 4 ) + ((uint32_t)readBuffer[2] << 4) + ((uint32_t)readBuffer[1] << 12); // *Humidity = data* 100.0f / ( 1<<20 ) ; // // data = (((uint32_t)readBuffer[3] & 0x0F) << 16) + ((uint32_t)readBuffer[4] << 8) + (uint32_t)readBuffer[5]; // *Temperature = data * 200.0f / (1 << 20) - 50; // } //}3.main.c
cpp/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2026 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "dma.h" #include "i2c.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "aht20.h" #include <stdio.h> #include<string.h> /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ //0:初始状态 发送测量命令 1:正在发送测量命令 2:测量命令发送完成,等待75毫秒后读取AHT20数据 //3:读取中 4:读取完成,解析并展示数据然后恢复到初始状态 uint8_t aht20State = 0; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_I2C1_Init(); MX_USART2_UART_Init(); /* USER CODE BEGIN 2 */ AHT20_Init(); float temperature, humidity; char message[50]; /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { // if(aht20State == 0) // { // AHT20_Measure(); // aht20State = 1; // }else if(aht20State == 2){ // HAL_Delay(75); // AHT20_Get(); // aht20State = 3; // }else if(aht20State == 4){ // AHT20_Analysis(&temperature, &humidity); // sprintf(message,"温度:%.lf℃, 湿度:%.lf %%\r\n",temperature,humidity); // HAL_UART_Transmit(&huart2,(uint8_t*)message, strlen(message), HAL_MAX_DELAY); // // HAL_Delay(1000); // aht20State = 0; // } AHT20_Read(&temperature, &humidity); sprintf(message, "温度:%.lf ℃,湿度:%.lf %%\r\n",temperature,humidity); HAL_UART_Transmit(&huart2, (uint8_t*)message, strlen(message), HAL_MAX_DELAY);//注意强制类型转化 HAL_Delay(1000); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */