STM32 环境监测与控制系统的设计与实现

发布于:2025-06-25 ⋅ 阅读:(21) ⋅ 点赞:(0)

        一个基于 STM32 微控制器的环境监测与控制系统的设计与实现。该系统能够实时采集温湿度数据,通过 OLED 屏幕显示环境参数,并提供用户交互界面进行阈值设置。系统还具备 PWM 电机控制、状态指示和异常报警功能,适合应用于智能家居、温室大棚等环境监测场景。

代码下载地址:编译前需要将main.c中中文转位gb2312编码存储,否则会报编码错误https://wwp.lanzoul.com/iGKFS2zhoxif

project1.zip - 蓝奏云

系统功能概述

该环境监测与控制系统主要实现以下功能:

  • 温湿度数据采集与显示(使用 DHT11 传感器)
  • 128x64 OLED 屏幕信息显示
  • 阈值设置与 Flash 存储功能
  • 四档 PWM 电机速度控制
  • 状态指示(LED 灯光)与异常报警(蜂鸣器)
  • 火焰检测功能
  • 按键短按与长按处理

系统工作流程如下:STM32 微控制器通过 DHT11 传感器采集温湿度数据,与预设阈值比较后控制相应的执行器(如风扇、加湿器等),并通过 OLED 屏幕显示实时数据和系统状态。用户可以通过按键设置温度和湿度阈值,系统将阈值保存在 Flash 中以便掉电保存。

硬件设计与选型

核心控制器

系统采用 STM32F103 系列微控制器作为核心,该系列芯片具有以下特点:

  • 32 位 Cortex-M3 内核,最高 72MHz 工作频率
  • 丰富的外设资源:ADC、I2C、TIM、GPIO 等
  • 内置 Flash 和 SRAM,满足程序存储和数据处理需求
  • 低功耗设计,适合电池供电或节能场景
传感器与执行器
  • DHT11 温湿度传感器:低成本、单总线接口,可同时测量温度和湿度
  • OLED 显示屏:128x64 分辨率,I2C 接口,显示效果清晰
  • 按键模块:4 个独立按键,用于人机交互
  • LED 指示灯:4 个 LED,用于状态指示
  • 蜂鸣器:用于异常情况报警
  • 电机控制:通过 PWM 信号控制电机速度
  • 火焰传感器:用于检测环境中的火焰信号
硬件接口设计

系统硬件接口设计如下:

  • DHT11 传感器:连接到 GPIOA_PIN_1(单总线接口)
  • OLED 显示屏:通过 I2C1 接口连接(SCL: PB6, SDA: PB7)
  • 按键输入:K1-K4 连接到 GPIOB_PIN_0-3(上拉输入)
  • LED 输出:LED1-LED4 连接到 GPIOA_PIN_4-7 和 GPIOB_PIN_0-1
  • 蜂鸣器:连接到 GPIOA_PIN_3
  • 电机控制:通过 TIM3 的 PWM 输出(CH2-CH4)控制
  • 火焰传感器:连接到 GPIOA_PIN_6(ADC 输入)

软件架构分析

系统软件采用模块化设计,主要包括以下模块:

  1. 硬件驱动模块:包含 GPIO、I2C、TIM、ADC 等底层驱动
  2. 传感器模块:DHT11 温湿度传感器驱动
  3. 显示模块:OLED 显示屏驱动与界面显示
  4. 按键模块:按键检测与长按处理
  5. 存储模块:Flash 数据存储与读取
  6. 控制模块:PWM 电机控制、LED 与蜂鸣器控制
  7. 主控制模块:系统初始化与主循环控制
/* 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"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "OLED.h"
#include "dht11.h"
#include "config.h"
#include "stm32f1xx_hal_flash.h"
#include "flash.h"

/* 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 ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;

I2C_HandleTypeDef hi2c1;

TIM_HandleTypeDef htim3;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
static void MX_TIM3_Init(void);
static void MX_ADC1_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
DHT11_TypeDef myDHT11;
bool dht11_flag = false;
bool key_flag = false;
bool auto_Flag = false; // 自动模式
bool set_ui = false;	//设置界面
bool cursor = false;  //光标位置标志位(false为第一行,true为第二行)

// 存储需要保存到Flash的数据
uint8_t TT=30;//温度阈值(单位:℃)
uint8_t HT=70;//湿度阈值(单位:%RH)

uint16_t motorIndex = 0;                    // motorSpeedIndex
uint16_t motorSpeed[] = {0, 200, 400, 999}; // Speed




//显示DHT11数据并更新OLED屏幕 128*64
void dht11_show(void)
{
  OLED_Clear();//清屏OLED
  char str[16];
  if(set_ui == false){
    OLED_ShowString(1, 1, "工位: 11", OLED_8X16);

    if (DHT11_ReadData(&myDHT11) == 0)
    {
      //温度处理 + LED1控制
      if(myDHT11.Temperature>TT){
        HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);//LED1
        motorIndex=3;
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET);
        OLED_ShowString(112, 16, "H", OLED_8X16);//温度过高显示"H"
      }else{
        HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);//LED1
        motorIndex=0;
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);
		OLED_ShowString(112, 16, "L", OLED_8X16);//温度正常显示"L"
      }
      sprintf(str, "温度:%d.0℃", myDHT11.Temperature);
      OLED_ShowString(1, 16, str, OLED_8X16);
      //湿度处理
      if(myDHT11.Humidity>HT){
        HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);//LED2
        OLED_ShowString(112, 32, "H", OLED_8X16);//湿度过高显示"H"
      }else{
        HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
		OLED_ShowString(112, 32, "L", OLED_8X16);//湿度正常显示"L"
      }
      sprintf(str, "湿度:%dRH%%", myDHT11.Humidity);
      OLED_ShowString(1, 32, str, OLED_8X16);
    }
    //状态显示
    
    
    //电机档位
    //sprintf(str, "D:%d", motorIndex);
    //OLED_ShowString(100, 16, str, OLED_8X16);
    //TIM3->CCR2=motorSpeed[motorIndex];
    
    //光照强度检测
		//	HAL_ADC_Start(&hadc1);
		//	HAL_ADC_PollForConversion(&hadc1, 50);
		//	uint16_t ADC_Value=0;
		//	ADC_Value = HAL_ADC_GetValue(&hadc1);   //读取AD值
		//	sprintf(str, "ADC%d", ADC_Value);
		//    OLED_ShowString(1, 48, str, OLED_8X16);
    
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6)==GPIO_PIN_RESET){
      OLED_ShowString(1, 48, "火焰:有", OLED_8X16);
      //蜂鸣器翻转
	  HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_3);
      //HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET);
      //LED3
      //HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET);
      HAL_GPIO_TogglePin(LED3_GPIO_Port,LED3_Pin);
    }else{
      OLED_ShowString(1, 48, "火焰:无", OLED_8X16);
      HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET);
      //蜂鸣器关
      HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET);
    }
    
    //自动模式处理
    
  }else{
    // 设置界面显示
    OLED_ShowString(1, 1, "参数设置", OLED_8X16);
    sprintf(str, "温度阈值:%d℃", TT);
    OLED_ShowString(1, 16, str, OLED_8X16);
    sprintf(str, "湿度阈值:%dRH%s", HT, "%");
    OLED_ShowString(1, 32, str, OLED_8X16);
    //光标显示
    if(cursor==false){
      OLED_Invert(72,16,32,16);//第一行高亮
    }else{
      OLED_Invert(72,32,48,16);//第二行高亮
    }
    

  }
  
  
	
	
	
  OLED_Update();
}

// 按键
bool KeyCheck(GPIO_TypeDef *gpio_port, uint16_t gpio_pin)
{
  if (HAL_GPIO_ReadPin(gpio_port, gpio_pin) == GPIO_PIN_RESET)
  {
    HAL_Delay(20);
    if (HAL_GPIO_ReadPin(gpio_port, gpio_pin) == GPIO_PIN_RESET)
    {
      while (HAL_GPIO_ReadPin(gpio_port, gpio_pin) == GPIO_PIN_RESET);
      
      return true;
    }
  }
  return false;
}
// 检查按键是否被长按
bool KeyCheck_LongPress(GPIO_TypeDef *gpio_port, uint16_t gpio_pin, uint32_t *press_time)
{
    if (HAL_GPIO_ReadPin(gpio_port, gpio_pin) == GPIO_PIN_RESET)
    {
        HAL_Delay(20); //消抖延时
        if (HAL_GPIO_ReadPin(gpio_port, gpio_pin) == GPIO_PIN_RESET)
        {
            if(*press_time == 0) {
                *press_time = HAL_GetTick(); // 记录按下开始时间
            }
            // 检查是否达到长按时间,这里设定为500ms
            else if(HAL_GetTick() - *press_time > 500) {
                return true; // 长按确认
            }
            return false; // 短按状态
        }
    }
    *press_time = 0; // 重置按下时间
    return false;
}
/* 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();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_I2C1_Init();
  MX_TIM3_Init();
  MX_ADC1_Init();
  /* USER CODE BEGIN 2 */
	//LED
	//HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
	//HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);
	//HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET);
	//HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_SET);
	
  // 加载Flash数据
  LoadThresholdsFromFlash();
	TT= thresholdData.TemperatureThreshold;
	HT= thresholdData.HumidityThreshold;
	if(TT==0 || HT==0){
    TT=30;
    HT=70;
  }
	SaveThresholdsToFlash();
	//ADC
	
	
  // DHT11
  DHT11_Init(&myDHT11, GPIOA, GPIO_PIN_1);
  // OLED
  OLED_Init();
	
  // PWM	CH2 CH3 CH4
  HAL_TIM_PWM_Init(&htim3);
  HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
	HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3);
	HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_4);
  TIM3->CCR2 = 500;
	TIM3->CCR3 = 600;
	TIM3->CCR4 = 999;
	//HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3);
  //TIM3->CCR3 = 0;
	//HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
	//蜂鸣器
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET);
	
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	static uint32_t k3_press_time = 0, k4_press_time = 0; // 记录按键按下时间
	
    if (dht11_flag == true)
    {
		dht11_flag = false;
		//dht11_show();
    }
	
    if (key_flag == true)
    {
      key_flag = false;
      if (KeyCheck(K1_GPIO_Port, K1_Pin) == 1)
      {
        // 电机档位+开机
        //if(++motorIndex==(sizeof(motorSpeed)/sizeof(motorSpeed[0]))) motorIndex=0;
				
        // 电机档位+UI
        set_ui = !set_ui;
        thresholdData.TemperatureThreshold=TT;
        thresholdData.HumidityThreshold=HT;
        SaveThresholdsToFlash();
					
      }
      if (KeyCheck(K2_GPIO_Port, K2_Pin) == 1)
      {
        if(set_ui==true){
          cursor=!cursor;
        }
      }
      if (KeyCheck(K3_GPIO_Port, K3_Pin) == 1)
      {
        // 增减
        if(cursor==false){
          if(TT<99 && TT>0){
            TT++;
          }
        }else{
          if(HT<99 && HT>0){
            HT++;
          }
        }
        
      }
		 if (HAL_GPIO_ReadPin(K3_GPIO_Port, K3_Pin) == GPIO_PIN_RESET) {
            bool is_long = KeyCheck_LongPress(K3_GPIO_Port, K3_Pin, &k3_press_time);
            int step = is_long ? 1 : 1; // 长按+5,短按+1
            
            if(set_ui == true) {
                if(cursor == false && TT < 99) TT += step;
                else if(HT < 99) HT += step;
            }
            HAL_Delay(100); // 防止重复触发
        }
	
      if (KeyCheck(K4_GPIO_Port, K4_Pin) == 1)
      {
        // 减少
        if(cursor==false){
          if(TT<99 && TT>0){
            TT--;
          }
        }else{
          if(HT<99 && HT>0){
            HT--;
          }
        }
      }
			// K4长按
       if (HAL_GPIO_ReadPin(K4_GPIO_Port, K4_Pin) == GPIO_PIN_RESET) {
            bool is_long = KeyCheck_LongPress(K4_GPIO_Port, K4_Pin, &k4_press_time);
            int step = is_long ? 1 : 1; // 长按-5,短按-1
            
            if(set_ui == true) {
                if(cursor == false && TT > 0) TT -= step;
                else if(HT > 0) HT -= step;
            }
            HAL_Delay(100); // 防止重复触发
        }
    }
	  
    
		
		
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  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_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
  PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief ADC1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_ADC1_Init(void)
{

  /* USER CODE BEGIN ADC1_Init 0 */

  /* USER CODE END ADC1_Init 0 */

  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC1_Init 1 */

  /* USER CODE END ADC1_Init 1 */

  /** Common config
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 1;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_2;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */

  /* USER CODE END ADC1_Init 2 */

}

/**
  * @brief I2C1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_I2C1_Init(void)
{

  /* USER CODE BEGIN I2C1_Init 0 */

  /* USER CODE END I2C1_Init 0 */

  /* USER CODE BEGIN I2C1_Init 1 */

  /* USER CODE END I2C1_Init 1 */
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 400000;
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c1.Init.OwnAddress1 = 0;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN I2C1_Init 2 */

  /* USER CODE END I2C1_Init 2 */

}

/**
  * @brief TIM3 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM3_Init(void)
{

  /* USER CODE BEGIN TIM3_Init 0 */

  /* USER CODE END TIM3_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  /* USER CODE BEGIN TIM3_Init 1 */

  /* USER CODE END TIM3_Init 1 */
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 72-1;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 1000-1;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM3_Init 2 */

  /* USER CODE END TIM3_Init 2 */
  HAL_TIM_MspPostInit(&htim3);

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  /* USER CODE BEGIN MX_GPIO_Init_1 */
  /* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(dht11_GPIO_Port, dht11_Pin, GPIO_PIN_SET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, BUZZ_Pin|LED1_Pin|LED2_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, LED3_Pin|LED4_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : dht11_Pin */
  GPIO_InitStruct.Pin = dht11_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(dht11_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pins : BUZZ_Pin LED1_Pin LED2_Pin */
  GPIO_InitStruct.Pin = BUZZ_Pin|LED1_Pin|LED2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pin : PA6 */
  GPIO_InitStruct.Pin = GPIO_PIN_6;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : LED3_Pin LED4_Pin */
  GPIO_InitStruct.Pin = LED3_Pin|LED4_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pins : K1_Pin K2_Pin K3_Pin K4_Pin */
  GPIO_InitStruct.Pin = K1_Pin|K2_Pin|K3_Pin|K4_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* USER CODE BEGIN MX_GPIO_Init_2 */
  /* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

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

Flash 存储操作需要注意以下几点:

  1. Flash 写入前需要先擦除
  2. 避免频繁擦写,延长 Flash 寿命
  3. 数据存储格式要合理,确保读写一致
  4. 增加数据有效性检查,防止无效数据

技术难点与解决方案

DHT11 时序控制

DHT11 采用严格的单总线时序,对时序要求非常高。在 STM32 上实现时,由于 Cortex-M3 内核的中断响应和任务调度可能影响时序精度,导致数据读取失败。

解决方案

  1. 优化 DHT11 驱动代码,减少非必要操作
  2. 在数据读取期间禁用中断,确保时序准确
  3. 增加重试机制,当读取失败时重新尝试
  4. 精确控制延时,使用 CPU 周期计数代替 HAL_Delay
OLED 显示缓冲区管理

128x64 的 OLED 显示屏需要较大的显示缓冲区,而 STM32F103 的 SRAM 资源有限,直接分配全屏缓冲区可能导致内存不足。

解决方案

  1. 采用分页显示方式,每次只更新需要变化的部分
  2. 动态分配显示缓冲区,只在需要时申请内存
  3. 优化显示数据结构,减少内存占用
  4. 实现高效的显示更新算法,减少重绘区域
按键消抖与长按检测

机械按键存在抖动问题,长按检测需要准确的时间测量,处理不当会导致按键响应不灵敏或误触发。

解决方案

  1. 采用软件消抖,通过延时和多次采样确认按键状态
  2. 使用 HAL_GetTick () 获取精确的时间戳,实现准确的长按检测
  3. 设计合理的按键状态机,处理按下、抖动、长按和释放等状态
  4. 增加防重复触发机制,避免短时间内多次响应

总结与改进方向

系统特点
  1. 功能完整:实现了环境监测、数据显示、用户交互和设备控制等功能
  2. 人机交互友好:支持按键短按和长按操作,OLED 显示清晰直观
  3. 数据持久化:使用 Flash 存储阈值数据,实现掉电保存
  4. 可扩展性强:模块化设计便于功能扩展和维护
改进方向
  1. 增加通信接口:添加 WiFi 或蓝牙模块,实现远程监控和控制
  2. 优化低功耗:增加睡眠模式,降低系统功耗,适合电池供电场景
  3. 增强数据处理:添加数据滤波和趋势分析功能,提高数据准确性
  4. 扩展传感器:增加光照、气压等传感器,实现更全面的环境监测
  5. 完善报警功能:添加多种报警方式,如短信报警、APP 推送等
应用场景

该系统可广泛应用于以下场景:

  • 智能家居环境监测与控制
  • 温室大棚温湿度监控
  • 仓储物流环境监测
  • 实验室环境监控
  • 小型气象站

网站公告

今日签到

点亮在社区的每一天
去签到