扩展要求:
对基于 STM32U575RITx 的智能除湿器项目进行技术解析,内容需涵盖以下方面:
- 硬件配置与初始化(如外设、时钟、电源管理);
- 温湿度采集与处理(含传感器通信、数据滤波及校准);
- 电池电压监测(ADC 采样及电量计算);
- 串口通信与协议解析(双协议支持及中断接收);
- LCD 显示(界面绘制与实时信息更新);
- 控制逻辑(手动模式按键处理、自动模式的模糊控制与学习自适应);
- 系统自检与反馈(开机自检、蜂鸣器及 LCD 反馈);
- 代码结构与优化(模块化设计、低功耗优化)。
核心代码:
main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 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 "adc.h"
#include "i2c.h"
#include "icache.h"
#include "spi.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "gai.h"
#include "bsp_sht20.h"
#include "stdio.h"
#include "string.h"
#include "bsp_ili9341_4line.h"
void SystemSelfTest_Fail(void); // 添加前向声明
//printf实现重定向
int fputc(int ch,FILE * p)
{
while(!(USART1->ISR & (1<<7))) {}
//判断发送数据寄存器是否为空
//第七位为1的时候为空,应该发送数据,此时跳出阻塞循环
USART1->TDR = ch;
return ch;
}
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define FILTER_WINDOW_SIZE 5
#define CMD_HEADER 0xAA
#define CMD_FOOTER 0x55
#define BATTERY_LOW_THRESHOLD 3.0f
#define LONG_PRESS_TIME 1000 // 长按时间阈值(ms)
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
float T,H;//存储温湿度值
float T_filtered, H_filtered; // 滤波后的温湿度值
// 滑动平均滤波数组
float temp_filter_buffer[FILTER_WINDOW_SIZE];
float hum_filter_buffer[FILTER_WINDOW_SIZE];
uint8_t filter_index = 0;
uint8_t filter_count = 0;
uint8_t recv_buf[1024]; //不定长接收缓冲区
uint8_t recv_flag; //不定长接收标志位
char fan[32];
char condenser[32];
char heater[32];
char temperature[32];
char humidity[32];
char Hum_threshold[32];
char Threshold_max[32];
char Threshold_min[32];
char ID[32];
int id=123;
uint8_t vbat[32]; //屏幕显示电压
uint32_t val; //设备电池电压
float Vbat; //电压
char battery_level[32]; // 电池电量百分比
float TempLow = 20.0; //温度下限阀值
float TempHigh = 40.0; //温度上限阀值
float Hum_level = 50.0; //湿度阀值
float temp_calibration_offset = 0.0; // 温度校准偏移量
float hum_calibration_offset = 0.0; // 湿度校准偏移量
int flag = 0; // 手动模式下的标志位,1为调温度上阈值,2为调温度下阈值,3为调湿度阈值
char Flag[32];//屏幕显示调那个阈值
int mode = 0; // 切换自动模式和手动模式 0为自动 1为手动
char Mode[32];//屏幕显示自动或者手动模式 0为自动 1为手动
uint32_t press_start_time[4] = {0}; // 记录按键按下时间
uint8_t is_pressing[4] = {0}; // 记录按键是否正在被按下
uint8_t low_power_mode = 0; // 低功耗模式标志
// 温湿度历史数据,用于学习自适应
float temp_history[24];
float hum_history[24];
uint8_t history_index = 0;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void SystemPower_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
// 中值滤波函数
float median_filter(float *buffer, uint8_t size) {
float sorted[FILTER_WINDOW_SIZE];
memcpy(sorted, buffer, size * sizeof(float));
// 冒泡排序
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - i - 1; j++) {
if (sorted[j] > sorted[j + 1]) {
float temp = sorted[j];
sorted[j] = sorted[j + 1];
sorted[j + 1] = temp;
}
}
}
// 返回中值
return sorted[size / 2];
}
// 滑动平均滤波
void apply_filter(float raw_temp, float raw_hum) {
// 添加新数据到缓冲区
temp_filter_buffer[filter_index] = raw_temp;
hum_filter_buffer[filter_index] = raw_hum;
filter_index = (filter_index + 1) % FILTER_WINDOW_SIZE;
// 计算当前有效数据数量
if (filter_count < FILTER_WINDOW_SIZE) {
filter_count++;
}
// 应用中值滤波
T_filtered = median_filter(temp_filter_buffer, filter_count);
H_filtered = median_filter(hum_filter_buffer, filter_count);
// 应用校准偏移
T_filtered += temp_calibration_offset;
H_filtered += hum_calibration_offset;
}
// 按键消抖检测
uint8_t debounce_check(uint16_t GPIO_Pin) {
static uint8_t state[16] = {0};
state[GPIO_Pin] = (state[GPIO_Pin] << 1) | HAL_GPIO_ReadPin(GPIOA, GPIO_Pin) | 0xfc;
return state[GPIO_Pin] == 0xff;
}
// 蜂鸣器反馈
void beep_feedback(uint8_t times) {
for (uint8_t i = 0; i < times; i++) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, 1); // 假设PC7连接蜂鸣器
HAL_Delay(100);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, 0);
if (i < times - 1) {
HAL_Delay(100);
}
}
}
// 计算电池电量百分比
uint8_t calculate_battery_percentage(float voltage) {
// 假设电池电压范围为2.7V-4.2V
if (voltage >= 4.2) return 100;
if (voltage <= 2.7) return 0;
return (uint8_t)((voltage - 2.7) / (4.2 - 2.7) * 100);
}
// 界面绘制函数
void draw_interface(void) {
// 绘制背景
ILI9341_Clear(WHITE);
// 绘制标题栏
ILI9341_DrawRectangle(0, 0, 240, 25, BLUE);
ILI9341_FillRectangle(1, 1, 238, 24, BLUE);
Gui_DrawFont_GBK16(80, 5, WHITE, BLUE, (uint8_t *)"进阶版智能除湿器");
// 绘制温湿度区域
ILI9341_DrawRectangle(10, 35, 220, 100, BLACK);
// 绘制阈值设置区域
ILI9341_DrawRectangle(10, 110, 220, 160, BLACK);
// 绘制设备状态区域
ILI9341_DrawRectangle(10, 170, 220, 240, BLACK);
// 绘制电池状态区域
ILI9341_DrawRectangle(10, 250, 220, 270, BLACK);
}
//LCD屏幕显示内容
void lcd_display(void) {
// 如果是第一次显示,绘制界面框架
static uint8_t first_display = 1;
if (first_display) {
draw_interface();
first_display = 0;
}
sprintf(temperature, "温度: %.1f°C", T_filtered);
sprintf(humidity, "湿度: %.1f%%", H_filtered);
sprintf(Hum_threshold, "湿度阈值: %.1f%%", Hum_level);
sprintf(Threshold_max, "温度上限: %.1f°C", TempHigh);
sprintf(Threshold_min, "温度下限: %.1f°C", TempLow);
sprintf(ID, "设备ID: %d", id);
sprintf(vbat, "电压: %.2fV", Vbat);
sprintf(Mode, "模式: %s", mode ? "手动" : "自动");
sprintf(Flag, "调整: %s", flag == 0 ? "无" : (flag == 1 ? "温度上限" : (flag == 2 ? "温度下限" : "湿度阈值")));
sprintf(battery_level, "电量: %d%%", calculate_battery_percentage(Vbat));
// 显示温湿度
Gui_DrawFont_GBK16(30, 50, BLACK, WHITE, temperature);
Gui_DrawFont_GBK16(30, 75, BLACK, WHITE, humidity);
// 显示阈值
if(flag == 0) {
Gui_DrawFont_GBK16(30, 125, BLACK, WHITE, Threshold_max);
Gui_DrawFont_GBK16(30, 145, BLACK, WHITE, Threshold_min);
Gui_DrawFont_GBK16(30, 165, BLACK, WHITE, Hum_threshold);
} else if(flag == 1) {
Gui_DrawFont_GBK16(30, 125, BLACK, BLUE, Threshold_max);
Gui_DrawFont_GBK16(30, 145, BLACK, WHITE, Threshold_min);
Gui_DrawFont_GBK16(30, 165, BLACK, WHITE, Hum_threshold);
} else if(flag == 2) {
Gui_DrawFont_GBK16(30, 125, BLACK, WHITE, Threshold_max);
Gui_DrawFont_GBK16(30, 145, BLACK, BLUE, Threshold_min);
Gui_DrawFont_GBK16(30, 165, BLACK, WHITE, Hum_threshold);
} else if(flag == 3) {
Gui_DrawFont_GBK16(30, 125, BLACK, WHITE, Threshold_max);
Gui_DrawFont_GBK16(30, 145, BLACK, WHITE, Threshold_min);
Gui_DrawFont_GBK16(30, 165, BLACK, BLUE, Hum_threshold);
}
// 显示设备状态
Gui_DrawFont_GBK16(30, 185, BLACK, WHITE, fan);
Gui_DrawFont_GBK16(30, 205, BLACK, WHITE, condenser);
Gui_DrawFont_GBK16(30, 225, BLACK, WHITE, heater);
// 显示电池状态
Gui_DrawFont_GBK16(30, 260, BLACK, WHITE, battery_level);
// 显示模式和调整状态
Gui_DrawFont_GBK16(30, 285, BLACK, WHITE, Mode);
Gui_DrawFont_GBK16(150, 285, BLACK, WHITE, Flag);
// 低电量警告
if (Vbat < BATTERY_LOW_THRESHOLD) {
ILI9341_DrawRectangle(0, 290, 240, 320, RED);
ILI9341_FillRectangle(1, 291, 238, 318, RED);
Gui_DrawFont_GBK16(80, 300, WHITE, RED, (uint8_t *)"电量低!");
}
}
// 串口通信协议解析
void parse_uart_command(void) {
// 检查包头和包尾
if (recv_buf[0] == CMD_HEADER && recv_buf[recv_buf[1] + 2] == CMD_FOOTER) {
uint8_t len = recv_buf[1];
uint8_t cmd = recv_buf[2];
uint8_t *data = &recv_buf[3];
uint8_t checksum = recv_buf[len + 1];
// 计算校验和
uint8_t calculated_checksum = 0;
for (int i = 0; i < len; i++) {
calculated_checksum ^= recv_buf[i + 2];
}
// 校验和验证
if (calculated_checksum == checksum) {
// 命令处理
switch (cmd) {
case 0x01: // 设置湿度阈值
Hum_level = *((float *)data);
printf("设置湿度阈值: %.1f%%\n", Hum_level);
beep_feedback(1);
break;
case 0x02: // 设置温度上限
TempHigh = *((float *)data);
printf("设置温度上限: %.1f°C\n", TempHigh);
beep_feedback(1);
break;
case 0x03: // 设置温度下限
TempLow = *((float *)data);
printf("设置温度下限: %.1f°C\n", TempLow);
beep_feedback(1);
break;
case 0x04: // 设置工作模式
mode = data[0];
printf("设置工作模式: %s\n", mode ? "手动" : "自动");
beep_feedback(1);
break;
case 0x05: // 控制风扇
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, data[0]);
sprintf(fan, "风扇: %s", data[0] ? "开启" : "关闭");
printf("风扇控制: %s\n", data[0] ? "开启" : "关闭");
beep_feedback(1);
break;
case 0x06: // 控制制冷器
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, data[0]);
sprintf(condenser, "制冷器: %s", data[0] ? "开启" : "关闭");
printf("制冷器控制: %s\n", data[0] ? "开启" : "关闭");
beep_feedback(1);
break;
case 0x07: // 控制加热器
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, data[0]);
sprintf(heater, "加热器: %s", data[0] ? "开启" : "关闭");
printf("加热器控制: %s\n", data[0] ? "开启" : "关闭");
beep_feedback(1);
break;
case 0x08: // 校准温湿度
temp_calibration_offset = *((float *)data);
hum_calibration_offset = *((float *)(data + 4));
printf("温湿度校准: 温度偏移=%.1f°C, 湿度偏移=%.1f%%\n",
temp_calibration_offset, hum_calibration_offset);
beep_feedback(2);
break;
default:
printf("未知命令: 0x%02X\n", cmd);
break;
}
} else {
printf("校验和错误\n");
}
} else {
printf("协议格式错误\n");
}
}
// 发送温湿度数据到上位机
void send_environment_data(void) {
uint8_t buffer[32];
uint8_t index = 0;
buffer[index++] = CMD_HEADER;
buffer[index++] = 12; // 数据长度
buffer[index++] = 0x10; // 数据类型:环境数据
// 添加温度数据
float *temp_ptr = &T_filtered;
memcpy(&buffer[index], temp_ptr, 4);
index += 4;
// 添加湿度数据
float *hum_ptr = &H_filtered;
memcpy(&buffer[index], hum_ptr, 4);
index += 4;
// 添加电池电压
float *vbat_ptr = &Vbat;
memcpy(&buffer[index], vbat_ptr, 4);
index += 4;
// 计算校验和
uint8_t checksum = 0;
for (int i = 2; i < index; i++) {
checksum ^= buffer[i];
}
buffer[index++] = checksum;
buffer[index++] = CMD_FOOTER;
// 发送数据
HAL_UART_Transmit(&huart1, buffer, index, 100);
}
// 接收中断处理
void uart_handle(void) {
if (recv_flag==1) {
// 检查是否是新协议格式
if (recv_buf[0] == CMD_HEADER) {
parse_uart_command();
} else {
// 兼容旧协议处理
if (strncmp(recv_buf, "123", 3)==0) {
if (strstr(recv_buf, "Hum_level=")) {
sscanf(recv_buf, "123 Hum_level=%f", &Hum_level);
printf("设置湿度阈值: %.1f%%\n", Hum_level);
} else if (strstr(recv_buf, "TempHigh=")) {
sscanf(recv_buf, "123 TempHigh=%f", &TempHigh);
printf("设置温度上限: %.1f°C\n", TempHigh);
} else if (strstr(recv_buf, "TempLow=")) {
sscanf(recv_buf, "123 TempLow=%f", &TempLow);
printf("设置温度下限: %.1f°C\n", TempLow);
} else if (strstr(recv_buf, "mode=")) {
sscanf(recv_buf, "123 mode=%d", &mode);
printf("设置工作模式: %s\n", mode ? "手动" : "自动");
} else if (strstr(recv_buf, "123 fan_on")) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, 1);
sprintf(fan, "fan : ON ");
printf("风扇开启\n");
} else if (strstr(recv_buf, "123 fan_off")) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, 0);
sprintf(fan, "fan : OFF");
printf("风扇关闭\n");
} else if (strstr(recv_buf, "123 heater_on")) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 1);
sprintf(heater, "heater : ON ");
printf("加热器开启\n");
} else if (strstr(recv_buf, "123 heater_off")) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 0);
sprintf(heater, "heater : OFF");
printf("加热器关闭\n");
} else if (strstr(recv_buf, "123 condenser_on")) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, 1);
sprintf(condenser, "condenser : ON ");
printf("制冷器开启\n");
} else if (strstr(recv_buf, "123 condenser_off")) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, 0);
sprintf(condenser, "condenser : OFF");
printf("制冷器关闭\n");
}
} else {
printf("未识别到该设备!\n");
}
}
}
recv_flag = 0;
memset(recv_buf, 0, 1024);
}
// 模糊控制算法
void fuzzy_control(void) {
// 计算温湿度偏差
float temp_diff = T_filtered - ((TempHigh + TempLow) / 2);
float hum_diff = H_filtered - Hum_level;
// 模糊化处理
// 这里简化实现,实际应用需要更复杂的模糊规则
uint8_t fan_speed = 0;
uint8_t condenser_strength = 0;
uint8_t heater_strength = 0;
// 湿度控制
if (hum_diff > 5) {
fan_speed = 100;
condenser_strength = 100;
} else if (hum_diff > 2) {
fan_speed = 70;
condenser_strength = 70;
} else if (hum_diff < -5) {
fan_speed = 0;
condenser_strength = 0;
} else if (hum_diff < -2) {
fan_speed = 30;
condenser_strength = 30;
}
// 温度控制
if (temp_diff > 5) {
heater_strength = 0;
condenser_strength = 100;
} else if (temp_diff > 2) {
heater_strength = 0;
condenser_strength = 70;
} else if (temp_diff < -5) {
heater_strength = 100;
condenser_strength = 0;
} else if (temp_diff < -2) {
heater_strength = 70;
condenser_strength = 0;
}
// 输出控制
if (fan_speed > 0) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, 1);
sprintf(fan, "风扇: 开启(%d%%)", fan_speed);
} else {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, 0);
sprintf(fan, "风扇: 关闭");
}
if (condenser_strength > 0) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, 1);
sprintf(condenser, "制冷器: 开启(%d%%)", condenser_strength);
} else {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, 0);
sprintf(condenser, "制冷器: 关闭");
}
if (heater_strength > 0) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 1);
sprintf(heater, "加热器: 开启(%d%%)", heater_strength);
} else {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 0);
sprintf(heater, "加热器: 关闭");
}
}
// 学习自适应算法
void adaptive_learning(void) {
// 保存当前温湿度到历史记录
temp_history[history_index] = T_filtered;
hum_history[history_index] = H_filtered;
history_index = (history_index + 1) % 24;
// 简单的自适应算法:根据过去24小时的温湿度波动调整阈值
float max_temp = temp_history[0];
float min_temp = temp_history[0];
float avg_hum = 0;
for (int i = 0; i < 24; i++) {
if (temp_history[i] > max_temp) max_temp = temp_history[i];
if (temp_history[i] < min_temp) min_temp = temp_history[i];
avg_hum += hum_history[i];
}
avg_hum /= 24;
// 根据历史数据动态调整阈值
// 这里只是简单示例,实际应用需要更复杂的算法
TempHigh = max_temp + 2;
TempLow = min_temp - 2;
Hum_level = avg_hum;
// 确保阈值在合理范围内
if (TempHigh > 45) TempHigh = 45;
if (TempLow < 15) TempLow = 15;
if (Hum_level > 80) Hum_level = 80;
if (Hum_level < 30) Hum_level = 30;
}
// 自动模式控制
void auto_control(void) {
if (mode == 0) {
// 应用模糊控制算法
fuzzy_control();
// 定期执行学习自适应
static uint32_t last_adaptive_time = 0;
if (HAL_GetTick() - last_adaptive_time > 3600000) { // 每小时执行一次
adaptive_learning();
last_adaptive_time = HAL_GetTick();
}
}
}
// 进入低功耗模式
void enter_low_power_mode(void) {
// 关闭不必要的外设
HAL_ADC_Stop(&hadc4);
HAL_UART_Abort(&huart1);
// 关闭LCD背光
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, 0);
// 设置系统时钟为最低频率
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_0;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
|RCC_CLOCKTYPE_PCLK3;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) {
Error_Handler();
}
// 进入停止模式
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
}
// 开机自检
void SystemSelfTest(void) {
// 显示开机画面
ILI9341_Clear(BLUE);
Gui_DrawFont_GBK16(60, 120, WHITE, BLUE, (uint8_t *)"系统启动中...");
// 初始化温湿度滤波数组
for (int i = 0; i < FILTER_WINDOW_SIZE; i++) {
temp_filter_buffer[i] = 25.0;
hum_filter_buffer[i] = 50.0;
}
// 读取温湿度传感器
if (BSP_SHT20_Init() != 0) {
SystemSelfTest_Fail();
}
// 测试LCD
Gui_DrawFont_GBK16(60, 140, WHITE, BLUE, (uint8_t *)"LCD测试中...");
HAL_Delay(500);
// 测试蜂鸣器
Gui_DrawFont_GBK16(60, 160, WHITE, BLUE, (uint8_t *)"蜂鸣器测试中...");
beep_feedback(1);
HAL_Delay(500);
// 测试GPIO输出
Gui_DrawFont_GBK16(60, 180, WHITE, BLUE, (uint8_t *)"外设测试中...");
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, 1); // 风扇
HAL_Delay(200);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, 0);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, 1); // 制冷器
HAL_Delay(200);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, 0);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 1); // 加热器
HAL_Delay(200);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 0);
HAL_Delay(1000);
}
void SystemSelfTest_Fail(void) {
ILI9341_Clear(RED);
Gui_DrawFont_GBK16(60, 120, WHITE, RED, (uint8_t *)"系统初始化失败!");
while(1) {
beep_feedback(3);
HAL_Delay(1000);
}
}
/* 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();
/* Configure the System Power */
SystemPower_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART1_UART_Init();
MX_SPI1_Init();
MX_ICACHE_Init();
MX_TIM6_Init();
MX_ADC4_Init();
MX_TIM17_Init();
/* USER CODE BEGIN 2 */
// 开机自检
SystemSelfTest();
ILI9341_Init(); //初始化屏幕
draw_interface(); //绘制界面
// 初始化显示内容
sprintf(fan, "风扇: 关闭");
sprintf(condenser, "制冷器: 关闭");
sprintf(heater, "加热器: 关闭");
//不定长接收中断
HAL_UART_Receive_IT(&huart1,recv_buf,1024); //开启接收中断
__HAL_UART_ENABLE_IT(&huart1,UART_FLAG_IDLE); //开启空闲中断
HAL_PWREx_EnableVddA(); //启用VDDA电压
HAL_PWREx_EnableVddIO2(); //启用VDDIO电压
HAL_ADCEx_Calibration_Start(&hadc4,ADC_CALIB_OFFSET,ADC_SINGLE_ENDED); //校准单端ADC采样
//启动ADC转换
HAL_ADC_Start(&hadc4);
HAL_ADC_PollForConversion(&hadc4,100); //等待转换完成,第二个参数表示超时时间,单位ms
val=HAL_ADC_GetValue(&hadc4); //获取ADC转换结果
Vbat=(val*4*3.3)/4095; //转换成真实电压值
if(Vbat>0) {
HAL_TIM_Base_Start_IT(&htim17); //开启定时,用于定时两秒上报数据
recv_flag = 0; //不定长接收标志位初始化
} else {
Gui_DrawFont_GBK16(40, 150, WHITE, BLUE, (uint8_t *)"电池电压异常");
while(1);
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
// 低功耗模式处理
if (low_power_mode) {
enter_low_power_mode();
}
// 温湿度采集
if (BSP_SHT20_GetData() == 0) {
T = BSP_SHT20_GetTemperature();
H = BSP_SHT20_GetHumidity();
// 应用滤波
apply_filter(T, H);
} else {
// 传感器读取失败,使用默认值
T = 25.0;
H = 50.0;
apply_filter(T, H);
}
// 电池电压采集
HAL_ADC_Start(&hadc4);
HAL_ADC_PollForConversion(&hadc4, 100);
val = HAL_ADC_GetValue(&hadc4);
Vbat = (val*4*3.3)/4095;
// 低电量检测
if (Vbat < BATTERY_LOW_THRESHOLD) {
static uint32_t last_warning_time = 0;
if (HAL_GetTick() - last_warning_time > 5000) {
beep_feedback(2);
last_warning_time = HAL_GetTick();
}
// 极低电量时自动进入低功耗模式
if (Vbat < BATTERY_LOW_THRESHOLD - 0.3) {
low_power_mode = 1;
}
}
uart_handle(); // 串口通信处理
auto_control(); // 自动模式控制
lcd_display(); // LCD显示更新
// 定时发送环境数据
static uint32_t last_send_time = 0;
if (HAL_GetTick() - last_send_time > 5000) {
send_environment_data();
last_send_time = HAL_GetTick();
}
// 长按检测
for (int i = 0; i < 4; i++) {
uint16_t pin = (i == 0) ? GPIO_PIN_12 :
(i == 1) ? GPIO_PIN_9 :
(i == 2) ? GPIO_PIN_8 : GPIO_PIN_5;
if (HAL_GPIO_ReadPin(GPIOA, pin) == 0) {
if (!is_pressing[i]) {
is_pressing[i] = 1;
press_start_time[i] = HAL_GetTick();
} else if (HAL_GetTick() - press_start_time[i] > LONG_PRESS_TIME) {
// 长按处理
if (i == 0) { // 模式切换按键
// 长按切换到自动模式并重置阈值
mode = 0;
Hum_level = 50.0;
TempHigh = 30.0;
TempLow = 20.0;
beep_feedback(2);
printf("长按模式切换: 自动模式并重置阈值\n");
} else if (i == 1 && mode == 1) { // 调整按键
// 长按快速调整阈值
if (flag == 1) {
TempHigh += 5.0;
if (TempHigh > 45.0) TempHigh = 45.0;
} else if (flag == 2) {
TempLow += 5.0;
if (TempLow > 40.0) TempLow = 40.0;
} else if (flag == 3) {
Hum_level += 10.0;
if (Hum_level > 80.0) Hum_level = 80.0;
}
beep_feedback(2);
}
is_pressing[i] = 0;
}
} else {
is_pressing[i] = 0;
}
}
// 简单的省电延时
HAL_Delay(200);
/* USER CODE END 3 */
}
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_0;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI;
RCC_OscInitStruct.PLL.PLLMBOOST = RCC_PLLMBOOST_DIV4;
RCC_OscInitStruct.PLL.PLLM = 3;
RCC_OscInitStruct.PLL.PLLN = 10;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 2;
RCC_OscInitStruct.PLL.PLLR = 1;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLLVCIRANGE_1;
RCC_OscInitStruct.PLL.PLLFRACN = 0;
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_CLOCKTYPE_PCLK3;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief Power Configuration
* @retval None
*/
static void SystemPower_Config(void)
{
/*
* Disable the internal Pull-Up in Dead Battery pins of UCPD peripheral
*/
HAL_PWREx_DisableUCPDDeadBattery();
/* USER CODE BEGIN PWR */
/* USER CODE END PWR */
}
/* USER CODE BEGIN 4 */
// 按键中断回调函数
void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
{
HAL_TIM_Base_Start_IT(&htim6); //定时器消抖
}
// 定时器回调
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim == &htim17) //定时两秒上报数据
{
printf("id:%d,flag:%d,mode:%d\ntemperature:%.2f, humidity:%.2f,Hum_level:%.2f\nTempHigh:%.2f,TempLow:%.2f,Vbat:%.2f\n",
id, flag, mode, T_filtered, H_filtered, Hum_level, TempHigh, TempLow, Vbat);
send_environment_data();
}
if(htim == &htim6)//消抖,按键操作
{
if (HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12)==0)//USER按键 PA12 切换模式
{
mode=1-mode;//0为自动,1为手动
beep_feedback(1);
if (mode == 0) {
sprintf(Mode, "模式: 自动");
printf("切换到自动模式\n");
} else {
sprintf(Mode, "模式: 手动");
printf("切换到手动模式\n");
flag = 0; // 重置调整标志
}
// 切换模式时关闭所有设备
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6 | GPIO_PIN_4 | GPIO_PIN_13, 0);
sprintf(fan, "风扇: 关闭");
sprintf(condenser, "制冷器: 关闭");
sprintf(heater, "加热器: 关闭");
}
if(mode==1) //手动模式
{
if (HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_9)==0)//KEY1 PC9 选择调整项
{
flag++;
if(flag>3)
flag=0; //flag=0表示确认
beep_feedback(1);
printf("选择调整项: %d\n", flag);
}
if(flag==1)//温度上阈值
{
if (HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_8)==0) { // 增加
TempHigh += 0.5;
if (TempHigh > 45.0) TempHigh = 45.0;
beep_feedback(1);
printf("温度上限增加到: %.1f°C\n", TempHigh);
}
if (HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5)==0) { // 减少
TempHigh -= 0.5;
if (TempHigh < TempLow + 2) TempHigh = TempLow + 2;
beep_feedback(1);
printf("温度上限减少到: %.1f°C\n", TempHigh);
}
}
if(flag==2)//温度下阈值
{
if (HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_8)==0) { // 增加
TempLow += 0.5;
if (TempLow > TempHigh - 2) TempLow = TempHigh - 2;
beep_feedback(1);
printf("温度下限增加到: %.1f°C\n", TempLow);
}
if (HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5)==0) { // 减少
TempLow -= 0.5;
if (TempLow < 15.0) TempLow = 15.0;
beep_feedback(1);
printf("温度下限减少到: %.1f°C\n", TempLow);
}
}
if(flag==3)//湿度阈值
{
if (HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_8)==0) { // 增加
Hum_level += 1.0;
if (Hum_level > 80.0) Hum_level = 80.0;
beep_feedback(1);
printf("湿度阈值增加到: %.1f%%\n", Hum_level);
}
if (HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5)==0) { // 减少
Hum_level -= 1.0;
if (Hum_level < 30.0) Hum_level = 30.0;
beep_feedback(1);
printf("湿度阈值减少到: %.1f%%\n", Hum_level);
}
}
}
HAL_TIM_Base_Stop_IT(&htim6);//取消消抖
}
}
/* 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
核心技术:
1. 硬件配置与初始化
- 外设配置:通过 STM32CubeMX 配置 I2C(用于 SHT20 温湿度传感器)、UART(串口通信)、ADC(电池电压采集)、GPIO(控制风扇、制冷器、加热器等)、TIM(定时功能)等。
- 时钟与电源管理:配置系统时钟确保各模块稳定运行,电源管理支持低功耗模式,在电量不足时关闭非必要外设,进入停止模式降低功耗。
2. 温湿度采集与处理
- 传感器通信:使用 I2C 协议与 SHT20 通信,
BSP_SHT20_Read
函数发送指令并读取寄存器数据,BSP_SHT20_GetData
将原始数据转换为实际温湿度值(基于官方公式)。 - 数据处理:采用滑动平均滤波和中值滤波提升数据稳定性,支持温湿度校准(通过偏移量
temp_calibration_offset
、hum_calibration_offset
)。
3. 电池电压监测
- ADC 采样:通过 ADC 采集电池电压,转换公式
Vbat=(val*4*3.3)/4095
将 ADC 值转为实际电压。 - 电量计算:根据电压范围(2.7V-4.2V)计算电量百分比,低电量时触发警告并支持进入低功耗模式。
4. 串口通信与协议解析
- 双协议支持:
- 新协议:带包头(
CMD_HEADER
)、包尾(CMD_FOOTER
)、校验和,确保数据可靠性,支持设置阈值、控制设备等命令。 - 老协议:通过字符串匹配(如
123 fan_on
)兼容旧指令。
- 新协议:带包头(
- 中断接收:利用 UART 中断和空闲中断实现不定长数据接收,
uart_handle
函数解析并处理指令。
5. LCD 显示
- 界面绘制:使用 ILI9341 驱动,
draw_interface
绘制界面框架,lcd_display
实时更新温湿度、设备状态(风扇、制冷器、加热器)、阈值、工作模式(自动 / 手动)、电池信息等。
6. 控制逻辑
- 手动模式:通过按键切换调整项(温度上限、下限,湿度阈值),支持增减操作,按键处理包含消抖和长按逻辑。
- 自动模式:
- 模糊控制:根据温湿度偏差(
temp_diff
、hum_diff
)调整设备(风扇、制冷器、加热器)工作状态与强度。 - 学习自适应:每小时记录温湿度历史数据,动态调整阈值(
TempHigh
、TempLow
、Hum_level
),适应环境变化。
- 模糊控制:根据温湿度偏差(
7. 系统自检与反馈
- 开机自检:
SystemSelfTest
函数测试温湿度传感器、LCD、蜂鸣器、GPIO 输出,确保硬件正常。 - 反馈机制:蜂鸣器反馈(
beep_feedback
)提示操作成功 / 失败,LCD 实时显示系统状态与警告(如低电量)。
8. 代码结构与优化
- 模块化设计:各功能(如温湿度处理、通信、显示)独立成模块,便于维护与扩展。
- 低功耗优化:在低电量时关闭 ADC、UART 等外设,调整系统时钟,进入 STOP 模式降低功耗。
技术原理:
1. 硬件感知与数据采集
- 温湿度采集:通过 I²C 接口与 SHT20 温湿度传感器通信,获取环境温湿度原始数据,再依据公式将其转换为实际物理量(如温度
T = -46.85f + 175.72f * ((float)pTem / 65536)
,湿度H = -6 + 125 * ((float)pHum / 65536)
)。 - 电池电压监测:利用 ADC 模块实时采集电池电压,通过公式
Vbat = (val * 4 * 3.3) / 4095
(val
为 ADC 转换值)计算实际电压,用于电量评估与低电量保护。
2. 数据处理与优化
- 滤波处理:采用滑动平均滤波和中值滤波算法,降低温湿度数据噪声,提升数据稳定性。
- 校准调整:支持温湿度校准,通过设置偏移量(
temp_calibration_offset
、hum_calibration_offset
)修正传感器误差。
3. 控制逻辑实现
- 自动模式:
- 模糊控制:根据温湿度偏差(
temp_diff = T_filtered - ((TempHigh + TempLow) / 2)
,hum_diff = H_filtered - Hum_level
),调整风扇、制冷器、加热器的工作状态与强度,实现智能除湿、控温。 - 学习自适应:每小时记录温湿度历史数据,动态调整温湿度阈值(
TempHigh
、TempLow
、Hum_level
),适应环境变化。
- 模糊控制:根据温湿度偏差(
- 手动模式:通过按键切换调整项(温度上限、下限,湿度阈值),支持增减操作,满足个性化需求。
4. 人机交互与通信
- LCD 显示:实时展示温湿度、设备状态(风扇、制冷器、加热器)、阈值、工作模式(自动 / 手动)、电池电压及电量等信息,界面清晰直观。
- 串口通信:
- 双协议支持:新协议(带包头
CMD_HEADER
、包尾CMD_FOOTER
、校验和)确保数据可靠,支持设置阈值、控制设备等命令;兼容旧协议(如123 fan_on
等字符串指令)。 - 中断接收:利用 UART 中断和空闲中断实现不定长数据接收,解析上位机指令并执行相应操作(如设备控制、参数设置)。
- 定时上报:每 2 秒向上位机发送设备状态信息(温湿度、电压、工作模式等),便于远程监控。
- 双协议支持:新协议(带包头
5. 系统自检与保护
- 开机自检:上电后检测电池电压、温湿度传感器、LCD、蜂鸣器、GPIO 输出等,确保硬件正常,异常时通过 LCD 和蜂鸣器提示。
- 低功耗保护:电池电压低于阈值(如
BATTERY_LOW_THRESHOLD = 3.0f
)时,关闭非必要外设,进入低功耗模式(STOP 模式),降低能耗。
6. 执行机构控制
- 根据温湿度阈值与当前环境参数,驱动风扇(GPIOC_PIN_6)、制冷器(GPIOC_PIN_4)、加热器(GPIOC_PIN_13)等执行机构,实现除湿、控温功能。例如,湿度高于阈值时,开启风扇和制冷器;温度低于阈值时,启动加热器。