feat: 添加AHT30温湿度传感器驱动及显示功能
实现AHT30温湿度传感器的I2C驱动 新增传感器任务定期读取数据 优化LCD显示界面,增加温湿度显示 添加I2C硬件和软件扫描功能 修复I2C引脚配置问题
This commit is contained in:
@@ -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
|
||||
|
||||
580
Core/Bsp/BSP_Device/bsp_aht30/bsp_aht30.c
Normal file
580
Core/Bsp/BSP_Device/bsp_aht30/bsp_aht30.c
Normal file
@@ -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 <string.h>
|
||||
|
||||
/* ================= 全局变量 ================= */
|
||||
|
||||
// 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;
|
||||
}
|
||||
193
Core/Bsp/BSP_Device/bsp_aht30/bsp_aht30.h
Normal file
193
Core/Bsp/BSP_Device/bsp_aht30/bsp_aht30.h
Normal file
@@ -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 <stdint.h>
|
||||
#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__ */
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user