STM32温室控制系统:传感器闭环、执行器时序与资源约束实战
2026/9/16 16:55:50 网站建设 项目流程

简介:本资源是一套完整落地的嵌入式实践项目方案,面向电子信息、自动化、物联网等专业的本科生,适用于毕业设计、课程设计、学科竞赛及综合实训场景,聚焦温室环境智能监控这一典型嵌入式应用。项目基于STM32F103系列主控开发,实现温湿度、光照、土壤湿度等多参数采集,支持LCD本地显示、按键交互与继电器自动调控,具备完整的软硬件协同逻辑与工程可部署性。压缩包共含若干核心文件(具体数量未提供),主体为Keil MDK工程源码、模块化C语言驱动代码、详细说明文档及结构清晰的PDF结题报告,涵盖需求分析、硬件选型、电路设计、软件流程与测试结果,总大小33.96MB。已有198人学习下载,所有代码经导师指导并以97分高分通过验收,开箱即用,无需修改即可编译运行,配套文档对关键中断配置、传感器校准、PID温控逻辑等难点均有明确注释与实现说明,显著降低初学者工程复现门槛。

1. 这不是“抄个代码交差”的毕设——STM32温室控制系统真正要打通的是传感器闭环、执行器时序与嵌入式资源边界的三重约束

很多同学拿到“基于STM32的温室控制系统”这个题目,第一反应是找一份带源码+PDF报告的压缩包解压就完事。但实际调试时会发现:DHT22读数跳变、继电器频繁吸合烧触点、OLED屏闪屏卡死、串口发出去的数据上位机收不到……根本原因不在代码有没有main.c,而在于没理清STM32作为资源受限嵌入式平台,在温湿度采集、光照调控、通风执行、人机交互这四个并行任务中如何分配CPU时间、中断优先级、ADC采样窗口和GPIO驱动能力。本项目本质是典型的状态机+多任务调度实践:用HAL库构建可靠外设驱动层,用环形缓冲区解耦传感器采集与控制决策,用状态标志位替代阻塞延时实现非抢占式任务协同。适合电子/自动化/农业工程专业学生,要求已掌握Keil5开发环境搭建、STM32CubeMX基础配置、GPIO/ADC/UART外设初始化流程,不依赖RTOS但需理解中断服务函数与主循环的协作逻辑。

2. 从CubeMX生成工程到跑通最小闭环:用HAL库初始化温湿度、光照、继电器三类关键外设

2.1 使用STM32CubeMX配置核心外设参数(以STM32F103C8T6为例)

选择芯片后,首先启用RCC时钟源为HSE(外部晶振),将系统时钟配置为72MHz;在Pinout视图中,将PA0配置为ADC1_IN0(接光敏电阻分压信号),PB1配置为ADC1_IN1(接土壤湿度传感器模拟输出),PC13配置为GPIO_Output(控制加热片继电器),PC14配置为GPIO_Output(控制排风扇继电器),PA9/PA10配置为USART1(连接CH340模块用于串口调试)。关键注意点:ADC采样周期必须设为固定值(如144个ADC周期),避免因温度变化导致采样时间漂移;GPIO输出类型选Push-Pull而非Open-Drain,确保继电器线圈能获得足够驱动电流;USART1波特率设为115200,数据位8,停止位1,无校验——这是与上位机通信的黄金参数组合。

提示:若出现error: no stm32 target found!,检查ST-Link驱动是否安装(使用ST-Link Utility v4.6+)、目标板供电是否正常(3.3V实测电压≥3.2V)、SWDIO/SWCLK引脚是否被其他外设复用(如未禁用JTAG调试接口)。

2.2 手动编写ADC多通道连续采样与DHT22单总线协议解析

HAL库默认ADC单次转换无法满足温湿度实时性要求,需改用DMA+连续转换模式。在main.c中添加以下初始化代码:

// 启用ADC1双通道DMA传输(PA0+PB1) hadc1.Init.ContinuousConvMode = ENABLE; hadc1.Init.NbrOfConversion = 2; hadc1.Init.DMAContinuousRequests = ENABLE; HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buf, 2, DMA_PINC_ENABLE);

DHT22通信对时序极其敏感,不能依赖HAL_Delay()。采用SysTick定时器微秒级延时方案:

void DHT22_Delay_us(uint16_t us) { uint32_t start = SysTick->VAL; uint32_t reload = SysTick->LOAD; uint32_t delay = (reload * us) / 1000000U; // 假设SysTick频率为1MHz while ((start - SysTick->VAL) < delay) { if (SysTick->VAL > start) start += reload + 1; } }

参数说明adc_buf为全局uint16_t数组,存储PA0(光照)、PB1(土壤湿度)原始ADC值;DHT22_Delay_us()reload值需根据实际SysTick配置计算(如72MHz系统时钟下,SysTick->LOAD=71999对应1ms中断);DHT22响应脉冲宽度必须严格控制在80μs低电平+80μs高电平,否则传感器拒绝应答。

2.3 继电器驱动电路与GPIO安全控制策略

PC13/PC14直接驱动继电器存在风险:STM32 GPIO最大灌电流仅25mA,而常见5V继电器线圈工作电流达40–70mA。必须加入驱动三极管(如S8050)和续流二极管(1N4007)。在软件层面实施双重保护:

#define RELAY_HEAT_GPIO_Port GPIOC #define RELAY_HEAT_Pin GPIO_PIN_13 #define RELAY_FAN_GPIO_Port GPIOC #define RELAY_FAN_Pin GPIO_PIN_14 void Relay_Control(uint8_t heat_on, uint8_t fan_on) { static uint8_t last_heat = 0, last_fan = 0; if (heat_on != last_heat) { HAL_GPIO_WritePin(RELAY_HEAT_GPIO_Port, RELAY_HEAT_Pin, heat_on ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_Delay(100); // 继电器机械动作延迟 last_heat = heat_on; } if (fan_on != last_fan) { HAL_GPIO_WritePin(RELAY_FAN_GPIO_Port, RELAY_FAN_Pin, fan_on ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_Delay(100); last_fan = fan_on; } }

关键参数表:继电器切换最小间隔必须≥100ms,防止触点电弧烧蚀;GPIO输出电平采用GPIO_PIN_SET(高电平导通)而非GPIO_PIN_RESET,符合NPN三极管驱动逻辑;所有继电器控制必须封装为函数调用,禁止在中断服务程序中直接操作GPIO。

3. 构建非阻塞状态机:用有限状态机实现温湿度阈值判断与执行器联动逻辑

3.1 定义温室控制核心状态与转移条件

传统if-else判断易导致逻辑耦合,采用状态机解耦采集、判断、执行三个阶段。定义如下5个核心状态:

状态编号状态名称触发条件执行动作
S0IDLE系统上电初始状态初始化ADC/DHT22/USART,清空所有标志位
S1READ_SENSORS每2秒定时器中断触发启动ADC DMA采集,触发DHT22读取,更新全局变量
S2JUDGE_ENVS1完成后立即进入根据预设阈值(如T>30℃且H<40%)设置执行标志
S3ACTUATE_RELAYSS2中任一执行标志置位调用Relay_Control(),记录动作时间戳
S4SEND_DATA每5秒定时器中断触发(独立于S1)将当前温湿度/光照/执行状态打包为JSON字符串发送

注意:所有状态转移必须通过switch-case实现,禁止使用goto;每个状态执行时间必须≤5ms(保证主循环100Hz刷新率)。

3.2 实现带防抖的阈值判断算法(解决传感器噪声导致误动作)

DHT22湿度读数常有±3%波动,直接比较会导致继电器频繁启停。采用滑动窗口中值滤波+迟滞比较:

#define FILTER_WINDOW_SIZE 5 uint16_t temp_filter[FILTER_WINDOW_SIZE] = {0}; uint16_t humi_filter[FILTER_WINDOW_SIZE] = {0}; void Update_Filter_Buffer(uint16_t new_temp, uint16_t new_humi) { static uint8_t idx = 0; temp_filter[idx] = new_temp; humi_filter[idx] = new_humi; idx = (idx + 1) % FILTER_WINDOW_SIZE; } uint16_t Get_Median(uint16_t arr[]) { uint16_t sorted[FILTER_WINDOW_SIZE]; memcpy(sorted, arr, sizeof(sorted)); // 简单冒泡排序(嵌入式场景可接受) for (int i = 0; i < FILTER_WINDOW_SIZE-1; i++) { for (int j = 0; j < FILTER_WINDOW_SIZE-1-i; j++) { if (sorted[j] > sorted[j+1]) { uint16_t tmp = sorted[j]; sorted[j] = sorted[j+1]; sorted[j+1] = tmp; } } } return sorted[FILTER_WINDOW_SIZE/2]; } // 迟滞比较:加热启动阈值25℃,停止阈值23℃ void Judge_Temperature(uint16_t current_temp) { uint16_t median_temp = Get_Median(temp_filter); if (median_temp > 2500) { // 25.00℃(放大100倍存储) heat_flag = 1; } else if (median_temp < 2300) { // 23.00℃ heat_flag = 0; } }

参数说明FILTER_WINDOW_SIZE=5平衡实时性与滤波效果;温度值放大100倍存储避免浮点运算;迟滞区间2℃防止临界点震荡;Get_Median()排序复杂度O(n²)但n=5时仅需10次比较,远低于HAL_Delay()开销。

3.3 串口协议设计与上位机数据解析兼容性处理

为适配不同上位机(如Python PyQt界面、Arduino串口监视器),定义轻量级文本协议:

[TEMP:25.3][HUMI:42.1][LIGHT:385][SOIL:621][HEAT:ON][FAN:OFF][TS:1672531200]

S4_SEND_DATA状态中拼接字符串:

char tx_buffer[128]; snprintf(tx_buffer, sizeof(tx_buffer), "[TEMP:%d.%01d][HUMI:%d.%01d][LIGHT:%d][SOIL:%d][HEAT:%s][FAN:%s][TS:%lu]", (int)(temp_val/100), (int)(temp_val%100), (int)(humi_val/100), (int)(humi_val%100), light_adc, soil_adc, heat_flag ? "ON" : "OFF", fan_flag ? "ON" : "OFF", HAL_GetTick() / 1000); HAL_UART_Transmit(&huart1, (uint8_t*)tx_buffer, strlen(tx_buffer), 100);

关键验证点HAL_UART_Transmit()超时值设为100ms,避免串口阻塞主循环;snprintf()长度必须严格限制,防止缓冲区溢出;所有数值字段统一为整数+小数位格式(如25.3℃存为253),消除浮点精度差异。

4. PDF报告自动生成与源码结构化归档:让评审老师3秒定位核心创新点

4.1 使用Python脚本批量提取源码关键信息生成技术文档框架

评审老师最关注“你做了什么改进”,而非“你用了哪些函数”。编写gen_report.py自动分析源码:

import re from pathlib import Path def extract_key_features(): src_dir = Path("Core/Src") features = [] # 提取状态机状态数 main_c = (src_dir / "main.c").read_text() states = len(re.findall(r'S\d+', main_c)) features.append(f"实现{states}状态非阻塞温室控制状态机") # 提取ADC通道数 adc_init = re.search(r'hadc1\.Init\.NbrOfConversion\s*=\s*(\d+)', main_c) if adc_init: features.append(f"配置{adc_init.group(1)}路ADC同步采样") # 提取DHT22时序精度 dht_file = (src_dir / "dht22.c").read_text() delay_line = re.search(r'DHT22_Delay_us\((\d+)\)', dht_file) if delay_line and int(delay_line.group(1)) <= 100: features.append("DHT22通信时序误差<100us(满足80±10μs要求)") return features if __name__ == "__main__": print("【核心创新点】") for feat in extract_key_features(): print(f"• {feat}")

执行命令python gen_report.py > report_summary.txt,该脚本直接读取源码文件,避免人工撰写时遗漏真实实现细节。

4.2 PDF报告内容组织与图表嵌入规范(适配答辩PPT复用)

PDF报告必须包含可验证的实测数据,而非理论描述。按以下结构组织:

章节必含内容技术要点
第3章 硬件设计STM32F103C8T6最小系统原理图(标注晶振电容值22pF)、继电器驱动电路(标出S8050基极限流电阻1kΩ)、DHT22接线图(强调上拉电阻4.7kΩ)晶振电容计算公式:C = (C1×C2)/(C1+C2) + Cstray,其中Cstray取3–5pF
第4章 软件架构状态机状态转移图(Mermaid语法,但PDF中转为矢量图)、ADC-DMA数据流图、串口协议帧结构表状态转移图必须标注每个箭头的触发条件(如“TIM2中断→READ_SENSORS”)
第5章 实测数据温室环境24小时温湿度曲线图(X轴时间,Y轴双坐标)、继电器动作次数统计表(区分加热/通风)、串口通信误码率测试结果(发送1000帧,错误0帧)曲线图横坐标必须为真实时间戳,禁止用“第1次采集”等模糊表述

提示:使用wkhtmltopdf工具将HTML报告转PDF时,添加--enable-local-file-access参数支持本地图片引用;所有图表文件名必须含版本号(如fig_state_machine_v1.2.png),避免答辩时展示过期截图。

4.3 源码包目录结构标准化(让老师3秒找到main函数)

压缩包内目录必须严格遵循嵌入式开发惯例,禁止出现源码/代码/等冗余层级:

STM32_Greenhouse_V1.2/ ├── Drivers/ │ ├── STM32F1xx_HAL_Driver/ # 官方HAL库(精简版,仅保留ADC/UART/GPIO) │ └── BSP/ # 板级支持包(含OLED驱动、DHT22驱动) ├── Core/ │ ├── Inc/ # 头文件:main.h, dht22.h, relay.h │ └── Src/ # 源文件:main.c(含状态机主循环), dht22.c, relay.c ├── Middleware/ │ └── cJSON/ # 轻量JSON解析(用于串口协议) ├── Project/ │ ├── STM32F103C8Tx_FLASH.ld # 链接脚本(指定RAM起始地址0x20000000) │ └── MDK-ARM/ # Keil5工程文件(.uvprojx) ├── Report/ │ ├── Greenhouse_Report.pdf # 20页以内,含目录与页眉“STM32温室控制系统” │ └── Test_Data.xlsx # 原始测试数据(含时间戳列) └── README.md # 一行说明:“Keil5 v5.37编译,ST-Link V2下载,串口115200”

强制规范main.c必须位于Core/Src/下且为唯一入口文件;Drivers/目录不得包含未使用的外设驱动(如删除SPI/USB相关文件);Report/目录中PDF文件名必须含项目全称与版本号,避免毕业设计.pdf等模糊命名。

5. 答辩现场快速验证技巧:3分钟演示系统鲁棒性与真实环境适应力

5.1 设计可复现的边界测试用例(应对老师“故意刁难”)

答辩时老师常问:“如果传感器断线怎么办?”、“突然断电重启后状态是否正确?”。提前准备3个硬核测试:

测试编号操作步骤预期现象技术原理
T1拔掉DHT22数据线,观察串口输出持续发送[TEMP:ERR][HUMI:ERR],继电器保持断电状态在DHT22读取函数中添加超时检测(HAL_GetTick()-start_time > 200),失败时置ERR标志
T2长按复位键后立即观察OLED显示显示“INIT OK”后3秒内完成首次温湿度读取SystemClock_Config()后插入HAL_Delay(100)确保晶振稳定,ADC校准在HAL_ADC_Init()后立即执行
T3用吹风机对准DHT22持续加热30秒温度值平稳上升至45℃,无跳变;加热继电器在温度>30℃时闭合,<28℃时断开迟滞比较+中值滤波双重保障,避免单次异常读数触发误动作

执行要点:T1测试必须在答辩前10分钟现场演示,证明系统具备故障自诊断能力;T3测试用普通家用吹风机(非实验室恒温箱),体现真实场景适应性。

5.2 串口数据实时可视化技巧(让老师直观看到控制效果)

使用Python的pyserial+matplotlib制作简易监控界面,避免依赖复杂上位机软件:

import serial import matplotlib.pyplot as plt from collections import deque # 初始化串口(Windows下COM3,Mac下/dev/tty.usbmodem14101) ser = serial.Serial('COM3', 115200, timeout=1) # 创建滚动数据队列(保存最近100个数据点) temp_data = deque(maxlen=100) humi_data = deque(maxlen=100) plt.ion() # 开启交互模式 fig, ax = plt.subplots() line_temp, = ax.plot([], [], 'r-', label='Temperature') line_humi, = ax.plot([], [], 'b-', label='Humidity') ax.set_ylim(0, 100) ax.legend() while True: try: line = ser.readline().decode('utf-8').strip() if '[TEMP:' in line: # 解析[TEMP:25.3]格式 temp_match = re.search(r'\[TEMP:(\d+\.\d+)\]', line) humi_match = re.search(r'\[HUMI:(\d+\.\d+)\]', line) if temp_match and humi_match: temp_data.append(float(temp_match.group(1))) humi_data.append(float(humi_match.group(1))) line_temp.set_data(range(len(temp_data)), temp_data) line_humi.set_data(range(len(humi_data)), humi_data) ax.relim() ax.autoscale_view() plt.pause(0.01) except: pass

部署方案:将上述脚本与pyserialmatplotlib打包为exe(使用PyInstaller),答辩时双击运行即可实时绘图,无需安装Python环境。图表标题必须显示“STM32温室控制系统实时监控(2024-03-15)”。

5.3 关键参数速查表(答辩时放在手边的“救命纸”)

将最可能被问到的参数整理成A6大小速查卡,打印后夹在报告扉页:

参数类别具体参数当前值修改位置备注
时钟系统时钟72MHzSystemClock_Config()HSE晶振8MHz经PLL×9
ADC采样周期144 cycleshadc1.Init.SamplingTime对应1.8μs采样时间
DHT22响应低电平80μsDHT22_Delay_us(80)实测误差±5μs
继电器切换间隔100msRelay_Control()HAL_Delay()小于该值触点寿命锐减
串口帧间隔5000msHAL_TIM_Base_Start_IT(&htim2)TIM2定时器重装载值35999(72MHz/2)

使用技巧:当老师问“ADC采样精度多少?”时,直接翻到此表指出“12位分辨率,有效位11位(受电源纹波影响)”,比背诵数据手册更显专业。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询