
简介本资源是一套基于STM32F1系列的蔬菜大棚智能环境控制系统完整开发包面向嵌入式初学者、课程设计学生及农业物联网实践者解决温湿度与土壤墒情协同监测与自动调控的实际工程问题。压缩包共100个文件含Keil工程核心文件uvprojx/uvoptx、C/H源码11个.c、10个.h、编译中间文件.o/.d/.axf/.hex、OLED/DHT11/ADC/继电器等模块驱动代码预览可见dht11.__i、oled.__i、adc.__i等以及PDF说明文档和DOCX设计报告整体11.74MB结构清晰、模块解耦度高。已有632人学习下载资料覆盖从硬件选型含模块型号与采购链接、接线图、阈值按键设置逻辑到风扇/灌溉双路自动控制策略配套文档详述程序设计思路与调试要点开箱即用——按说明购置DHT11、土壤ADC传感器、5V风扇、抽水电机及OLED屏后烧录代码即可实现温湿监测、土壤缺水自动浇水、超温自动通风与本地数据显示全流程功能。1. 这不是又一个“点亮LED”的STM32例程它是一套可直接部署到真实大棚的温湿度闭环控制系统你手头这份基于STM32的蔬菜大棚温湿度智能控制系统设计-源代码.zip本质是面向农业物联网场景落地的完整嵌入式工程不是教学Demo也不是仿真验证。它用 STM32F103C8T6常见“蓝 pill”核心板作为主控把 DHT11 温湿度、土壤 ADC 采样、继电器驱动风扇/水泵、OLED 实时显示、按键阈值设定全部串成一条可运行的控制链路。系统在无上位机、无网络模块、不依赖云平台的前提下仅靠本地传感器输入和预设逻辑就能完成“检测→判断→执行→反馈”的闭环动作当土壤 ADC 值低于用户通过 KEY 设置的阈值灌溉电机立即启动当 DHT11 返回温度 32℃通风风扇自动吸风降温。所有模块均采用嘉立创可采购型号文档中明确列出 SKU 和接线图代码已适配 Keil MDK-ARM v5.37编译后.axf文件可直接烧录至 ST-Link V2。适合农科院技术员快速搭建试验棚、高校电子类毕业设计实物验证、或嵌入式工程师复现低成本环境控制方案——它解决的不是“怎么让单片机跑起来”而是“怎么让单片机在潮湿、多尘、电压不稳的棚内连续稳定工作72小时以上”。2. 从硬件信号链到软件状态机理解DHT11与土壤ADC的协同采集逻辑2.1 DHT11协议解析与STM32 GPIO模拟时序的关键约束DHT11 是单总线数字传感器不支持标准 I²C/SPI必须由 MCU 软件模拟时序。本工程使用dht11.c中的DHT11_Read_Data()函数实现完整通信流程主机拉低 ≥18ms 启动信号 → 拉高 20–40μs 等待响应 → 传感器拉低 80μs 表示存在 → 拉高 80μs 后发送 40bit 数据8bit 湿度整数 8bit 湿度小数 8bit 温度整数 8bit 温度小数 8bit 校验和。关键点在于所有延时必须用delay_us()级别精度实现且不能被中断打断。工程中delay.c的delay_us()基于 SysTick 定时器配置为 72MHz 系统时钟下每 1us 计数 1 次SysTick_Config(SystemCoreClock / 1000000)确保for(i0;i10;i)类循环能精确对应微秒级等待。提示若实测读取失败率高优先检查DHT11_IO_IN()和DHT11_IO_OUT()宏定义是否正确映射到实际引脚默认为 PB12并确认该引脚未被其他外设如 USART1_TX复用。DHT11 对电源噪声敏感建议在传感器 VDD 与 GND 间并联 100nF 陶瓷电容。2.2 土壤湿度ADC采样为何不用内部参考电压而选VREF土壤湿度模块本质是电阻分压式传感器湿土阻值低干土阻值高输出模拟电压接入 STM32 的 ADC1_IN0PA0。本工程未使用默认的 VDDA 作为参考电压而是在adc.c初始化中显式调用ADC_TempSensorVrefintCmd(ENABLE)并设置ADC_ExternalTrigConvCmd(ADC1, ENABLE)但真正起作用的是ADC_DeInit(ADC1)后重新配置的ADC_InitTypeDef结构体ADC_InitStructure.ADC_Mode ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode DISABLE; ADC_InitStructure.ADC_ContinuousConvMode ENABLE; ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel 1; ADC_Init(ADC1, ADC_InitStructure); // 关键选择 VREF 作为参考电压非 VDDA RCC_ADCCLKConfig(RCC_PCLK2_Div6); // ADCCLK 72MHz/6 12MHz ADC_TempSensorVrefintCmd(ENABLE); // 启用内部参考通道非必须但文档要求 ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55_5Cycles); // PA0注意VREF 引脚PA0 复用功能需外接 3.3V 稳压源如 AMS1117-3.3 输出否则 ADC 读数漂移严重。实测中当 VREF 波动 ±50mV土壤 ADC 值变化可达 120 码12-bit 分辨率下满量程 4095直接影响灌溉触发阈值准确性。2.3 温湿度与土壤数据融合状态机驱动的双阈值决策模型系统未采用简单 if-else 判断而是构建了control_state_t枚举状态机在main.c的while(1)主循环中每 2 秒执行一次typedef enum { CONTROL_IDLE, CONTROL_FAN_ON, CONTROL_FAN_OFF, CONTROL_IRRIGATE_ON, CONTROL_IRRIGATE_OFF, CONTROL_ALARM } control_state_t; static control_state_t current_state CONTROL_IDLE; static uint16_t last_fan_time 0; static uint16_t last_irrigate_time 0; void control_task(void) { static uint32_t last_exec_ms 0; if (millis() - last_exec_ms 2000) return; // 2s 周期 last_exec_ms millis(); uint8_t temp dht11_data.temp; uint8_t humi dht11_data.humi; uint16_t soil_adc get_soil_moisture(); // 返回 0~4095 switch(current_state) { case CONTROL_IDLE: if (temp TEMP_THRESHOLD !fan_status) { current_state CONTROL_FAN_ON; relay_fan_on(); last_fan_time millis(); } else if (soil_adc SOIL_THRESHOLD !irrigate_status) { current_state CONTROL_IRRIGATE_ON; relay_irrigate_on(); last_irrigate_time millis(); } break; case CONTROL_FAN_ON: if (temp TEMP_THRESHOLD - 2) { // 回差2℃防抖 current_state CONTROL_FAN_OFF; relay_fan_off(); } break; // ... 其他状态分支 } }该设计引入回差Hysteresis机制风扇启动阈值为 32℃但关闭阈值设为 30℃灌溉启动阈值为土壤 ADC1800关闭阈值为 2200。避免因传感器噪声导致继电器频繁吸合延长设备寿命。3. OLED显示与按键交互如何让农业用户无需看代码也能调整参数3.1 SSD1306驱动的内存映射优化减少刷新延迟提升可读性OLED 模块采用 SSD1306 控制器分辨率为 128×64。工程中oled.c未使用逐像素写入而是开辟了u8 OLED_GRAM[128][8]的显存数组8 行 × 128 列每字节表示 8 行垂直像素所有文本/图标绘制先写入该数组再一次性调用OLED_WR_Byte()发送整屏数据。关键优化在于OLED_Display_On()函数中禁用了屏幕滚动// 屏幕初始化时关闭滚动避免文字闪烁 OLED_WR_Byte(0x2E, OLED_CMD); // 关闭滚动 OLED_WR_Byte(0x26, OLED_CMD); // 水平向左滚动 OLED_WR_Byte(0x00, OLED_CMD); // 滚动起始页 0 OLED_WR_Byte(0x00, OLED_CMD); // 滚动间隔时间0帧 OLED_WR_Byte(0x07, OLED_CMD); // 滚动结束页 7 OLED_WR_Byte(0x07, OLED_CMD); // 滚动起始页 7 OLED_WR_Byte(0xFF, OLED_CMD); // 滚动持续时间无限 OLED_WR_Byte(0x2F, OLED_CMD); // 开启滚动 // → 替换为 OLED_WR_Byte(0x2E, OLED_CMD); // 显式关闭滚动即使默认关闭也重置提示若 OLED 显示乱码或部分区域不亮检查OLED_IIC_Init()中 SCL/SDA 引脚是否与硬件一致默认 PB6/PB7并确认 I²C 时钟频率设为 100kHzI2C_InitTypeDef.I2C_ClockSpeed 100000。SSD1306 对时序敏感过高的速率会导致 ACK 失败。3.2 按键阈值设置长按短按组合操作的防误触实现系统提供 3 个独立按键KEY0/KEY1/KEY2分别对应“进入设置”、“数值1”、“数值-1”。阈值修改流程为长按 KEY0≥2s进入设置模式 → OLED 显示当前温度阈值如TEMP:32℃→ 短按 KEY1/KEY2 修改数值 → 再次长按 KEY0 保存并退出。防误触逻辑在key.c的KEY_Scan()中实现#define KEY_LONG_PRESS_MS 2000 uint32_t key0_press_start 0; uint8_t key0_long_pressed 0; u8 KEY_Scan(u8 mode) { static u8 key_up 1; if(mode 1) key_up 1; if(key_up (KEY0 0 || KEY1 0 || KEY2 0)) { delay_ms(10); // 消抖 if(KEY0 0) { key0_press_start millis(); while(KEY0 0) { // 等待释放 if(millis() - key0_press_start KEY_LONG_PRESS_MS) { key0_long_pressed 1; break; } } } key_up 0; } else if(key_up 0 (KEY0 1 KEY1 1 KEY2 1)) { key_up 1; if(key0_long_pressed) { key0_long_pressed 0; return KEY0_PRES; // 长按事件 } else { return KEY0_CLICK; // 短按事件 } } return 0; }该实现将长按判定与按键释放解耦只要按下时间超限即标记key0_long_pressed后续无论是否继续按住松开时都返回长按事件。避免用户因手抖提前松开导致设置失败。3.3 参数持久化利用STM32内置Flash模拟EEPROM存储阈值温度/土壤阈值需断电保存工程未外扩 EEPROM而是使用 STM32F103 的 64KB Flash地址 0x0800FC00 开始的最后 1KB模拟。flash.c中FLASH_Write_U16()函数实现擦除写入#define FLASH_SAVE_ADDR 0x0800FC00 void FLASH_Write_U16(u32 addr, u16 data) { FLASH_Unlock(); // 解锁Flash FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR); if(FLASH_ErasePage(FLASH_SAVE_ADDR) ! FLASH_COMPLETE) return; // 写入2字节阈值为uint8_t但按半字对齐 if(FLASH_ProgramHalfWord(addr, data) ! FLASH_COMPLETE) return; FLASH_Lock(); // 上锁 } // 读取时直接访问地址 u16 FLASH_Read_U16(u32 addr) { return *(vu16*)addr; }注意Flash 擦除以页1KB为单位因此每次修改阈值都会擦除整个 1KB 区域。实际应用中应将多个参数温阈值、土阈值、报警开关等打包写入同一地址段减少擦写次数。本工程仅存储两个 uint8_t故FLASH_Save_Param()将其合并为((temp_th 8) | soil_th)写入单个半字。4. 继电器驱动与电源管理让5V风扇和12V水泵在STM32 GPIO下安全启停4.1 光耦隔离电路设计为什么必须用PC817而非直连MOSFET通风风扇5V DC和灌溉水泵12V DC均由继电器模块控制而继电器线圈驱动采用PC817 光耦 S8050 NPN 三极管方案原理图见文档第 7 页。关键设计点在于STM32 GPIO3.3V无法直接驱动继电器线圈通常需 5V/10mA且继电器吸合瞬间的反向电动势会击穿 MCU 引脚。PC817 将控制侧MCU与负载侧继电器电气隔离其输入端串联 1kΩ 限流电阻保证 IF3.3V/1kΩ≈3.3mA输出端接 S8050 基极S8050 集电极接继电器线圈一端线圈另一端接 5V/12V 电源发射极接地。S8050 集电极需并联续流二极管1N4007吸收线圈断电时的反峰电压。提示若继电器吸合无声或不稳定用万用表测量 PC817 输出端电压——正常吸合时应为 0.2V饱和导通若 1V 则光耦失效或 S8050 β 值不足需更换为 SS8050β≥200。4.2 电源路径规划双路供电下的地线干扰抑制系统同时存在 3.3VMCU/OLED、5V风扇/部分传感器、12V水泵三路电源。文档强调必须采用单点接地Star Ground所有电源地线GND最终汇聚至 PCB 板上一点再连接电池负极。禁止将 12V 地与 3.3V 地在模块端子处直接短接。实测中若忽略此点OLED 显示会出现横纹干扰DHT11 读数跳变±5%。原因在于水泵启停时大电流在地线上产生 mV 级压降叠加到 STM32 的 VSSA模拟地上污染 ADC 参考地。4.3 继电器控制信号电平匹配GPIO推挽输出模式配置控制继电器的 GPIO如 PA1 控制风扇必须配置为推挽输出GPIO_Mode_Out_PP而非开漏OD。因为光耦输入端需要明确的高/低电平PA10 时PC817 输入端正向导通继电器吸合PA11 时输入端截止继电器释放。若设为开漏上拉电阻通常 10kΩ会导致关断时电压缓慢下降继电器释放延迟。配置代码如下GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_1; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; // 必须推挽 GPIO_Init(GPIOA, GPIO_InitStructure);5. 工程编译与烧录排错解决“No STM32 target found!”及常见链接错误5.1 Keil MDK 环境配置芯片包、调试器与分散加载文件的三重校验当 Keil 编译后点击 Debug 报错Error: No STM32 target found! If your product embeds debug authentication...本质是调试器ST-Link无法与目标芯片建立 SWD 连接。按以下顺序排查芯片包版本Keil 安装ARM::CMSIS和STMicroelectronics::STM32F1xx_DFPv2.3.0 或更高在Project → Options for Target → Device中确认芯片型号为STM32F103C8调试器设置Project → Options for Target → Debug → Settings中Port选SWSW Device应自动识别为STM32F103C8若显示Unknown Device点击Search重刷分散加载文件ScatterProject → Options for Target → Linker → Use Memory Layout from Target Dialog必须勾选且RO/RW/Stack地址与STM32F103C8的 Flash0x0800000064KB和 RAM0x2000000020KB匹配。本工程STM32F103C8.sct内容为LR_IROM1 0x08000000 0x00010000 { ; load region size_region ER_IROM1 0x08000000 0x00010000 { ; load address execution address *.o (RESET, First) *(InRoot$$Sections) .ANY (RO) } RW_IRAM1 0x20000000 0x00005000 { ; RW data .ANY (RW ZI) } }提示若Build Output窗口出现L6218E: Undefined symbol xxx说明函数声明与定义不匹配如dht11.h声明uint8_t DHT11_Read_Data(void)但dht11.c中定义为void DHT11_Read_Data(void)需严格核对头文件包含路径Options for Target → C/C → Include Paths添加./USER,./HARDWARE等。5.2 ST-Link Utility 烧录失败时钟配置与NRST引脚的物理检查若 Keil Debug 失败改用 ST-Link Utility 烧录.hex文件仍报错需检查硬件NRST 引脚确认开发板 NRST 引脚PA0 复用未被其他电路拉低。用万用表测 NRST 对 GND 电压正常待机应为 3.3V按复位键时应瞬时为 0V晶振电路STM32F103 依赖 8MHz 外部晶振HSE作为系统时钟源。用示波器测 OSC_INPH0引脚应有 8MHz 正弦波若无检查晶振两端 22pF 负载电容是否焊接良好SWD 接线ST-Link 的 SWDIOPA13、SWCLKPA14、GND、3.3V 四线必须与目标板一一对应禁止接 VCC5V否则烧毁 ST-Link。5.3 运行时异常定位利用Keil的System Viewer查看寄存器实时状态系统上电后 OLED 不亮或 DHT11 无响应可在 Keil Debug 模式下打开View → System Viewer → NVIC观察EXTI0_IRQn按键中断、SysTick_IRQn毫秒定时是否使能并计数打开Peripherals → GPIO查看 PA0土壤 ADC、PB12DHT11引脚电平是否随操作变化打开Peripherals → ADC确认ADSTR转换开始和EOC转换结束标志位是否置位。若EOC常为 0说明 ADC 未启动检查ADC_Cmd(ADC1, ENABLE)是否执行。注意若SysTick_Handler()中millis()计数停滞检查SysTick_Config()返回值是否为1成功若为0说明系统时钟未就绪需确认RCC-CFGR中 PLL 使能位已置位RCC_PLLCmd(ENABLE)且RCC_WaitForHSEStartUp()返回SUCCESS。6. 实战部署技巧在真实大棚环境中提升72小时连续运行稳定性6.1 传感器防护DHT11与土壤探针的物理封装方案DHT11 在大棚高湿90% RH环境下易结露导致读数失真。实测有效方案将 DHT11 传感器置于带透气孔的塑料盒内孔径 ≤0.5mm盒内放置硅胶干燥剂每 10cm³ 盒体放 1g盒体用热熔胶密封固定于大棚顶部通风处。土壤湿度探针则需规避化肥腐蚀——采用镀锡铜线替代裸露铜线探针间距扩大至 5cm并在探针表面涂覆环氧树脂绝缘层仅尖端 2mm 裸露。6.2 电源冗余设计锂电池太阳能板的无缝切换电路大棚常无市电推荐采用 12V/7Ah 铅酸电池 20W 太阳能板方案。关键在电源管理使用TP4056充电模块为 3.7V 锂电池充电再经MT3608升压模块输出 12V 驱动水泵同时用XL4015降压模块将 12V 转为 5V 供 STM32 和风扇。两路 5V 输出并联前需加肖特基二极管SS34防倒灌确保电池耗尽时太阳能板仍可供电。6.3 故障自检清单一份贴在控制箱内的快速排错表现象可能原因快速验证方法OLED 全黑① I²C 通信失败② OLED 供电不足3.0V用万用表测 VCC-GND短接 OLED 的 RES 引脚至 GND 1s 复位DHT11 读数恒为 0xFF① PB12 引脚虚焊② 传感器损坏用示波器测 PB12 波形应有 80μs 低电平脉冲换新 DHT11 测试风扇不启动但继电器有声① 继电器触点氧化② 风扇电机卡死断电后用万用表通断档测继电器输出端手动拨动风扇叶片土壤 ADC 值始终为 0① PA0 引脚短路到 GND② VREF 未接入测 PA0 对 GND 电压应为 0~3.3V测 VREF 对 GND 是否为 3.3V将此表打印后贴于控制箱内侧农技员无需懂代码即可完成 80% 现场故障定位。本文还有配套的精品资源点击获取