单片机大项目:智能除湿器

项目清单:

一.硬件配置清单

二.核心代码补充

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"

//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 */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* 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 T,H;//存储温湿度值

uint8_t recv_buf[1024]; //不定长接收缓冲区
uint8_t recv_flag;  //不定长接收标志位

uint8_t fan[32];           // 显示风机
uint8_t condenser[32];     // 显示制冷器
uint8_t heater[32];        // 显示加热器

uint8_t temperature[32];   // 显示温度
uint8_t humidity[32];      // 显示湿度

uint8_t Hum_threshold[32]; // 显示湿度阈值
uint8_t Threshold_max[32]; // 显示温度上阈值
uint8_t Threshold_min[32]; // 显示温度下阈值


uint8_t ID[32]; //显示设备ID
int id=123;

uint8_t vbat[32]; //屏幕显示电压
uint32_t val; //设备电池电压
float Vbat; //电压


float TempLow = 20.0; //温度下限阀值
float TempHigh = 40.0; //温度上限阀值
float Hum_level = 50.0; //湿度阀值


int flag = 0; // 手动模式下的标志位,1为调温度上阈值,2为调温度下阈值,3为调湿度阈值
uint8_t Flag[32];//屏幕显示调那个阈值

int mode = 0; // 切换自动模式和手动模式 0为自动 1为手动
uint8_t Mode[32]; //屏幕显示自动或者手动模式 0为自动 1为手动

void lcd_display(void);//LCD屏幕显示
void uart_handle(void);// 不定长接收中断操作
void auto_control(void);//自动模式


//开机自检
void SystemSelfTest(void);
void SystemSelfTest_Fail(void);

/* 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 */

  ILI9341_Init();        //初始化屏幕
  //ILI9341_Clear(0xf81f);  //清屏并显示颜色
  //参数:X轴;Y轴;字色;背景色,显示数据
  //Gui_DrawFont_GBK16(10,30,BLACK,0xbaba,"gaigai666");


  //不定长接收中断
  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;     //不定长接收标志位初始化


  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
    while (1)
    {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */


      //温湿度采集
      BSP_SHT20_GetData();


      //启动ADC转换
      HAL_ADC_Start(&hadc4);
      HAL_ADC_PollForConversion(&hadc4, 100); // 等待转换完成,第二个参数表示超时时间,单位ms
      val = HAL_ADC_GetValue(&hadc4);  // 获取ADC转换结果
      Vbat = (val*4*3.3)/4095; //转换成真实电压值


      uart_handle(); // 串口发送操作
      auto_control(); // 自动模式的操作
      lcd_display();  // LCD显示
    }
  }
  else
  {
   
    Gui_DrawFont_GBK16(40, 150, WHITE, BLUE, (uint8_t *)"Sorry Failed");//蓝屏并显示失败
  }

  /* 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",id, flag, mode, T, H, Hum_level, TempHigh, TempLow, Vbat);
  }


  if(htim == &htim6)//消抖,按键操作
  {
    if (HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12)==0)//USER按键  PA12  切换模式
    {
      mode=1-mode;//0为自动,1为手动
      HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6 | GPIO_PIN_4 | GPIO_PIN_13, 0);//全都关闭
      strcpy(fan, "fan : OFF");
      strcpy(condenser, "condenser : OFF");
      strcpy(heater, "heater : OFF");
    }

    if(mode==1) //手动模式
    {

      if (HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_9)==0)//KEY1 PC9
      {
        flag++;
        if(flag>3)
          flag=0; //flag=0表示确认
      }

      if(flag==1)//温度上阈值
      {
        if (HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_8)==0)
          TempHigh++;
        if (HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5)==0)
          TempHigh--;
      }

      if(flag==2)//温度下阈值
      {
        if (HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_8)==0)
          TempLow++;
        if (HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5)==0)
          TempLow--;
      }

      if(flag==3)//湿度阈值
      {
        if (HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_8)==0)
          Hum_level++;
        if (HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5)==0)
          Hum_level--;
      }
    }
    HAL_TIM_Base_Stop_IT(&htim6);//取消消抖
  }
}


//LCD屏幕显示内容
void lcd_display(void)
{
  sprintf(temperature, "temperature : %.2f",T);
  sprintf(humidity, "humidity : %.2f ", H);
  sprintf(Hum_threshold, "Hum_level : %.2f", Hum_level);
  sprintf(Threshold_max, "Tem_High : %.2f", TempHigh);
  sprintf(Threshold_min, "Tem_Low : %.2f", TempLow);
  sprintf(ID, "ID : %d", id);
  sprintf(vbat, "vbat : %.2f", Vbat);
  sprintf(Mode, "Mode : %d ", mode);
  sprintf(Flag, "Flag : %d ", flag);

  Gui_DrawFont_GBK16(45, 30, BLUE, WHITE, (uint8_t *)"Smart dehumidifier");//大标题,智能除湿器

  Gui_DrawFont_GBK16(20, 65, BLACK, WHITE, temperature);// 显示温湿度
  Gui_DrawFont_GBK16(20, 90, BLACK, WHITE, humidity);

  if(flag==0)
  {
    Gui_DrawFont_GBK16(20, 125, BLACK, WHITE, Threshold_max);// 显示温度上阀值
    Gui_DrawFont_GBK16(20, 145, BLACK, WHITE, Threshold_min);// 显示温度下阀值
    Gui_DrawFont_GBK16(20, 165, BLACK, WHITE, Hum_threshold);// 显示湿度阀值
  }

  if(flag == 1)
    Gui_DrawFont_GBK16(20, 125, BLACK, BLUE, Threshold_max);// 显示温度上阀值
  else
    Gui_DrawFont_GBK16(20, 125, BLACK, WHITE, Threshold_max);// 显示温度上阀值

  if(flag == 2)
    Gui_DrawFont_GBK16(20, 145, BLACK, BLUE, Threshold_min);// 显示温度下阀值
  else
    Gui_DrawFont_GBK16(20, 145, BLACK, WHITE, Threshold_min);// 显示温度下阀值

  if(flag == 3)
    Gui_DrawFont_GBK16(20, 165, BLACK, BLUE, Hum_threshold);// 显示湿度阀值
  else
    Gui_DrawFont_GBK16(20, 165, BLACK, WHITE, Hum_threshold);// 显示湿度阀值


  Gui_DrawFont_GBK16(35, 190, BLACK, WHITE, fan);//显示风扇开关情况
  Gui_DrawFont_GBK16(35, 205, BLACK, WHITE, condenser);// 显示制冷机开关情况
  Gui_DrawFont_GBK16(35, 220, BLACK, WHITE, heater);// 显示加热片开关情况

  Gui_DrawFont_GBK16(20, 240, BLACK, WHITE, ID);// 设备ID
  Gui_DrawFont_GBK16(20, 260, BLACK, WHITE, vbat);// 显示电压
  Gui_DrawFont_GBK16(20, 280, BLACK, WHITE, Mode);// 显示模式
  Gui_DrawFont_GBK16(100, 280, BLACK, WHITE, Flag);// 显示调那个阈值
}

/*
strstr(recv_buf, "Hum_level=");
strstr 从 recv_buf 的开头开始扫描,寻找子字符串 "Hum_level="。
如果找到 "Hum_level=",strstr 返回一个指向该子字符串在 USART1_RxBuff 中起始位置的指针。
如果未找到 "Hum_level=",strstr 返回 NULL。

sscanf(recv_buf, "Hum_level= %f", &Hum_level);
sscanf 从 recv_buf 的开头开始扫描,寻找与 "Hum_level= " 匹配的文本
一旦找到匹配的部分,sscanf 会继续扫描,寻找一个浮点数(由 %f 指定)
如果找到一个有效的浮点数,sscanf 会将其值存储到 Hum_level
*/

// 接收中断处理
void uart_handle(void)
{
  if (recv_flag==1)
  {
    if (strncmp(recv_buf, "123", 3)==0)//识别设备ID
    {
      if (strstr(recv_buf, "Hum_level="))
      {
        sscanf(recv_buf, "123 Hum_level=%f", &Hum_level);
      }
      else if (strstr(recv_buf, "TempHigh="))
      {
        sscanf(recv_buf, "123 TempHigh=%f", &TempHigh);
      }
      else if (strstr(recv_buf, "TempLow="))
      {
        sscanf(recv_buf, "123 TempLow=%f", &TempLow);
      }
      else if (strstr(recv_buf, "mode="))
      {
        sscanf(recv_buf, "123 mode=%d", &mode);
      }
      else if (strstr(recv_buf, "123 fan_on"))
      {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, 1);//开启风扇
      }
      else if (strstr(recv_buf, "123 fan_off"))
      {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, 0);//关闭风扇
      }
      else if (strstr(recv_buf, "123 heater_on"))
      {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 1);//开启LD3加热器
      }
      else if (strstr(recv_buf, "123 heater_off"))
      {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 0);//关闭LD3加热器
      }

      else if (strstr(recv_buf, "123 condenser_on"))
      {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, 1);//开启LD1制冷器
      }

      else if (strstr(recv_buf, "123 condenser_on"))
      {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, 0);//关闭LD1制冷器
      }
    }
    else
    {
      printf("未识别到该设备!\n");
    }
  }
  recv_flag = 0;//重置接收标志位
  memset(recv_buf, 0, 1024);//清空接收数据
}


// 自动模式控制
// PC6  风扇
// PC4  LD1 制冷器
// PC13 LD3 加热器
void auto_control(void)
{
  static uint8_t f = 0;//除湿操作默认0为开

  if(mode==0)
  {
    // 先进行除湿
    if (H > Hum_level)
    {
      HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6 | GPIO_PIN_4 | GPIO_PIN_13, 1);//全都打开
      strcpy(fan, "fan : ON ");
      strcpy(condenser, "condenser : ON ");
      strcpy(heater, "heater : ON ");

      f = 0;//除湿操作0为开
    }
    else
    {
      //如果湿度低于或等于 Hum_level,并且之前除湿设备是开启的(f == 0),则关闭除湿设备
      //并设置 f = 1,表示除湿设备已关闭。

      if (f == 0)
      {
        f = 1;
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6 | GPIO_PIN_4 | GPIO_PIN_13, 0);//全都关闭
        strcpy(fan, "fan : OFF");
        strcpy(condenser, "condenser : OFF");
        strcpy(heater, "heater : OFF");
      }

      // 然后控温
      if (T > TempHigh)
      {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6 | GPIO_PIN_4, 1);//开启风扇和LD1制冷器
        strcpy(fan, "fan : ON ");
        strcpy(condenser, "condenser : ON ");
      }
      else if (T < TempHigh - 2)
      {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6 | GPIO_PIN_4, 0);//关闭风扇和LD1制冷器
        strcpy(fan, "fan : OFF");
        strcpy(condenser, "condenser : OFF");
      }

      if (T < TempLow)
      {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 1);//开启LD3加热器
        strcpy(heater, "heater : ON ");
      }
      else if (T > TempLow + 2)
      {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 0);//关闭LD3加热器
        strcpy(heater, "heater : OFF");
      }
    }
  }
}

/* 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 */

三.项目技术要领

一、硬件驱动与外设控制

  1. 多外设初始化与配置

    • 使用 STM32CubeMX 生成基础外设初始化代码(如MX_GPIO_InitMX_I2C1_Init等),涉及 GPIO、I2C(温湿度传感器 SHT20)、USART(串口通信)、ADC(电池电压采集)、SPI(LCD 屏幕 ILI9341)、TIM(定时器中断)等。
    • 关键点:通过HAL_PWREx_EnableVddA()HAL_PWREx_EnableVddIO2()启用模拟电源和 IO 电源,确保 ADC 等模拟外设正常工作。
  2. 温湿度传感器(SHT20)驱动

    • 通过 I2C 接口读取数据,调用BSP_SHT20_GetData()函数获取温度(T)和湿度(H),体现 I2C 通信协议的应用。
  3. LCD 屏幕(ILI9341)驱动

    • 使用 SPI 接口驱动屏幕,通过Gui_DrawFont_GBK16函数实现中文字符显示,涉及字库调用和坐标定位,需注意显存操作和刷屏效率。
  4. ADC 采集与电压计算

    • 校准 ADC(HAL_ADCEx_Calibration_Start)后,通过HAL_ADC_GetValue获取原始值,经公式Vbat=(val*4*3.3)/4095转换为实际电压(考虑分压电路),体现模拟信号数字化处理流程。

二、软件架构与功能模块

  1. 系统初始化与自检

    • main函数中完成外设初始化后,通过Vbat>0判断电源状态,若异常则显示 “Sorry Failed”,实现基本的开机自检逻辑。
  2. 定时任务与中断机制

    • 定时器中断
      • htim17每 2 秒触发一次,通过printf上报设备状态(ID、模式、温湿度、阈值等),体现定时数据上报功能。
      • htim6用于按键消抖,避免机械按键抖动导致的误触发,提升交互可靠性。
    • 串口中断
      • 启用不定长接收中断(HAL_UART_Receive_IT)和空闲中断(UART_FLAG_IDLE),通过recv_flag标志位触发数据处理,实现串口命令解析(如远程控制阈值、设备开关)。
  3. 模式切换与控制逻辑

    • 手动模式(mode=1
      • 通过按键(PA12、PC9、PC8、PC5)切换阈值调整状态(flag),直接修改TempHighTempLowHum_level,并通过 LCD 高亮显示当前调整项(蓝色背景)。
    • 自动模式(mode=0
      • 基于温湿度阈值自动控制设备:
        • 湿度高于阈值时,同时开启风扇、制冷器、加热器(除湿)。
        • 湿度正常后,根据温度自动启停制冷器或加热器,体现 PID-like 的滞后控制逻辑(如T > TempHigh时启动制冷,T < TempHigh-2时关闭,避免频繁启停)。
  4. 串口通信协议

    • 采用文本协议,命令格式如"123 Hum_level=30.5"(设备 ID + 参数名 + 值),通过strstrsscanf解析指令,实现远程参数配置和设备控制(如fan_onheater_off)。
    • 安全机制:通过设备 ID(id=123)过滤非法指令,未匹配 ID 时返回 “未识别到该设备”。

三、代码实现细节

  1. printf重定向

    • 通过重写fputc函数,将printf输出重定向到 USART1,利用串口发送数据,方便调试和状态上报。
  2. 字符串操作与缓冲区管理

    • 使用recv_buf[1024]缓冲区存储串口接收数据,通过memset清空缓冲区,避免数据残留。
    • strncmpstrstrsscanf等函数的组合使用,实现灵活的文本解析逻辑。
  3. 状态显示与用户交互

    • LCD 界面布局清晰,分区域显示温湿度、阈值、设备状态、模式等信息,通过颜色区分当前调整项(蓝色背景),提升人机交互体验。
    • 按键消抖采用定时器中断处理,确保按键操作的稳定性。
  4. 静态变量与标志位

    • auto_control函数中使用静态变量f记录除湿设备状态,避免重复开关,优化设备寿命和能耗。
    • flagmode标志位采用全局变量,实现多模块状态同步。

四、技术挑战与优化点

  1. 实时性与任务调度

    • 主循环中顺序执行温湿度采集、ADC 转换、串口处理、LCD 显示等任务,未使用 RTOS,需注意各任务执行时间避免超时(如HAL_ADC_PollForConversion的超时参数设为 100ms)。
  2. 抗干扰与稳定性

    • ADC 校准、按键消抖、串口指令过滤(ID 验证)等机制提升系统抗干扰能力,但需注意多中断优先级配置(如 USART 与 TIM 中断优先级),避免中断嵌套导致的异常。
  3. 代码可维护性

    • 函数模块化(如lcd_displayauto_control)提升可读性,但全局变量较多,可考虑封装为结构体或使用静态局部变量限制作用域。
  4. 扩展能力

    • 现有代码支持通过串口远程控制和阈值配置,可进一步扩展 OTA 升级、无线通信(如 WiFi/BLE)、更多传感器接入等功能。

四.项目实现原理

一、硬件实现原理

1. 传感器数据采集
  • 温湿度传感器(SHT20)
    • 工作原理:基于电容式湿度传感和带隙温度传感技术,通过 I2C 接口输出数字信号。
    • 通信协议:STM32 通过 I2C 总线发送命令(如触发测量),并读取 16 位温湿度数据,经校准后转换为实际值(如温度单位为℃,湿度单位为 % RH)。
  • 电池电压检测(ADC)
    • 电路设计:采用分压电路(如 4:1)将电池电压降至 ADC 可测量范围(0-3.3V)。
    • 转换公式Vbat = (ADC值 × 4 × 3.3V) / 4095,其中 4095 为 12 位 ADC 的满量程值。
2. 执行设备控制
  • GPIO 输出:通过 PC4(制冷器)、PC6(风扇)、PC13(加热器)引脚输出高低电平,控制外部继电器或 MOSFET 开关,实现设备启停。
  • 安全机制:所有设备默认关闭,仅在满足条件时激活,避免误操作。
3. 人机交互接口
  • LCD 显示(ILI9341)
    • 通信方式:SPI 总线传输数据,支持 16 位色彩(65K 色)。
    • 显示原理:通过写入显存地址和像素数据,控制屏幕各点颜色,实现文本和图形显示。
  • 按键输入:PA12(模式切换)、PC8/PC9/PC5(阈值调整)通过外部中断触发,配合定时器消抖确保可靠性。

二、软件架构原理

1. 初始化与自检流程

2. 多任务调度机制
  • 主循环(轮询)

主循环(轮询)

while(1) {
  温湿度采集;
  ADC转换;
  串口命令处理;
  自动控制逻辑;
  LCD刷新;
}
  • 中断驱动
    • 定时器中断:htim17(定时上报)和 htim6(按键消抖)通过中断服务函数(ISR)异步执行。
    • 串口中断:空闲中断(UART_FLAG_IDLE)检测一帧数据结束,触发命令解析。
3. 状态机控制模型
  • 工作模式

  • 自动控制算法

    plaintext

    if (湿度 > 阈值) {
      启动除湿(风扇+制冷器+加热器);
    } else {
      if (温度 > 上限) 启动制冷;
      else if (温度 < 上限-2) 关闭制冷;
      
      if (温度 < 下限) 启动加热;
      else if (温度 > 下限+2) 关闭加热;
    }
    
     
    • 滞后设计:通过TempHigh-2TempLow+2实现回差控制,避免设备频繁启停。

三、通信与数据处理原理

1. 串口协议解析
  • 命令格式设备ID 参数名=值(如123 Hum_level=50.0)。
  • 解析流程

    c

    if (接收到数据 && ID匹配) {
      if (strstr(数据, "Hum_level="))
        sscanf(数据, "123 Hum_level=%f", &Hum_level);
      else if (strstr(数据, "fan_on"))
        开启风扇;
      // ...其他命令解析
    }
    
  • 安全性:通过 ID 验证过滤非法指令,防止未授权操作。
2. 数据存储与同步
  • 全局变量TempHighTempLowHum_level等参数存储在 RAM 中,掉电丢失。
  • 显示同步:LCD 显示函数(lcd_display)在每次循环中更新界面,确保数据实时可见。

四、核心算法与控制策略

1. 除湿与温控逻辑
  • 优先级策略:湿度控制优先于温度控制,即湿度超标时优先除湿,湿度正常后再调节温度。
  • 节能设计:通过静态变量f记录除湿状态,避免重复启停设备。
2. 按键消抖机制
  • 硬件防抖:按键并联电容,减少机械抖动。
  • 软件防抖

    c

    检测到按键按下 --> 启动定时器 --> 定时器溢出后再次检测电平 --> 确认按键状态
    
     
    • 有效避免因抖动导致的误触发。

五、系统优化与扩展方向

1. 实时性优化
  • 当前问题:主循环顺序执行任务,若某任务耗时过长(如 LCD 刷新),可能影响其他功能响应。
  • 解决方案:引入 RTOS(如 FreeRTOS),将任务划分为独立线程(如传感器采集线程、控制线程、显示线程),提高并发处理能力。
2. 数据持久化
  • 现状:参数存储在 RAM 中,断电丢失。
  • 改进:添加 EEPROM 或 Flash 存储模块,在参数修改时保存到非易失性存储器,开机自动加载。
3. 通信扩展
  • 现状:仅支持串口通信,距离受限。
  • 升级:集成 WiFi/BLE 模块,实现远程 APP 控制;或添加 MQTT 协议,接入物联网平台。

五.项目实现展示(见哔哩哔哩的视频录制)

STM32智能除湿器(初级版本)_哔哩哔哩_bilibili

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值