news 2026/9/16 20:15:16

STM32F103C8T6温室环境监测系统开发实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32F103C8T6温室环境监测系统开发实战

简介:面向高校电子、计算机类学生,这是一套完整的基于STM32F103C8T6的温室环境监测系统设计资料,实时采集空气温湿度、土壤湿度和光照强度,适用于毕业设计、期末大作业及嵌入式入门实践。压缩包共280个文件,约9.12MB,主要包含C源文件与H头文件、Keil工程文件(uvprojx)、编译生成的hex/axf固件、uvoptx工程配置及备份文件,并有jpg/png图片和Markdown文档辅助阅读,目录结构清晰,便于按模块查阅。源码与设计文档相互对应,覆盖ARM Cortex-M3内核的外设驱动、传感器数据采集、逻辑处理与显示交互等关键环节,可帮助学习者理解嵌入式监控系统的完整开发流程。目前已有56人学习,作为一款结构紧凑、物料齐全的参考项目,具备较好的实践与借鉴价值。

1. 从需求到板卡:STM32F103C8T6温室监测要解决什么

一个温室大棚里,温度、湿度、光照和土壤水分的数据如果全靠人工去读,半夜和正午各跑一趟,既耽误事也容易漏报。STM32F103C8T6温室环境监测系统的核心任务,就是把这套“人肉巡检”换成一片 64KB Flash、20KB RAM 的 M3 内核芯片,通过传感器定时采集环境参数,在 OLED 上显示或经串口上报给上位机,再由继电器联动风机、补光灯和水泵。这里有个反直觉的结论:系统翻车点往往不在算法和协议,而在电源纹波、引脚浮空、传感器时序抖动这些最容易被忽略的地方。本文适合刚用 CubeMX 拉过外设、想把一套监测系统真正落地的开发者,也会给到源码工程里值得调整的几个关键参数。硬件成本可以压在 60 元以内,但调试时间的预算建议留足一天。

2. 硬件设计与引脚规划:STM32F103C8T6最小系统板接线与传感器选型

2.1 传感器选型:DHT11、SHT30 还是 DS18B20

温室监测最常见的三个传感器是 DHT11、SHT30 和 DS18B20,三者定位完全不同,不能闭眼选。DHT11 便宜、单总线协议、无需寄存器配置,读一次 40 位数据即可同时拿到温度和湿度,适合教学原型和预算敏感的 DIY 项目。它的精度为温度 ±2℃、湿度 ±5% RH,分辨率只有 1,温室环境控制够用,但想用它做论文级数据采集就勉强了。

SHT30 是 I2C 接口,内部有校准,温度精度 ±0.3℃、湿度 ±2% RH,响应速度也快,适合需要数据质量的场景,但引脚只有 1mm 间距的 DFN 封装,手工焊接要谨慎。DS18B20 只测温度,精度略高于 DHT11,好处是支持菊花链并联,一根总线挂十几个节点,适合测大棚不同位置的温差。多数初版方案建议用 DHT11 先把链路跑通,再把驱动替换成 SHT30,因为它们的代码抽象层可以复用——一个读温湿度的接口函数,C 语言里用函数指针或者直接宏切换即可。

2.1.1 电源与电平匹配的先后顺序

传感器供电用 3.3V 还是 5V,要先看模块上的电平转换电路。常见的 DHT11 模块板载上拉电阻和稳压,可以 3.3V 直连 STM32;如果是裸传感器加 4.7kΩ 上拉到 3.3V 也可。注意不能把 5V 传感器输出直接接到 PA0 这类 5V 容忍引脚以外的 GPIO 上,否则长期运行可能损伤芯片。

2.2 STM32F103C8T6 引脚分配与原理图要点

STM32F103C8T6 共 48 引脚,LQFP48 封装,可用 GPIO 约 37 个。温室监测系统不需要太多外设,引脚规划原则是:把有复用功能的引脚优先给串口和 I2C,普通 GPIO 留给传感器和继电器。下面是一张实测可用的分配表。

外设引脚说明
USART1_TXPA9调试日志输出,默认 115200 8N1
USART1_RXPA10预留上位机指令输入
DHT11 数据PB12开漏输出 + 外部 4.7kΩ 上拉
OLED I2C_SCLPB6I2C1 时钟,注意总线速度设为 400kHz
OLED I2C_SDAPB7I2C1 数据
继电器 INPA1推挽输出,高电平吸合
水泵/风机反馈PA2外部中断输入,用于检测设备状态
按键PA0下拉输入,切换显示页
状态 LEDPC13板载 LED,心跳指示

原理图上最容易出错的是 BOOT0 和 BOOT1 的接法。BOOT0 经 10kΩ 下拉到地,BOOT1 悬空即可,注意不要为了省事把 BOOT0 直接接地,因为后续若想恢复系统 Bootloader,还得再飞线。复位电路用 10kΩ 上拉加 100nF 电容到地,NRST 引脚对地并联按键可选。晶振方面,无源 8MHz 晶振两端各接 20pF 负载电容,这是 HSE 的标准配置,电容容值偏差过大会导致启动失败,现象是程序卡在 SystemInit 里。

2.2.1 USB 转串口模块共用电源的坑

调试时如果用 CH340 模块给板子供电,注意 CH340 模块的 LDO 输出能力通常只有 100mA 左右。系统里继电器线圈吸合瞬间电流可达 70mA 甚至更高,加上 OLED 背光、传感器和芯片本身,瞬间电流会逼近极限,导致 MCU 掉电重启。建议继电器和传感器采用独立电源,共地不共源,或者至少拿万用表量一下稳定后的整机电流再决定供电方式。

2.3 最小系统板 vs 自制 STM32F103C8T6 最小系统电路

市面上常见的 STM32F103C8T6 最小系统板自带晶振、LDO、Type-C 和 SWD 下载口,直接插杜邦线就能用,调试阶段非常方便。但正式做样机或者比赛交付时,建议自已画板,核心就是最小系统电路:8MHz HSE 晶振、两个 20pF 电容、复位电路、BOOT 引脚配置、3.3V 电源去耦(10μF + 100nF 组合)、SWD 四线接口(SWDIO、SWCLK、GND、3.3V)。还要在 VDD 和 VDDA 之间加磁珠或 10Ω 电阻隔离,否则 ADC 采样会有可见的噪声。

3. 基于 CubeMX 的工程初始化与 STM32F103C8T6 串口通信配置

3.1 时钟树:从 8MHz HSE 倍频到 72MHz

打开 STM32CubeMX,芯片型号选 STM32F103C8Tx,RCC 设置为 HSE 外部晶振模式。时钟树配置里把 PLL Source 选为 HSE,PLLMUL 设为 x9,在 F103 上 HSE 8MHz 乘 9 得 72MHz,正好是主频上限。APB1 总线时钟最多 36MHz,APB2 是 72MHz,要让 USART 和 I2C 都跑在合理的频率下:USART1 挂在 APB2 上,I2C1 挂在 APB1 上。如果时钟树配错,串口算出来的波特率会出现系统性偏差,典型表现是:上位机能收到乱码,但用逻辑分析仪看波形又和预期波特率吻合不上。

3.1.1 调试口和下载口冲突的处理

调试时用 SWD 下载代码,需要确保 PA13/PA14 没有被重映射成普通 GPIO。CubeMX 默认保留 SWD 功能,但如果你在别的工程里把 PA13/PA14 改了,就会出现“能够连接但下载即失败”的闹心情况。处理办法是按住板子复位键,在 KEIL 设置里勾选 Reset and Run,点下载的同时松开复位,趁内核还在复位状态时写入 Flash。

3.2 USART1 串口通信初始化与重定向 printf

串口是这套系统的“眼睛”,所有传感器的原始值、解析结果和控制动作都要通过串口打印到调试终端。USART1 配置为异步模式,波特率 115200,数据位 8,停止位 1,无校验,无硬件流控。生成代码后需要重定向 printf 到串口,否则 printf 默认输出到标准库设备,看不到任何内容:

#include <stdio.h> int fputc(int ch, FILE *f) { while ((USART1->SR & USART_FLAG_TXE) == 0); USART1->DR = (uint8_t)ch; return ch; }

代码逻辑是查询发送数据寄存器空标志,TXE 为 1 时写入数据寄存器。这里的 USART1->SR 必须等 TXE 置位才能写 DR,否则数据会丢失。注意使用 MicroLIB 时 printf 会走 fputc 重定向,使用标准 C 库则需要额外处理半主机模式,否则程序会在 printf 处卡死。建议在 KEIL 的 Options 里勾选 Use MicroLIB,省去一堆麻烦。串口助手里如果发现第一行数据乱码、后续正常,多半是上位机波特率没对上,或者板子复位瞬间发送了半个字节。

3.3 I2C1 初始化与 OLED 显示驱动

OLED 屏选择 SSD1306 驱动的 0.96 寸 128x64 屏,I2C 地址通常为 0x78(7 位地址 0x3C)。CubeMX 里 I2C1 速度模式设为 Fast Mode,时钟 400kHz。注意 STM32F103 的 I2C 外设是硬件 I2C,存在一些兼容性问题,如果 OLED 初始化时卡在 HAL_I2C_IsDeviceReady 超时,可以先检查上拉电阻——标准做法是 SCL 和 SDA 各接一个 4.7kΩ 上拉到 3.3V。某些模块板载上拉,如果再加就会导致信号上升沿过缓,同样会超时。

uint8_t oled_init_cmds[] = { 0xAE, 0x20, 0x10, 0xB0, 0xC8, 0x00, 0x10, 0x40, 0x81, 0xFF, 0xA1, 0xA6, 0xA8, 0x3F, 0xD3, 0x00, 0xD5, 0x80, 0xD9, 0xF1, 0xDA, 0x12, 0xDB, 0x40, 0x8D, 0x14, 0xAF }; void OLED_WriteCmd(uint8_t cmd) { uint8_t buf[2] = {0x00, cmd}; HAL_I2C_Master_Transmit(&hi2c1, 0x3C << 1, buf, 2, 100); }

初始化命令序列完成关显示、设置页地址模式、显示偏移、电荷泵开启和显示开启。重点是 0x8D 命令后的参数 0x14,意味着开启内部电荷泵,不开启 OLED 完全不亮。0xDA 后的 0x12 是 COM 引脚配置,适用于 128x64 屏,如果显示内容出现上下分裂,检查这个值。

4. 数据采集与控制逻辑:DHT11 时序解析、阈值联动与状态机设计

4.1 DHT11 单总线时序与 STM32F103C8T6 GPIO 模拟

DHT11 的通信协议是单总线,主机必须先发起始信号把传感器从睡眠中唤醒:GPIO 拉低至少 18ms,然后释放并拉高 20-40μs,等待传感器响应。DHT11 随后拉低 80μs 表示应答,再拉高 80μs 准备送数据。之后是 40 位数据,每位以 50μs 低电平开头,如果接着的是 26-28μs 的高电平表示 0,70μs 的高电平表示 1。用 HAL 库的 HAL_GetTick 做延时会有 1ms 误差,无法满足微秒级时序要求,常见做法是直接操作寄存器:

static void DHT11_Delay_us(uint32_t us) { uint32_t count = us * 8; // 72MHz 下约 8 个周期/微秒 while (count--) { __NOP(); } } uint8_t DHT11_ReadBit(void) { uint8_t bit = 0; while (HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin) == GPIO_PIN_RESET); DHT11_Delay_us(40); // 跳过前 50us 低电平 + 高电平前半段 if (HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin) == GPIO_PIN_SET) { bit = 1; while (HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin) == GPIO_PIN_SET); } return bit; }

位读取的关键是“从低电平结束开始计时 40μs 后再采样”,如果在高电平刚出现就去读,0 和 1 还没分化,必然误判。__NOP() 循环的次数要根据实际主频微调,最简单可靠的验证方式是用逻辑分析仪抓 DHT11 数据脚波形,和协议手册对比。整套 40 位数据依次是湿度整数、湿度小数、温度整数、温度小数、校验和,校验和为前四个字节相加取低 8 位:

uint8_t DHT11_ReadData(uint8_t *humidity, uint8_t *temperature) { uint8_t data[5] = {0}; for (int i = 0; i < 40; i++) { data[i / 8] <<= 1; if (DHT11_ReadBit()) { data[i / 8] |= 1; } } if ((uint8_t)(data[0] + data[1] + data[2] + data[3]) != data[4]) { return 1; // 校验失败 } *humidity = data[0]; *temperature = data[2]; return 0; }

校验失败建议直接丢弃本次数据,等下一次采样周期,不要用上次的有效值硬凑。温室环境中传感器受水汽凝结影响可能出现偶发通信错误,连续失败 3 次才报警,避免单一干扰导致误报。

4.1.1 读取周期与 5V 电源稳定性

DHT11 两次读取间隔建议不小于 2 秒,频繁读取会让传感器内部发热,湿度读数偏高。如果传感器和继电器共用电源,继电器一旦吸合,电源电压跌落可能导致 DHT11 时序错乱,此时读取结果表现为校验和频繁失败。这属于典型“软件查不出、硬件一量就懂”的问题。另外 DHT11 数据线走线要避开继电器线圈和风机电源线,必要时候用屏蔽线或双绞线。

4.2 阈值控制与继电器联动逻辑

温室环境监测不只是显示数据,还要在温度过高时开启风机、湿度过低时启动水泵。最简单的实现是在 main 循环里做阈值判断,但更健壮的做法是引入状态机,避免阈值在临界点反复切换导致继电器频繁动作。

typedef enum { STATE_NORMAL, STATE_FAN_ON, STATE_PUMP_ON, STATE_ALARM } SystemState; void Control_Update(float temp, float humi) { static SystemState state = STATE_NORMAL; static uint32_t last_switch_time = 0; uint32_t now = HAL_GetTick(); switch (state) { case STATE_NORMAL: if (temp > TEMP_HIGH_THRESHOLD && (now - last_switch_time) > 5000) { HAL_GPIO_WritePin(FAN_GPIO_Port, FAN_Pin, GPIO_PIN_SET); state = STATE_FAN_ON; last_switch_time = now; } if (humi < HUMI_LOW_THRESHOLD && (now - last_switch_time) > 5000) { HAL_GPIO_WritePin(PUMP_GPIO_Port, PUMP_Pin, GPIO_PIN_SET); state = STATE_PUMP_ON; last_switch_time = now; } break; case STATE_FAN_ON: if (temp < TEMP_LOW_THRESHOLD) { HAL_GPIO_WritePin(FAN_GPIO_Port, FAN_Pin, GPIO_PIN_RESET); state = STATE_NORMAL; } break; // STATE_PUMP_ON 同理 default: break; } }

这里引入两个阈值:TEMP_HIGH_THRESHOLD 是启动温度,TEMP_LOW_THRESHOLD 是停止温度,中间留出回差(滞回区间)。比如高温 35℃ 启动风机、32℃ 停止,避免风机在 34.9℃ 和 35.1℃ 之间反复启停,继电器触点寿命会显著缩短。状态切换最小间隔 5 秒是通过 last_switch_time 实现的,同时起到保护压缩机和风机的作用。这个回差设计来自工业控制里的滞回比较器思想,用在单片机上一样有效。

4.3 OLED 显示框架与按键翻页

OLED 作为本地人机交互界面,至少需要三页:实时温湿度页、设备状态页、阈值配置页。工程里可以把显示逻辑独立成一个模块,主循环每 200ms 刷新一次。SSD1306 自身不带中文字库,如果显示中文需要取模软件生成字模,或者直接用英文字符加符号替代。实时页显示效果类似:

TEMP: 28.5 C HUMI: 62.0 % FAN : OFF PUMP: ON

按键 PA0 配置为外部中断下降沿触发,在中断回调里设置一个标志位,主循环检测到标志后切换当前页索引。注意 PA0 在 WAKEUP 引脚上有特殊功能,如果不小心把它配置成模拟输入,按键会失灵。显示刷新注意不要阻塞主循环,HAL_I2C_Mem_Write 在 400kHz 下写一帧全屏数据大约耗时 30ms,建议只更新变化的区域,或者让刷新周期保持 200ms 以上。

5. 源码使用与联调验证:从烧录到串口监控的完整走查

拿到一份“含完整源码及文档”的工程,第一步不是急着打开 main.c,而是先看工程根目录结构。常见做法的源码包会包含四个部分:Core 目录下是 main.c、stm32f1xx_it.c 等启动相关文件;Drivers 目录是 HAL 库源码;App 目录是开发者自己写的业务代码,比如 dht11.c、oled.c、control.c;Docs 目录放原理图 PDF 和调试笔记。先找到 App 目录里的 hal_conf.h 或者 board.h,确认引脚宏定义和自己实际的接线是否一致。

// board.h 关键配置示例 #define DHT11_GPIO_PORT GPIOB #define DHT11_GPIO_PIN GPIO_PIN_12 #define FAN_GPIO_PORT GPIOA #define FAN_GPIO_PIN GPIO_PIN_1 #define OLED_I2C_ADDR 0x3C #define DEBUG_UART_BAUDRATE 115200

这些宏定义是最常改的地方,也是“文档里写了但很多人不看”的部分。用 ST-Link 连接板子后,在 KEIL 里选择 STM32F103C8 目标,Flash Download 里勾选 Reset and Run,点下载按钮。如果下载失败,检查 Debug 设置里的 SW Device 是否识别到芯片 ID 为 0x1BA01477,识别不到先量 VDD 电压和 SWDIO 连线。

串口联调阶段,用一个 5V 供电的 CH340 模块,TXD 接板子 PA10,RXD 接 PA9,GND 接 GND,打开串口助手选择 115200-8-N-1。上电后应看到以下格式的日志输出:

[BOOT] System initialized, 72MHz [INFO] DHT11 first read success: 23.0 C, 54.0 % [INFO] OLED initialized [CTRL] Fan activated due to high temp [WARN] DHT11 checksum error, retry in 2s

如果日志里出现一条“DHT11 checksum error”,不用急着改代码,先用示波器确认数据线上的上拉电阻是否正常、杜邦线是否超过 20cm。DHT11 对线长敏感,20cm 以内容易稳定,超过 50cm 建议加一级缓冲或换 SHT30。

调试阶段有几个值得留意的现象:程序反复重启,检查看门狗是否被误开启,以及电源电压是否低于 2.9V;串口能收到数据但全部是 0xFF,检查 RXD/TXD 是否交叉接反;OLED 一直在闪,检查 I2C 通信时是否被定时器中断打断。I2C 总线的字节级传输不允许被打断,如果定时中断里恰好有耗时操作,OLED 会花屏或卡死。

6. 数据可靠性与长期运行的三个关键验证

系统上电跑通只是第一步,温室环境监测设备要在户外或大棚内长期工作,核心指标是可靠性和可维护性。一个简易的 5 分钟稳定性测试脚本,可以让串口助手定时记录 300 条数据,统计温湿度的最大漂移量和校验失败次数。正常情况下 DHT11 连续 300 次读取的校验失败率应当为 0,如果失败率超过 1%,优先检查数据线上拉到 3.3V 的电阻值和主控电源纹波。

I2C 总线可靠性可以用一个简单的重试机制增强。HAL_I2C_Master_Transmit 返回 HAL_ERROR 时,不直接放弃,而是重试三次。SSD1306 偶尔无响应时重新发送初始化序列,这种“软复位”策略在长跑测试中能显著降低花屏概率。硬件上可以在 OLED 电源引脚加一个 10μF 电容,防止刷屏瞬间电流尖峰拉低总线电压。

代码层面的健壮性还可以加一个软件看门狗。独立看门狗 IWDG 的溢出时间设为 2 秒,主循环每 500ms 喂狗一次。DHT11 读取如果卡死在忙等循环里,看门狗会把芯片拉回正轨,保证无人值守时不至于“静默死亡”。喂狗位置要放在整个主循环调度的末尾,确保所有外设操作都完成后才喂,否则起不到监控作用。配合 PWU 低功耗模式,设备在夜间无人时段可将电流压到微安级别,早晨定时醒来完成第一次采集。

给维修预留窗口也很实用:把最近 20 条传感器原始数据(不经过任何软件滤波)存在 Flash 的末尾扇区,掉电后仍然可查。遇到“客户说湿度突然跳高”的情况,通过串口导出 Flash 数据就能还原是传感器故障还是环境真实变化。这套做法让系统不只是“采集数据”,而是“可追溯的数据系统”,也是工程里最容易被忽略但最有价值的一层设计。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 20:15:05

充电站聚合电动汽车参与电力市场的两阶段投标策略及Matlab实现

简介&#xff1a;面向电动汽车可调度潜力与充电站两阶段市场投标策略研究&#xff0c;这套Matlab代码包专为计算机、电子信息工程、数学等专业学生及研究者设计&#xff0c;可用于课程设计、期末大作业或毕业设计中的仿真与算法验证。压缩包共含44个文件&#xff0c;主要包括20…

作者头像 李华
网站建设 2026/9/16 20:15:01

面向通用场景的微信机器人设计与实现

微信生态开发功能与场景实践总结一、常见功能模块1. 好友关系管理好友信息维护&#xff1a;支持添加、删除、更新好友信息标签分组系统&#xff1a;实现自定义标签创建/编辑/删除&#xff0c;优化联系人组织架构2. 消息管理多格式消息传输&#xff1a;支持文本、图片、文件、视…

作者头像 李华
网站建设 2026/9/16 20:14:31

PMSM无传感器FOC的模型预测控制与滑模自适应Simulink仿真

简介&#xff1a;PMSM模型预测控制&#xff08;MPCCMPTC&#xff09;结合自适应与滑模观测器的Simulink仿真包&#xff0c;面向电机控制领域本科、硕士及科研人员&#xff0c;用于算法对比、毕设验证与课题预研。包内共9个文件&#xff0c;包含4个slx仿真模型&#xff0c;分别对…

作者头像 李华