1. 学习I2C总线通信协议,使用STM32F103完成基于I2C协议的AHT20温湿度传感器的数据采集,并将采集的温度-湿度值通过串口输出。具体任务:
1)解释什么是“软件I2C”和“硬件I2C”?
I2C(Inter-Integrated Circuit)是一种串行通信协议,用于在集成电路之间传输数据。I2C使用两条线:SCL(时钟线)和SDA(数据线),通过主从架构进行通信。STM32F103微控制器支持I2C通信,可以通过软件模拟或硬件外设实现I2C协议。
软件I2C
软件I2C(Software I2C)指的是通过软件代码来模拟I2C通信时序和协议。这种方法不依赖微控制器的硬件I2C外设,而是通过普通的GPIO引脚和定时控制来实现I2C通信。具体来说,软件I2C通常包括以下几个步骤:
- 拉低SDA: 模拟起始条件。
- 拉低或拉高SCL:控制时钟线以产生时钟脉冲。
- 读写SDA:根据需要发送或接收数据位。
- 释放SDA:模拟停止条件。
优点:
- 灵活性高,可以用于没有硬件I2C外设的微控制器。
- 可以在任意GPIO引脚上实现。
缺点:
- 需要占用CPU资源,效率较低。
- 定时要求高,容易受到中断的影响。
硬件I2C
硬件I2C(Hardware I2C)利用微控制器内部专门的I2C外设模块来实现I2C通信。STM32F103系列微控制器通常内置多个I2C外设,可以直接使用这些外设来控制I2C通信,而无需手动模拟时序。
优点:
- 效率高,由硬件自动处理时序和协议,减少CPU负担。
- 更加稳定,受外部中断的影响较小。
- 通常支持更高的I2C总线速度(标准模式、快速模式等)。
缺点:
- 硬件接口的灵活性较低,受限于特定的I2C引脚。
- 需要通过寄存器配置,学习曲线可能较高。
实现I2C通信并采集AHT20数据
1. 配置I2C外设
首先,需要通过STM32的I2C外设初始化代码配置I2C总线。
#include "stm32f1xx_hal.h"
I2C_HandleTypeDef hi2c1;
void MX_I2C1_Init(void)
{
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000; // 100kHz 标准模式
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
// 初始化错误处理
}
}
2. AHT20 数据读取函数
编写函数,通过I2C读取AHT20的数据。
#define AHT20_ADDRESS 0x38 << 1 // AHT20 I2C地址,考虑到7位地址格式
uint8_t AHT20_Init(void)
{
uint8_t init_cmd[] = {0xBE, 0x08, 0x00};
return HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, init_cmd, 3, HAL_MAX_DELAY);
}
uint8_t AHT20_ReadData(uint8_t *data)
{
uint8_t trigger_measurement_cmd[] = {0xAC, 0x33, 0x00};
HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, trigger_measurement_cmd, 3, HAL_MAX_DELAY);
HAL_Delay(80); // 延时保证测量完成
return HAL_I2C_Master_Receive(&hi2c1, AHT20_ADDRESS, data, 6, HAL_MAX_DELAY);
}
3. 串口配置与数据输出
配置串口,并通过串口发送读取到的温湿度数据。
UART_HandleTypeDef huart1;
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
// 初始化错误处理
}
}
void Print_Temp_Humidity(float temperature, float humidity)
{
char buffer[100];
int len = snprintf(buffer, sizeof(buffer), "Temperature: %.2f C, Humidity: %.2f %%\r\n", temperature, humidity);
HAL_UART_Transmit(&huart1, (uint8_t *)buffer, len, HAL_MAX_DELAY);
}
4. 主循环
在主循环中调用这些函数,周期性地读取并输出温湿度数据。
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART1_UART_Init();
// 初始化AHT20
if (AHT20_Init() != HAL_OK)
{
// 错误处理
}
uint8_t data[6];
while (1)
{
if (AHT20_ReadData(data) == HAL_OK)
{
uint32_t raw_temperature = ((uint32_t)data[3] & 0x0F) << 16 | data[4] << 8 | data[5];
uint32_t raw_humidity = (uint32_t)data[1] << 12 | data[2] << 4 | (data[3] & 0xF0) >> 4;
float temperature = ((float)raw_temperature / 1048576.0) * 200.0 - 50.0;
float humidity = ((float)raw_humidity / 1048576.0) * 100.0;
Print_Temp_Humidity(temperature, humidity);
}
HAL_Delay(2000); // 2秒钟读取一次
}
}
2)阅读AHT20数据手册,编程实现:每隔2秒钟采集一次温湿度数据,显示到OLED上,同时通过串口发送到上位机的“串口助手”软件。
定期读取温湿度数据
读取传感器数据并进行处理:
// 读取AHT20数据
void AHT20_ReadData(float *temperature, float *humidity)
{
uint8_t data[7];
AHT20_StartMeasurement();
HAL_Delay(80); // 等待测量完成
HAL_I2C_Master_Receive(&hi2c1, AHT20_ADDR, data, 7, HAL_MAX_DELAY);
uint32_t rawHumidity = (data[1] << 12) | (data[2] << 4) | (data[3] >> 4);
uint32_t rawTemperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
*humidity = ((float)rawHumidity / 1048576.0) * 100.0;
*temperature = ((float)rawTemperature / 1048576.0) * 200.0 - 50.0;
}
显示到OLED显示屏(可选)
使用适当的OLED驱动库来显示数据,这里假设已经初始化OLED显示:
void OLED_DisplayData(float temperature, float humidity)
{
char buffer[32];
sprintf(buffer, "Temp: %.2f C", temperature);
OLED_ShowString(0, 0, buffer);
sprintf(buffer, "Hum: %.2f %%", humidity);
OLED_ShowString(0, 16, buffer);
}
通过串口发送数据到上位机
初始化UART并发送数据:
UART_HandleTypeDef huart1;
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
// Initialization Error
Error_Handler();
}
}
void UART_SendData(float temperature, float humidity)
{
char buffer[64];
int len = sprintf(buffer, "Temperature: %.2f C, Humidity: %.2f %%\r\n", temperature, humidity);
HAL_UART_Transmit(&huart1, (uint8_t*)buffer, len, HAL_MAX_DELAY);
}
主循环
在主循环中,每隔2秒钟采集一次数据,并进行显示和发送:
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART1_UART_Init();
AHT20_Init();
while (1)
{
float temperature, humidity;
AHT20_ReadData(&temperature, &humidity);
OLED_DisplayData(temperature, humidity); // 如果有OLED显示
UART_SendData(temperature, humidity);
HAL_Delay(2000); // 每隔2秒采样一次
}
}
作者水平有限,不足之处欢迎指正。