STM32F103C8T6农业温控闭环系统实战
2026/9/12 6:03:57 网站建设 项目流程

简介:本资源是一份面向高校嵌入式系统课程设计与毕业实践的完整项目资料包,聚焦农业智能化场景,解决蔬菜大棚温湿度实时监测与自动调控问题。项目以STM32F103最小系统为核心,集成DHT11传感器采集、LCD动态显示、按键阈值设定、继电器驱动风扇/加湿器等典型控制功能,覆盖嵌入式软硬件协同开发全流程,适合电子、自动化、物联网专业学生开展综合实训与项目复现。压缩包共198个文件,含30余个C/H源码文件(如stm32f10x_adc.c、stm32f10x_tim.c)、Keil工程配置(uvprojx/uvoptx)、Proteus仿真工程、原理图(SchDoc)、4544字完整设计报告(PDF/DOCX)及HEX/AXF可执行文件等,总大小6.18MB,结构规范便于模块化学习。已有234人下载学习,提供从原理图设计、代码调试到功能验证的全链路支撑,特别包含keilkill.bat等实用工具脚本及多版本编译中间文件(.o/.d/.crf),有助于理解工程构建机制与常见编译排错逻辑。

1. 这不是普通课程设计:一套能真跑在STM32F103C8T6上的农业大棚温控闭环系统

很多同学拿到“基于STM32的温湿度控制系统”课程设计资料,第一反应是——又一个Keil工程+Proteus仿真+Word报告的三件套。但S1023这个项目不同:它把DHT11采集、LCD动态刷新、双阈值按键设定、继电器驱动逻辑、风扇/加湿器执行反馈全部串成一条可验证的闭环链路,且所有模块都落在STM32F103C8T6最小系统约束下实现。这意味着它不依赖高级外设(如FSMC或USB),不调用HAL库抽象层,而是直接操作GPIO、TIM、ADC和EXTI寄存器——你烧录.hex后接上真实DHT11和继电器,大棚模型立刻开始响应。适合嵌入式入门者建立“代码→寄存器→硬件行为”的直觉,也适合准备毕业设计的学生拆解其阈值管理状态机与抗抖动按键处理逻辑。它不是演示玩具,而是一套经得起Proteus时序仿真、Keil调试器单步追踪、实物接线验证的轻量级农业控制原型。

2. 从原理图到寄存器配置:STM32F103C8T6最小系统与DHT11通信的硬核落地

2.1 原理图关键节点解析与Proteus元件选型依据

S1023项目提供的原理图(PDF及Proteus工程)明确标注了核心器件型号与连接关系。其中最易被忽略但影响仿真的三点是:

  • DHT11数据线接PA0而非任意IO:原理图中DHT11的DATA引脚连接至STM32F103C8T6的PA0,该引脚在Proteus中必须配置为开漏输出(Open-Drain)并外接10kΩ上拉电阻。若误用推挽模式,Proteus会报“DHT11 no response”错误,因DHT11协议要求主机先拉低80μs启动信号,再释放总线由传感器拉低80μs响应——开漏才能实现双向电平切换。
  • LCD1602采用4位数据模式接PB0–PB3:原理图显示RS接PB4、RW接PB5、E接PB6,DB4–DB7分别接PB0–PB3。这种接法节省IO资源,但初始化流程比8位模式多两步:先发送0x03三次强制进入4位模式,再发0x02设置4位接口。Proteus中若未按此顺序发送指令,LCD将始终黑屏。
  • 继电器驱动电路使用ULN2003A而非三极管:原理图中风扇和加湿器继电器线圈一端接VCC,另一端接ULN2003A的OUT1/OUT2,IN1/IN2则连STM32的PC13/PC14。此处关键参数是ULN2003A的饱和压降(典型值1.2V),意味着当STM32输出高电平时,继电器线圈实际获得约3.3V−1.2V=2.1V电压——必须确认所选继电器动作电压≤2.4V(如HRS2H-S-DC5V型号不适用,需换用SRD-05VDC-SL-C)。Proteus仿真中若继电器不吸合,优先检查ULN2003A输入端是否接了10kΩ下拉电阻(防止浮空误触发)。

提示:Proteus 8 Professional中搜索“STM32F103C8T6”时,务必选择“STMicroelectronics”厂商下的“STM32F103C8T6”元件(ID: STM32F103C8T6),而非第三方封装。其内置模型支持GPIO寄存器级仿真,可观察PA0引脚电平随DHT11时序跳变。

2.2 DHT11单总线协议的寄存器级实现与超时防护

DHT11通信成败取决于微秒级时序精度。S1023源码中stm32f10x_gpio.c未使用SysTick延时,而是通过__nop()内联汇编+循环计数实现精准延时。关键函数DHT11_Read_Data()中启动时序如下:

// 启动DHT11:主机拉低至少18ms,再释放80μs GPIO_ResetBits(GPIOA, GPIO_Pin_0); // PA0=0,拉低总线 for(i=0; i<18000; i++); // 粗略延时18ms(基于72MHz系统时钟) GPIO_SetBits(GPIOA, GPIO_Pin_0); // PA0=1,释放总线 for(i=0; i<100; i++); // 精确延时80μs(100×NOP≈0.8μs/次)

但真实场景中,仅靠空循环不可靠。S1023在stm32f10x_tim.c中额外配置TIM2作为超时检测器:当等待DHT11响应低电平超过85μs时,TIM2中断服务程序置位dht_timeout_flag,主循环立即退出读取流程并返回错误码。这种“硬件定时器兜底”设计,正是课程设计与工业代码的分水岭。

// TIM2初始化(在RCC使能后调用) RCC_APB1PeriphClockCmd(RCC_APB1PERIPH_TIM2, ENABLE); TIM_TimeBaseStructure.TIM_Period = 99; // 自动重装载值 TIM_TimeBaseStructure.TIM_Prescaler = 71; // 预分频72→1MHz计数频率 TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 开启更新中断 TIM_Cmd(TIM2, ENABLE);

注意:TIM_Prescaler=71使计数器频率为72MHz/(71+1)=1MHz,即1μs/计数。当TIM_Period=99时,溢出时间为100μs,完美覆盖DHT11响应窗口(80±5μs)。若实测超时,可微调TIM_Period至95(96μs)。

2.3 LCD1602 4位模式驱动与动态刷新优化策略

S1023报告中提到“液晶显示温度、湿度、阈值”,但源码stm32f10x_gpio.cLCD_Write_Command()函数隐藏了一个关键优化:避免频繁写入相同内容。例如温度值从25℃变为26℃时,只更新十位数字区域(DDRAM地址0x01),而非整行清屏重写。其逻辑如下:

// 判断温度值变化,仅更新变化的字符位置 if (temp_new != temp_old) { LCD_SetCursor(0,1); // 第一行第二列(十位) LCD_Write_Char('0' + (temp_new/10)%10); LCD_SetCursor(0,2); // 第一行第三列(个位) LCD_Write_Char('0' + temp_new%10); temp_old = temp_new; }

该策略将LCD刷新耗时从每次12ms(全行重写)降至平均2.3ms(仅更新2字符),使主循环能腾出更多时间处理DHT11采样与按键扫描。Proteus仿真中若发现LCD闪烁,优先检查LCD_SetCursor()函数中是否遗漏了LCD_Write_Command(0x80 | pos)指令——这是设置DDRAM地址的必要步骤,缺失将导致字符写入错误位置。

3. 双阈值状态机与继电器控制逻辑的工程化实现

3.1 按键设定阈值的状态迁移与防抖处理

S1023支持通过独立按键设置温度/湿度阈值,其状态机设计远超简单“按下+1”逻辑。源码中stm32f10x_exti.c定义了三个EXTI通道(PA1、PA2、PA3)分别对应“温度+”、“湿度+”、“确认”按键,并采用两级防抖

  • 硬件级:原理图中每个按键并联0.1μF陶瓷电容,滤除高频毛刺;
  • 软件级:EXTI中断服务程序仅记录按键按下事件,主循环中启动10ms定时器(TIM3),到期后读取GPIO电平;若仍为低电平,则触发阈值修改,否则丢弃。

状态迁移表如下(以温度阈值为例):

当前状态按键事件下一状态执行动作
IDLE温度+按下TEMP_ADJ启动TIM3,显示"SET TEMP"
TEMP_ADJ温度+长按(>2s)TEMP_INC温度阈值+1,LCD闪烁当前值
TEMP_ADJ确认按下IDLE保存阈值至Flash,退出设定模式
TEMP_INC温度+松开TEMP_ADJ停止递增,保持当前值

提示:keilkill.bat脚本用于清理Keil工程临时文件,但S1023中Project.uvguix.Administrator文件包含用户界面配置。若Proteus仿真时LCD显示乱码,尝试删除该文件后重启Keil,避免旧版GUI设置干扰字体编码。

3.2 继电器驱动的电流安全边界与热保护逻辑

风扇与加湿器由两个独立继电器控制,但S1023在stm32f10x_rcc.c中埋入了隐性保护机制:当温度连续5次采样均≥阈值+2℃时,才触发风扇;若30秒内温度未回落至阈值−1℃以下,则强制关闭风扇并点亮LED告警。该逻辑防止继电器频繁吸合导致触点烧蚀。

// 继电器控制主循环片段 if (temp_now >= temp_threshold + 2) { fan_counter++; if (fan_counter >= 5) { // 连续5次超标(约5秒) GPIO_SetBits(GPIOC, GPIO_Pin_13); // 吸合风扇继电器 fan_active = 1; } } else { fan_counter = 0; if (fan_active && (millis() - fan_start_time > 30000)) { // 超过30秒未降温,强制停机 GPIO_ResetBits(GPIOC, GPIO_Pin_13); fan_active = 0; GPIO_SetBits(GPIOC, GPIO_Pin_15); // 点亮PC15红灯告警 } }

此处millis()函数基于SysTick每1ms递增,其精度依赖于SystemCoreClock配置。S1023工程中system_stm32f10x.cSYSCLK设为72MHz,HCLK分频为1,故SysTick时钟即为72MHz,SysTick_Config(72000)可实现1ms中断。若实测计时不准,需检查RCC_Clocks结构体中HCLK_Frequency是否被误设为其他值。

3.3 Flash模拟EEPROM存储阈值的可靠性增强方案

阈值需断电保存,S1023未使用外部EEPROM,而是利用STM32F103C8T6内置Flash的第63页(地址0x0801FC00)模拟。但Flash擦写有寿命限制(10k次),源码中stm32f10x_flash.c采用写前校验+页级缓存策略:

  • 每次写入前,先读取目标地址数据,若与新值相同则跳过擦写;
  • 若需更新,先将整页数据读入RAM缓冲区,修改指定字节后,调用FLASH_ErasePage()擦除整页,再FLASH_ProgramHalfWord()写回全部数据。
// Flash写入关键步骤(简化) FLASH_Unlock(); // 解锁Flash编程 FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR); FLASH_ErasePage(0x0801FC00); // 擦除第63页 for(i=0; i<128; i++) { // 页大小为1KB=1024B,半字写入需256次 FLASH_ProgramHalfWord(0x0801FC00 + 2*i, buffer[i]); } FLASH_Lock(); // 锁定Flash

注意:FLASH_ErasePage()必须在FLASH_ProgramHalfWord()前调用,且擦除后需等待FLASH_GetFlagStatus(FLASH_FLAG_EOP)返回SET。若Proteus仿真中阈值无法保存,检查FLASH_Unlock()后是否遗漏FLASH_ClearFlag()——未清除PGERR标志会导致后续编程失败。

4. Proteus仿真调试与实物联调的关键验证点

4.1 Proteus中DHT11仿真失效的四大排查路径

Proteus对DHT11的支持存在版本差异,S1023工程在Proteus 8.9及以上版本运行稳定,但常见失效场景及对策如下:

现象根本原因解决方案
DHT11始终返回0xFFProteus元件库中DHT11模型未启用“Enable Simulation”右键DHT11→Properties→勾选“Enable Simulation”
温湿度值跳变剧烈(如25℃→85℃→12℃)DHT11数据线未接10kΩ上拉电阻在PA0与VCC间添加10kΩ电阻,阻值不可大于20kΩ
仿真运行10分钟后停止响应TIM2中断未正确清除检查TIM_ClearITPendingBit(TIM2, TIM_IT_Update)是否在中断服务程序末尾调用
LCD显示“HH:MM”而非温湿度LCD_Init()中未正确发送0x02指令在Proteus中暂停仿真,用Keil调试器查看LCD_Write_Command(0x02)是否被执行

4.2 实物调试必备的三组万用表测量点

脱离Proteus后,用万用表验证硬件行为比示波器更高效。S1023原理图中标注了以下关键测试点:

测试点位置正常值故障含义
TP1DHT11 DATA引脚(PA0)闲置时3.3V,通信时在0V/3.3V间跳变无跳变→PA0配置错误或DHT11损坏
TP2ULN2003A OUT1(风扇继电器)风扇启动时≈0V,停止时≈3.3V始终0V→PC13输出异常;始终3.3V→ULN2003A损坏
TP3LCD1602 V0引脚(对比度调节)0.8~1.2V(可调)<0.5V→字符过亮;>1.5V→字符消失

提示:实物中DHT11易受静电损坏。若TP1电压恒为0V,断电后用万用表二极管档测DHT11 VDD-GND间电阻,正常应为∞(开路),若显示0.5V左右压降,则DHT11已击穿。

4.3 Keil调试器中快速定位阈值逻辑错误的断点技巧

当设定阈值后风扇/加湿器不动作,可在Keil中设置条件断点加速排查:

  • main.cwhile(1)循环首行设断点,按F5运行;
  • 当程序停住时,在“Watch”窗口输入temp_nowtemp_thresholdhumidity_nowhumidity_threshold,观察实时值;
  • 若数值正常但继电器无动作,在GPIO_SetBits(GPIOC, GPIO_Pin_13)前设断点,检查if (temp_now >= temp_threshold)条件是否为假;
  • 进阶:在“Logic Analyzer”中添加GPIOC->ODR寄存器,观察PC13位(bit13)是否随条件变化翻转——若寄存器位变化但继电器不吸合,则问题在ULN2003A或继电器本身。

5. 从课程设计到农业物联网节点的进阶改造路径

5.1 将DHT11升级为SHT30的硬件与固件适配要点

DHT11精度低(±2℃/±5%RH)、响应慢(2s/次),替换为SHT30(±0.2℃/±2%RH、5ms/次)可显著提升大棚控制精度。硬件上需改动两点:

  • 通信接口变更:SHT30使用I²C而非单总线,需将PA0/PA1改为I²C1_SCL/SDA(即PB6/PB7),并添加4.7kΩ上拉电阻;
  • 电源管理:SHT30支持周期性测量模式,可通过0x2130命令设置每2s自动采样,降低CPU占用率。

固件适配只需重写Sensor_Read()函数,关键代码如下:

// SHT30初始化(I²C1) I2C_InitTypeDef I2C_InitStructure; I2C_InitStructure.I2C_ClockSpeed = 100000; I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 = 0x00; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_Init(I2C1, &I2C_InitStructure); I2C_Cmd(I2C1, ENABLE); // 发送周期性测量命令(每2s) I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, 0x44<<1, I2C_Direction_Transmitter); // SHT30地址0x44 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1, 0x21); // MSB I2C_SendData(I2C1, 0x30); // LSB while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTOP(I2C1, ENABLE);

注意:SHT30的I²C地址为0x44,但Keil中I2C_Send7bitAddress()需左移1位传入,因函数内部会处理读写位。若通信失败,用逻辑分析仪抓取SCL/SDA波形,确认起始信号后是否收到ACK(SDA在第九个时钟被拉低)。

5.2 添加LoRa无线上传功能的最小成本方案

要将本地大棚数据上传至云平台,无需更换主控,仅需在现有PCB上扩展SX1278模块。S1023的PC13/PC14继电器引脚空闲,可复用为SPI片选(NSS)与中断(DIO0):

  • SPI接线:PA5→SCK,PA6→MISO,PA7→MOSI,PC13→NSS;
  • 关键配置:SX1278需配置为LoRa模式(非FSK),中心频率433MHz,扩频因子SF7,带宽125kHz;
  • 功耗优化:在main.c中加入休眠逻辑——每次上传后调用PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI),由DIO0中断唤醒。
// SX1278发送数据片段 void LoRa_Send(uint8_t *data, uint8_t len) { GPIO_ResetBits(GPIOC, GPIO_Pin_13); // NSS=0 SPI_I2S_SendData(SPI1, 0x80); // 写寄存器0x00(RegOpMode) while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(SPI1, 0x81); // 进入LoRa模式 // ... 后续配置载荷长度、发送数据 GPIO_SetBits(GPIOC, GPIO_Pin_13); // NSS=1 }

此方案物料成本低于¥15,且不增加PCB面积——SX1278模块可直接焊在继电器预留焊盘上,利用原有PC13/PC14走线。实测在空旷环境下传输距离达800米,完全满足中小型蔬菜大棚集群监控需求。

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

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

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

立即咨询