当前位置: 首页 > news >正文

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

        一个基于 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();//清屏OLEDchar 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);//LED1motorIndex=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);//LED1motorIndex=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);//LED2OLED_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(); // 记录按下开始时间}// 检查是否达到长按时间,这里设定为500mselse 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// DHT11DHT11_Init(&myDHT11, GPIOA, GPIO_PIN_1);// OLEDOLED_Init();// PWM	CH2 CH3 CH4HAL_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;// 电机档位+UIset_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,短按+1if(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,短按-1if(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 推送等
应用场景

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

  • 智能家居环境监测与控制
  • 温室大棚温湿度监控
  • 仓储物流环境监测
  • 实验室环境监控
  • 小型气象站
http://www.lryc.cn/news/574650.html

相关文章:

  • Vue3+el-table-v2虚拟表格大数据量多选功能详细教程
  • STM32[笔记]--4.嵌入式硬件基础
  • 攻防世界-MISC-MeowMeowMeow
  • Unity小工具:资源引用的检索和替换
  • 深入研究:小红书笔记详情API接口详解
  • Linux环境下MariaDB如何实现负载均衡
  • 一文了解AI Agent的幕后基础设施
  • 记一次 Kafka 磁盘被写满的排查经历
  • 采用ArcGIS10.8.2 进行插值图绘制
  • macOS - 快速上手使用 YOLO
  • MySQL之SQL性能优化策略
  • 信创建设,如何统一管理异构服务器的认证、密码、权限管理等?
  • React性能优化精髓之一:频繁setState导致滚动卡顿的解决方案
  • 新增MCP接入和AutoAgent,汉得灵猿AI中台1.6版正式发布!
  • 【软考高级系统架构论文】论单元测试方法及应用
  • Linux离线安装mysql
  • 探秘深蓝 “引擎”:解码水下推进器的科技与应用
  • Flask(四) 模板渲染render_template
  • Dify×奇墨科技:开源+本土化,破解企业AI落地难题
  • Chrome MCP Server:AI驱动浏览器自动化测试实战「喂饭教程」
  • iframe窗体默认白色背景去除
  • 重点解析(软件工程)
  • 云电脑,“死”于AI时代前夕 | 数智化观察
  • 基于DE1-SoC的My_First_oneAPI(二)
  • 黑马Day01-03集开始
  • 第24篇:Linux内核深度解析与OpenEuler 24.03实践指南
  • TCP/UDP协议深度解析(一):UDP特性与TCP确认应答以及重传机制
  • 交易期权先从买方开始
  • C8BJWD8BJV美光固态闪存HSA22HSA29
  • android脱糖