From 43a5042c9ec7ab7544eea504a1ca89fc2271f304 Mon Sep 17 00:00:00 2001 From: wangbeihong Date: Mon, 23 Feb 2026 23:00:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0AHT30=E6=B8=A9?= =?UTF-8?q?=E6=B9=BF=E5=BA=A6=E4=BC=A0=E6=84=9F=E5=99=A8=E9=A9=B1=E5=8A=A8?= =?UTF-8?q?=E5=8F=8A=E6=98=BE=E7=A4=BA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现AHT30温湿度传感器的I2C驱动 新增传感器任务定期读取数据 优化LCD显示界面,增加温湿度显示 添加I2C硬件和软件扫描功能 修复I2C引脚配置问题 --- CMakeLists.txt | 7 + Core/Bsp/BSP_Device/bsp_aht30/bsp_aht30.c | 580 ++++++++++++++++++ Core/Bsp/BSP_Device/bsp_aht30/bsp_aht30.h | 193 ++++++ Core/Bsp/BSP_Device/spi_st7735s/spi_st7735s.h | 1 + Core/Src/freertos.c | 86 ++- Core/Src/i2c.c | 1 + hahha.ioc | 2 +- 7 files changed, 862 insertions(+), 8 deletions(-) create mode 100644 Core/Bsp/BSP_Device/bsp_aht30/bsp_aht30.c create mode 100644 Core/Bsp/BSP_Device/bsp_aht30/bsp_aht30.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 18abeae..5bb0548 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,7 @@ add_executable(${CMAKE_PROJECT_NAME} Core/Bsp/BSP_Device/spi_st7735s/fonts.c Core/Bsp/BSP_Device/bsp_mp3/mp3_driver.c Core/Bsp/MultiButton-master/Multi_Button.c + Core/Bsp/BSP_Device/bsp_aht30/bsp_aht30.c ) # Add STM32CubeMX generated sources add_subdirectory(cmake/stm32cubemx) @@ -70,6 +71,7 @@ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE Core/Bsp/BSP_Device/spi_st7735s Core/Bsp/BSP_Device/bsp_mp3 Core/Bsp/MultiButton-master + Core/Bsp/BSP_Device/bsp_aht30 ) # Add project symbols (macros) @@ -80,6 +82,11 @@ target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE # Remove wrong libob.a library dependency when using cpp files list(REMOVE_ITEM CMAKE_C_IMPLICIT_LINK_LIBRARIES ob) +# Enable float support for printf +target_link_options(${CMAKE_PROJECT_NAME} PRIVATE + -u _printf_float +) + # Add linked libraries target_link_libraries(${CMAKE_PROJECT_NAME} stm32cubemx diff --git a/Core/Bsp/BSP_Device/bsp_aht30/bsp_aht30.c b/Core/Bsp/BSP_Device/bsp_aht30/bsp_aht30.c new file mode 100644 index 0000000..e03eb82 --- /dev/null +++ b/Core/Bsp/BSP_Device/bsp_aht30/bsp_aht30.c @@ -0,0 +1,580 @@ +/** + ****************************************************************************** + * @file bsp_aht30.c + * @brief AHT30温湿度传感器驱动实现 + ****************************************************************************** + * @attention + * AHT30是一种高精度、低功耗的温湿度传感器,支持I2C通信协议 + ****************************************************************************** + */ + +#include "bsp_aht30.h" +#include "cmsis_os.h" +#include "elog.h" +#include "i2c.h" +#include + +/* ================= 全局变量 ================= */ + +// AHT30设备实例 +static AHT30_HandleTypeDef aht30_device = {0}; + +/* ================= 私有函数声明 ================= */ + +/** + * @brief I2C写命令 + * @param cmd 命令数组 + * @param len 命令长度 + * @retval 1=成功, 0=失败 + */ +static uint8_t AHT30_WriteCommand(uint8_t *cmd, uint8_t len); + +/** + * @brief I2C读取数据 + * @param data 数据缓冲区 + * @param len 数据长度 + * @retval 1=成功, 0=失败 + */ +static uint8_t AHT30_ReadBytes(uint8_t *data, uint16_t len); + +/** + * @brief 复位寄存器(LibDriver标准方法) + * @param addr 寄存器地址 + * @retval 1=成功, 0=失败 + */ +static uint8_t AHT30_ResetRegister(uint8_t addr); + +/* ================= 私有函数实现 ================= */ + +/** + * @brief I2C写命令 + */ +static uint8_t AHT30_WriteCommand(uint8_t *cmd, uint8_t len) { + if (HAL_I2C_Master_Transmit(aht30_device.hi2c, + (AHT30_I2C_ADDR << 1), + cmd, len, 500) == HAL_OK) { + return 1; + } + elog_e(AHT30_TAG, "I2C写命令失败, 地址: 0x%02X", AHT30_I2C_ADDR); + return 0; +} + +/** + * @brief I2C读取数据 + */ +static uint8_t AHT30_ReadBytes(uint8_t *data, uint16_t len) { + if (HAL_I2C_Master_Receive(aht30_device.hi2c, + (AHT30_I2C_ADDR << 1), + data, len, 500) == HAL_OK) { + return 1; + } + elog_e(AHT30_TAG, "I2C读取数据失败, 地址: 0x%02X", AHT30_I2C_ADDR); + return 0; +} + +/* ================= 公共函数实现 ================= */ + +/** + * @brief 初始化AHT30传感器 + */ +uint8_t AHT30_Init(I2C_HandleTypeDef *hi2c) { + if (!hi2c) { + elog_e(AHT30_TAG, "I2C句柄为空"); + return 0; + } + + memset(&aht30_device, 0, sizeof(AHT30_HandleTypeDef)); + aht30_device.hi2c = hi2c; + + elog_d(AHT30_TAG, "开始初始化AHT30传感器"); + + // 等待传感器上电稳定 + osDelay(50); + + // 读取初始状态 + uint8_t status; + if (AHT30_ReadBytes(&status, 1)) { + elog_d(AHT30_TAG, "AHT30初始状态: 0x%02X", status); + } + + // 检查校准使能位,如果未启用则执行复位校准 + if ((status & 0x18) != 0x18) { + elog_w(AHT30_TAG, "AHT30未校准,执行复位校准"); + + if (!AHT30_ResetRegister(0x1B)) { + elog_e(AHT30_TAG, "复位寄存器0x1B失败"); + return 0; + } + if (!AHT30_ResetRegister(0x1C)) { + elog_e(AHT30_TAG, "复位寄存器0x1C失败"); + return 0; + } + if (!AHT30_ResetRegister(0x1E)) { + elog_e(AHT30_TAG, "复位寄存器0x1E失败"); + return 0; + } + } + + osDelay(10); + aht30_device.initialized = 1; + elog_i(AHT30_TAG, "AHT30初始化成功"); + + return 1; +} + +/** + * @brief 软复位AHT30传感器 + */ +uint8_t AHT30_Reset(void) { + uint8_t cmd = AHT30_CMD_RESET; + + if (!AHT30_WriteCommand(&cmd, 1)) { + elog_e(AHT30_TAG, "复位命令发送失败"); + return 0; + } + + // 等待复位完成 + osDelay(20); + + return 1; +} + +/** + * @brief 复位寄存器(LibDriver标准方法) + */ +static uint8_t AHT30_ResetRegister(uint8_t addr) { + uint8_t buf[3]; + uint8_t regs[3]; + + buf[0] = addr; + buf[1] = 0x00; + buf[2] = 0x00; + if (!AHT30_WriteCommand(buf, 3)) { + elog_e(AHT30_TAG, "写入地址0x%02X失败", addr); + return 0; + } + + osDelay(5); + + if (!AHT30_ReadBytes(regs, 3)) { + elog_e(AHT30_TAG, "读取寄存器失败"); + return 0; + } + + osDelay(10); + + buf[0] = 0xB0 | addr; + buf[1] = regs[1]; + buf[2] = regs[2]; + if (!AHT30_WriteCommand(buf, 3)) { + elog_e(AHT30_TAG, "恢复寄存器失败"); + return 0; + } + + return 1; +} + +/** + * @brief 校准AHT30传感器 + */ +uint8_t AHT30_Calibrate(void) { + uint8_t cmd[3] = {AHT30_CMD_CALIBRATE, 0x08, 0x00}; + + if (!AHT30_WriteCommand(cmd, 3)) { + elog_e(AHT30_TAG, "校准命令发送失败"); + return 0; + } + + // 等待校准完成 + osDelay(300); + + // 读取校准状态 + uint8_t status; + if (AHT30_ReadBytes(&status, 1)) { + if (status & 0x08) { + return 1; + } else { + elog_e(AHT30_TAG, "AHT30校准失败,状态: 0x%02X", status); + return 0; + } + } + + elog_e(AHT30_TAG, "无法读取校准状态"); + return 0; +} + +/** + * @brief 读取AHT30温湿度数据 + */ +uint8_t AHT30_ReadData(AHT30_Data_t *data) { + uint8_t cmd[3] = {AHT30_CMD_MEASURE, 0x33, 0x00}; + uint8_t recv[7] = {0}; + + if (!data) { + elog_e(AHT30_TAG, "数据指针为空"); + return 0; + } + + // 发送测量命令 + if (!AHT30_WriteCommand(cmd, 3)) { + elog_e(AHT30_TAG, "测量命令发送失败"); + return 0; + } + + // 等待测量完成 + osDelay(AHT30_MEASUREMENT_DELAY); + + // 读取测量数据 + if (!AHT30_ReadBytes(recv, 7)) { + elog_e(AHT30_TAG, "测量数据读取失败"); + return 0; + } + + // CRC校验 + uint8_t crc = AHT30_CRC8(&recv[0], 6); + if (crc != recv[6]) { + elog_e(AHT30_TAG, "CRC校验失败,计算值: 0x%02X, 接收值: 0x%02X", crc, recv[6]); + return 0; + } + + // 检查状态位 + if (recv[0] & 0x80) { + elog_e(AHT30_TAG, "设备忙"); + return 0; + } + if (!(recv[0] & 0x08)) { + elog_e(AHT30_TAG, "数据无效"); + return 0; + } + + // 解析湿度数据 (20位) + uint32_t hum_raw = ((uint32_t)recv[1] << 12) | + ((uint32_t)recv[2] << 4) | + (recv[3] >> 4); + data->humidity = (float)hum_raw / 1048576.0f * 100.0f; + + // 解析温度数据 (20位) + uint32_t temp_raw = ((uint32_t)(recv[3] & 0x0F) << 16) | + ((uint32_t)recv[4] << 8) | + recv[5]; + data->temperature = (float)temp_raw / 1048576.0f * 200.0f - 50.0f; + + data->valid = 1; + data->timestamp = osKernelGetTickCount(); + + // 更新设备缓存 + memcpy(&aht30_device.data, data, sizeof(AHT30_Data_t)); + + return 1; +} + +/** + * @brief 获取最新的温湿度数据 + */ +AHT30_Data_t* AHT30_GetData(void) { + return &aht30_device.data; +} + +/** + * @brief 检查AHT30是否已初始化 + */ +uint8_t AHT30_IsInitialized(void) { + return aht30_device.initialized; +} + +/** + * @brief 扫描I2C设备 + */ +void AHT30_ScanI2C(I2C_HandleTypeDef *hi2c) { + if (!hi2c) { + elog_e(AHT30_TAG, "I2C句柄为空"); + return; + } + + elog_i(AHT30_TAG, "开始扫描I2C设备..."); + elog_i(AHT30_TAG, "I2C句柄: 0x%p", (void*)hi2c); + + // 检查GPIO状态 + GPIO_PinState scl = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6); + GPIO_PinState sda = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7); + elog_i(AHT30_TAG, "GPIO状态 - SCL(PB6): %d, SDA(PB7): %d", scl, sda); + + if (scl == GPIO_PIN_RESET && sda == GPIO_PIN_RESET) { + elog_w(AHT30_TAG, "警告: SCL和SDA都是低电平,可能被短路!"); + } + + uint8_t found_count = 0; + for (uint8_t addr = 1; addr < 128; addr++) { + HAL_StatusTypeDef status = HAL_I2C_IsDeviceReady(hi2c, addr << 1, 3, 100); + if (status == HAL_OK) { + elog_i(AHT30_TAG, "发现I2C设备: 0x%02X (8位: 0x%02X)", addr, addr << 1); + found_count++; + } + } + + if (found_count == 0) { + elog_w(AHT30_TAG, "未发现任何I2C设备!请检查:"); + elog_w(AHT30_TAG, " 1. 硬件连接 (SCL->PB6, SDA->PB7)"); + elog_w(AHT30_TAG, " 2. 电源供电 (VCC->3.3V, GND->GND)"); + elog_w(AHT30_TAG, " 3. 上拉电阻 (4.7kΩ 到 3.3V)"); + elog_w(AHT30_TAG, " 4. I2C地址是否正确"); + } else { + elog_i(AHT30_TAG, "共发现 %d 个I2C设备", found_count); + } +} + +/** + * @brief 软件I2C扫描(备用方案) + */ +void AHT30_ScanI2C_Soft(void) { + elog_i(AHT30_TAG, "使用软件I2C扫描..."); + + // 配置为输出模式(推挽输出) + GPIO_InitTypeDef GPIO_InitStruct = {0}; + GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Pull = GPIO_PULLUP; + HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + // 设置为高电平 + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); + + uint8_t found_count = 0; + + // 简单软件I2C扫描 + for (uint8_t addr = 0; addr < 128; addr++) { + // 开始条件 + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); + osDelay(1); + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); + osDelay(1); + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET); + osDelay(1); + + // 发送地址 + for (uint8_t i = 0; i < 8; i++) { + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET); + if (addr & (0x80 >> i)) { + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); + } else { + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); + } + osDelay(1); + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); + osDelay(1); + } + + // 读取ACK + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET); + osDelay(1); + + // 切换SDA为输入 + GPIO_InitStruct.Pin = GPIO_PIN_7; + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_PULLUP; + HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + GPIO_PinState ack = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7); + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); + osDelay(1); + + // 恢复SDA为输出 + GPIO_InitStruct.Pin = GPIO_PIN_7; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + // 停止条件 + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); + osDelay(1); + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); + osDelay(1); + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); + osDelay(1); + + if (ack == GPIO_PIN_RESET) { + elog_i(AHT30_TAG, "软件I2C发现设备: 0x%02X", addr); + found_count++; + } + } + + elog_i(AHT30_TAG, "软件I2C扫描完成,发现 %d 个设备", found_count); + + // 恢复I2C模式 + MX_I2C1_Init(); +} + +/** + * @brief AHT30 CRC8校验 + */ +uint8_t AHT30_CRC8(uint8_t *data, uint8_t len) { + uint8_t i; + uint8_t byte; + uint8_t crc = 0xFF; + + for (byte = 0; byte < len; byte++) { + crc ^= data[byte]; + for (i = 8; i > 0; --i) { + if ((crc & 0x80) != 0) { + crc = (crc << 1) ^ 0x31; + } else { + crc = crc << 1; + } + } + } + + return crc; +} + +/** + * @brief 读取AHT30状态 + */ +uint8_t AHT30_GetStatus(uint8_t *status) { + if (!status) { + elog_e(AHT30_TAG, "状态指针为空"); + return 0; + } + + if (!AHT30_ReadBytes(status, 1)) { + elog_e(AHT30_TAG, "读取状态失败"); + return 0; + } + + return 1; +} + +/** + * @brief 仅读取温度数据 + */ +uint8_t AHT30_ReadTemperature(float *temperature) { + uint8_t cmd[3] = {AHT30_CMD_MEASURE, 0x33, 0x00}; + uint8_t recv[7] = {0}; + uint32_t temp_raw; + + if (!temperature) { + elog_e(AHT30_TAG, "温度指针为空"); + return 0; + } + + if (!AHT30_WriteCommand(cmd, 3)) { + elog_e(AHT30_TAG, "测量命令发送失败"); + return 0; + } + + osDelay(AHT30_MEASUREMENT_DELAY); + + if (!AHT30_ReadBytes(recv, 7)) { + elog_e(AHT30_TAG, "测量数据读取失败"); + return 0; + } + + if (recv[0] & AHT30_STATUS_BUSY) { + elog_e(AHT30_TAG, "设备忙"); + return 0; + } + + if (AHT30_CRC8(recv, 6) != recv[6]) { + elog_e(AHT30_TAG, "CRC校验失败"); + return 0; + } + + temp_raw = ((uint32_t)recv[3] << 16) | + ((uint32_t)recv[4] << 8) | + (uint32_t)recv[5]; + temp_raw = temp_raw & 0xFFFFF; + *temperature = (float)temp_raw / 1048576.0f * 200.0f - 50.0f; + + return 1; +} + +/** + * @brief 仅读取湿度数据 + */ +uint8_t AHT30_ReadHumidity(float *humidity) { + uint8_t cmd[3] = {AHT30_CMD_MEASURE, 0x33, 0x00}; + uint8_t recv[7] = {0}; + uint32_t hum_raw; + + if (!humidity) { + elog_e(AHT30_TAG, "湿度指针为空"); + return 0; + } + + if (!AHT30_WriteCommand(cmd, 3)) { + elog_e(AHT30_TAG, "测量命令发送失败"); + return 0; + } + + osDelay(AHT30_MEASUREMENT_DELAY); + + if (!AHT30_ReadBytes(recv, 7)) { + elog_e(AHT30_TAG, "测量数据读取失败"); + return 0; + } + + if (recv[0] & AHT30_STATUS_BUSY) { + elog_e(AHT30_TAG, "设备忙"); + return 0; + } + + if (AHT30_CRC8(recv, 6) != recv[6]) { + elog_e(AHT30_TAG, "CRC校验失败"); + return 0; + } + + hum_raw = ((uint32_t)recv[1] << 16) | + ((uint32_t)recv[2] << 8) | + (uint32_t)recv[3]; + hum_raw = hum_raw >> 4; + *humidity = (float)hum_raw / 1048576.0f * 100.0f; + + return 1; +} + +/** + * @brief 写寄存器(扩展功能) + */ +uint8_t AHT30_SetReg(uint8_t *buf, uint16_t len) { + if (!buf || len == 0) { + elog_e(AHT30_TAG, "无效参数"); + return 0; + } + + return AHT30_WriteCommand(buf, len); +} + +/** + * @brief 读寄存器(扩展功能) + */ +uint8_t AHT30_GetReg(uint8_t *buf, uint16_t len) { + if (!buf || len == 0) { + elog_e(AHT30_TAG, "无效参数"); + return 0; + } + + return AHT30_ReadBytes(buf, len); +} + +/** + * @brief 获取芯片信息 + */ +uint8_t AHT30_GetInfo(AHT30_Info_t *info) { + if (!info) { + elog_e(AHT30_TAG, "信息指针为空"); + return 0; + } + + memset(info, 0, sizeof(AHT30_Info_t)); + strncpy(info->chip_name, "ASAIR AHT30", 32); + strncpy(info->manufacturer_name, "ASAIR", 32); + strncpy(info->interface, "IIC", 8); + info->supply_voltage_min = 2.2f; + info->supply_voltage_max = 5.5f; + info->max_current = 0.60f; + info->temperature_min = -40.0f; + info->temperature_max = 85.0f; + + return 1; +} diff --git a/Core/Bsp/BSP_Device/bsp_aht30/bsp_aht30.h b/Core/Bsp/BSP_Device/bsp_aht30/bsp_aht30.h new file mode 100644 index 0000000..71dde2e --- /dev/null +++ b/Core/Bsp/BSP_Device/bsp_aht30/bsp_aht30.h @@ -0,0 +1,193 @@ +/** + ****************************************************************************** + * @file bsp_aht30.h + * @brief AHT30温湿度传感器驱动头文件 + ****************************************************************************** + * @attention + * AHT30是一种高精度、低功耗的温湿度传感器,支持I2C通信协议 + * I2C地址: 0x70 (7位地址,8位地址为0xE0/0xE1) + ****************************************************************************** + */ + +#ifndef __BSP_AHT30_H__ +#define __BSP_AHT30_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "stm32f1xx_hal.h" +#include +#include "main.h" + +/* ================= 宏定义 ================= */ + +// AHT30 I2C地址 +#define AHT30_I2C_ADDR 0x38 // 7位地址 (8位地址为0x70) + +// AHT30命令定义 +#define AHT30_CMD_CALIBRATE 0xE1 // 校准命令 +#define AHT30_CMD_RESET 0xBA // 复位命令 +#define AHT30_CMD_MEASURE 0xAC // 测量命令 +#define AHT30_CMD_NORMAL_CMD1 0xA8 // 正常测量命令1 +#define AHT30_CMD_NORMAL_CMD2 0x00 // 正常测量命令2 +#define AHT30_CMD_NORMAL_CMD3 0x00 // 正常测量命令3 + +// AHT30测量参数 +#define AHT30_MEASUREMENT_DELAY 85 // 测量等待时间(ms) + +// AHT30状态位定义 +#define AHT30_STATUS_BUSY (1 << 7) // 忙标志 +#define AHT30_STATUS_NOR_MODE (0 << 5) // 正常模式 +#define AHT30_STATUS_CYC_MODE (1 << 5) // 循环模式 +#define AHT30_STATUS_CMD_MODE (2 << 5) // 命令模式 +#define AHT30_STATUS_CRC_FLAG (1 << 4) // CRC标志 +#define AHT30_STATUS_CAL_EN (1 << 3) // 校准使能 +#define AHT30_STATUS_CMP_INT (1 << 2) // 比较中断 + +// 日志标签 +#define AHT30_TAG "AHT30" + +/* ================= 数据结构定义 ================= */ + +/** + * @brief AHT30温湿度数据结构体 + */ +typedef struct { + float temperature; // 温度值(℃) + float humidity; // 湿度值(%RH) + uint8_t valid; // 数据有效标志: 1=有效, 0=无效 + uint32_t timestamp; // 数据时间戳(ms) +} AHT30_Data_t; + +/** + * @brief AHT30设备结构体 + */ +typedef struct { + I2C_HandleTypeDef *hi2c; // I2C句柄 + AHT30_Data_t data; // 传感器数据 + uint8_t initialized; // 初始化标志 +} AHT30_HandleTypeDef; + +/** + * @brief AHT30芯片信息结构体 + */ +typedef struct { + char chip_name[32]; // 芯片名称 + char manufacturer_name[32]; // 制造商名称 + char interface[8]; // 通信接口 + float supply_voltage_min; // 最小供电电压 + float supply_voltage_max; // 最大供电电压 + float max_current; // 最大电流 + float temperature_min; // 最小工作温度 + float temperature_max; // 最大工作温度 +} AHT30_Info_t; + +/* ================= 函数声明 ================= */ + +/** + * @brief 初始化AHT30传感器 + * @param hi2c I2C句柄指针 + * @retval 1=成功, 0=失败 + */ +uint8_t AHT30_Init(I2C_HandleTypeDef *hi2c); + +/** + * @brief 软复位AHT30传感器 + * @retval 1=成功, 0=失败 + */ +uint8_t AHT30_Reset(void); + +/** + * @brief 校准AHT30传感器 + * @retval 1=成功, 0=失败 + */ +uint8_t AHT30_Calibrate(void); + +/** + * @brief 读取AHT30温湿度数据 + * @param data 数据存储指针 + * @retval 1=成功, 0=失败 + */ +uint8_t AHT30_ReadData(AHT30_Data_t *data); + +/** + * @brief 获取最新的温湿度数据 + * @return AHT30数据结构体指针 + */ +AHT30_Data_t* AHT30_GetData(void); + +/** + * @brief 检查AHT30是否已初始化 + * @retval 1=已初始化, 0=未初始化 + */ +uint8_t AHT30_IsInitialized(void); + +/** + * @brief 读取AHT30状态 + * @param status 状态存储指针 + * @retval 1=成功, 0=失败 + */ +uint8_t AHT30_GetStatus(uint8_t *status); + +/** + * @brief 仅读取温度数据 + * @param temperature 温度存储指针 + * @retval 1=成功, 0=失败 + */ +uint8_t AHT30_ReadTemperature(float *temperature); + +/** + * @brief 仅读取湿度数据 + * @param humidity 湿度存储指针 + * @retval 1=成功, 0=失败 + */ +uint8_t AHT30_ReadHumidity(float *humidity); + +/** + * @brief 写寄存器(扩展功能) + * @param buf 数据缓冲区 + * @param len 数据长度 + * @retval 1=成功, 0=失败 + */ +uint8_t AHT30_SetReg(uint8_t *buf, uint16_t len); + +/** + * @brief 读寄存器(扩展功能) + * @param buf 数据缓冲区 + * @param len 数据长度 + * @retval 1=成功, 0=失败 + */ +uint8_t AHT30_GetReg(uint8_t *buf, uint16_t len); + +/** + * @brief 获取芯片信息 + * @param info 信息结构体指针 + * @retval 1=成功, 0=失败 + */ +uint8_t AHT30_GetInfo(AHT30_Info_t *info); + +/** + * @brief 扫描I2C设备(调试用) + * @param hi2c I2C句柄指针 + */ +void AHT30_ScanI2C(I2C_HandleTypeDef *hi2c); + +/** + * @brief 软件I2C扫描(硬件I2C失败时使用) + */ +void AHT30_ScanI2C_Soft(void); + +/** + * @brief AHT30 CRC8校验 + * @param data 数据指针 + * @param len 数据长度 + * @return CRC校验值 + */ +uint8_t AHT30_CRC8(uint8_t *data, uint8_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* __BSP_AHT30_H__ */ diff --git a/Core/Bsp/BSP_Device/spi_st7735s/spi_st7735s.h b/Core/Bsp/BSP_Device/spi_st7735s/spi_st7735s.h index 215c20d..1c2e0a4 100644 --- a/Core/Bsp/BSP_Device/spi_st7735s/spi_st7735s.h +++ b/Core/Bsp/BSP_Device/spi_st7735s/spi_st7735s.h @@ -243,6 +243,7 @@ extern SPI_HandleTypeDef ST7735_SPI_PORT; #define ST7735_CYAN 0x07FF // 青色 #define ST7735_MAGENTA 0xF81F // 洋红 #define ST7735_YELLOW 0xFFE0 // 黄色 +#define ST7735_ORANGE 0xFD20 // 橙色 #define ST7735_WHITE 0xFFFF // 白色 #define ST7735_COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3)) diff --git a/Core/Src/freertos.c b/Core/Src/freertos.c index 32f2ae0..2f6d40c 100644 --- a/Core/Src/freertos.c +++ b/Core/Src/freertos.c @@ -33,6 +33,8 @@ #include "multi_button.h" #include "spi_st7735s.h" #include "stdio.h" +#include "bsp_aht30.h" +#include "i2c.h" /* USER CODE END Includes */ @@ -110,6 +112,13 @@ const osThreadAttr_t button_attributes = { .stack_size = 512 * 4, .priority = (osPriority_t) osPriorityRealtime2, }; +/* Definitions for sensor */ +osThreadId_t sensorHandle; +const osThreadAttr_t sensor_attributes = { + .name = "sensor", + .stack_size = 1024 * 4, + .priority = (osPriority_t) osPriorityNormal, +}; /* Private function prototypes -----------------------------------------------*/ /* USER CODE BEGIN FunctionPrototypes */ @@ -127,6 +136,7 @@ void StartDefaultTask(void *argument); extern void wifi_task_mqtt(void *argument); void LCD_Task(void *argument); void button_task(void *argument); +void sensorTask(void *argument); void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */ @@ -169,6 +179,9 @@ void MX_FREERTOS_Init(void) { /* creation of button */ buttonHandle = osThreadNew(button_task, NULL, &button_attributes); + /* creation of sensor */ + sensorHandle = osThreadNew(sensorTask, NULL, &sensor_attributes); + /* USER CODE BEGIN RTOS_THREADS */ /* add threads, ... */ /* USER CODE END RTOS_THREADS */ @@ -279,13 +292,13 @@ void LCD_Task(void *argument) ST7735_FillScreen(bg_color); ST7735_WriteString(2, 2, "Temp & Humi", &Font_7x10, text_color, bg_color); - // 显示温度 - snprintf(display_str, sizeof(display_str), "T: %.1f C", sensor_data.temperature); - ST7735_WriteString(5, 20, display_str, &Font_11x18, text_color, bg_color); + // 显示温度(橙色 - 国际标准温度色) + snprintf(display_str, sizeof(display_str), "T: %.2f C", sensor_data.temperature); + ST7735_WriteString(5, 20, display_str, &Font_11x18, ST7735_ORANGE, bg_color); - // 显示湿度 - snprintf(display_str, sizeof(display_str), "H: %.1f %%", sensor_data.humidity); - ST7735_WriteString(5, 45, display_str, &Font_11x18, text_color, bg_color); + // 显示湿度(青色 - 国际标准湿度色) + snprintf(display_str, sizeof(display_str), "H: %.2f %%", sensor_data.humidity); + ST7735_WriteString(5, 45, display_str, &Font_11x18, ST7735_CYAN, bg_color); break; case LCD_PAGE_FOOD_WEIGHT: @@ -357,7 +370,7 @@ void LCD_Task(void *argument) } } else { // 其他页面:1秒刷新一次 - osDelay(1000); + osDelay(2000); } } /* USER CODE END LCD_Task */ @@ -384,6 +397,65 @@ void button_task(void *argument) /* USER CODE END button_task */ } +/* USER CODE BEGIN Header_sensorTask */ +/** +* @brief Function implementing the sensor thread. +* @param argument: Not used +* @retval None +*/ +/* USER CODE END Header_sensorTask */ +void sensorTask(void *argument) +{ + /* USER CODE BEGIN sensorTask */ + elog_i(TAG, "启动传感器任务"); + + // 扫描I2C设备(调试) + AHT30_ScanI2C(&hi2c1); + + // 如果硬件I2C扫描失败,尝试软件I2C + uint8_t found = 0; + for (uint8_t addr = 1; addr < 128; addr++) { + if (HAL_I2C_IsDeviceReady(&hi2c1, addr << 1, 1, 50) == HAL_OK) { + found = 1; + break; + } + } + + if (!found) { + elog_w(TAG, "硬件I2C未发现设备,尝试软件I2C扫描..."); + AHT30_ScanI2C_Soft(); + } + + // 初始化AHT30温湿度传感器 + elog_i(TAG, "初始化AHT30温湿度传感器"); + if (AHT30_Init(&hi2c1)) { + elog_i(TAG, "AHT30初始化成功"); + } else { + elog_e(TAG, "AHT30初始化失败"); + } + + /* Infinite loop */ + for(;;) + { + // 读取温湿度数据 + AHT30_Data_t aht30_data; + if (AHT30_ReadData(&aht30_data)) { + // 数据读取成功,更新sensor_data + sensor_data.temperature = aht30_data.temperature; + sensor_data.humidity = aht30_data.humidity; + + elog_d(TAG, "温湿度数据 - 温度: %.2f℃, 湿度: %.2f%%", + aht30_data.temperature, aht30_data.humidity); + } else { + elog_w(TAG, "AHT30数据读取失败"); + } + + // 每2秒读取一次 + osDelay(2000); + } + /* USER CODE END sensorTask */ +} + /* Private application code --------------------------------------------------*/ /* USER CODE BEGIN Application */ diff --git a/Core/Src/i2c.c b/Core/Src/i2c.c index 445f025..c9a78ab 100644 --- a/Core/Src/i2c.c +++ b/Core/Src/i2c.c @@ -74,6 +74,7 @@ void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle) GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* I2C1 clock enable */ diff --git a/hahha.ioc b/hahha.ioc index 273edbd..a1c5f0e 100644 --- a/hahha.ioc +++ b/hahha.ioc @@ -35,7 +35,7 @@ Dma.USART1_TX.0.Priority=DMA_PRIORITY_VERY_HIGH Dma.USART1_TX.0.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority FREERTOS.FootprintOK=true FREERTOS.IPParameters=Tasks01,FootprintOK,configTOTAL_HEAP_SIZE -FREERTOS.Tasks01=defaultTask,41,256,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL;wifi_mqtt,40,3000,wifi_task_mqtt,As external,NULL,Dynamic,NULL,NULL;LCD_SHOW_Task,40,1024,LCD_Task,Default,NULL,Dynamic,NULL,NULL;button,50,512,button_task,Default,NULL,Dynamic,NULL,NULL +FREERTOS.Tasks01=defaultTask,41,256,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL;wifi_mqtt,40,3000,wifi_task_mqtt,As external,NULL,Dynamic,NULL,NULL;LCD_SHOW_Task,40,1024,LCD_Task,Default,NULL,Dynamic,NULL,NULL;button,50,512,button_task,Default,NULL,Dynamic,NULL,NULL;sensor,24,1024,sensorTask,Default,NULL,Dynamic,NULL,NULL FREERTOS.configTOTAL_HEAP_SIZE=30000 File.Version=6 GPIO.groupedBy=Group By Peripherals