ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

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

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 8N1USART1_RXPA10预留上位机指令输入DHT11 数据PB12开漏输出 外部 4.7kΩ 上拉OLED I2C_SCLPB6I2C1 时钟注意总线速度设为 400kHzOLED 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芯片型号选 STM32F103C8TxRCC 设置为 HSE 外部晶振模式。时钟树配置里把 PLL Source 选为 HSEPLLMUL 设为 x9在 F103 上 HSE 8MHz 乘 9 得 72MHz正好是主频上限。APB1 总线时钟最多 36MHzAPB2 是 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 地址通常为 0x787 位地址 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 的高电平表示 070μ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.cDocs 目录放原理图 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 接板子 PA10RXD 接 PA9GND 接 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 数据就能还原是传感器故障还是环境真实变化。这套做法让系统不只是“采集数据”而是“可追溯的数据系统”也是工程里最容易被忽略但最有价值的一层设计。本文还有配套的精品资源点击获取
返回列表