基于STM32与多传感器融合的水质监测系统设计与实现
2026/9/4 12:42:43 网站建设 项目流程

简介:本资源是一套完整的基于STM32F103的水质监测系统课程设计实现方案,面向电子信息、自动化及物联网方向的本科生与嵌入式初学者,解决多参数水质实时采集、显示与越限报警等典型嵌入式应用开发问题。压缩包共172个文件,933KB,涵盖Keil5工程核心文件(.uvprojx/.uvoptx)、HAL库驱动源码(.c/.h)、编译中间文件(.o/.d/.lst)、可执行镜像(.hex/.axf)及Proteus仿真电路(.pdsprj/.pdsbak),结构完整,支持软硬协同调试。已有9360人学习下载,资源提供可直接烧录运行的固件、传感器信号调理与ADC采样逻辑、LCD1602动态刷新驱动、三路参数阈值判断与LED报警控制等关键模块代码,且包含油度、PH、TDS传感器的实际接口适配与校准思路,便于课程实践、毕业设计或竞赛原型快速复现。

1. 项目概述:从零搭建一个多参数水质监测站

最近在做一个挺有意思的嵌入式项目,核心就是用一块STM32单片机,搭一个能同时测水质浑浊度、PH值和TDS(总溶解固体)的小型监测系统。这玩意儿听起来像是环境监测站里的大家伙,但其实用我们手头常见的开发板和传感器,完全可以在桌面上实现原型。项目的完整流程包括了硬件电路设计(我用Proteus做了仿真)、软件编程(Keil5 MDK环境),以及最后把程序烧录到实际的STM32板子上跑起来。对于想深入STM32实际应用,特别是对模拟信号采集、传感器驱动和系统集成感兴趣的朋友,这个项目能串起来很多关键知识点。它不只是点个灯、调个串口那么简单,而是涉及了从模拟世界到数字世界的完整信号链处理,非常锻炼人。

2. 系统整体设计与核心思路拆解

2.1 为什么选择STM32作为主控?

这个选择几乎是必然的。水质监测的几个关键传感器,比如PH电极和浊度传感器,输出都是模拟电压信号。STM32系列单片机集成了高精度的ADC(模数转换器),像STM32F103C8T6这种“国民级”芯片,就有12位ADC,分辨率达到1/4096,对于测量PH值(通常输出0-3.3V或0-5V对应0-14PH)和浊度(电压与浊度成比例)来说,精度完全够用。更重要的是,我们需要同时采集多路信号,STM32的ADC支持多通道扫描和DMA传输,可以在不占用CPU太多资源的情况下,高效、同步地获取三个传感器的数据,这对于保证监测的实时性至关重要。此外,后续可能还需要连接OLED显示屏实时显示、通过串口发送数据到电脑或者Wi-Fi/4G模块上传云端,STM32丰富的片上外设(多个USART、I2C、SPI)和足够的处理能力,为系统扩展留足了空间。

2.2 三大监测参数的技术原理与传感器选型

水质浑浊度(Turbidity):原理是光散射法。传感器内部有一个LED光源和一个光电接收器。当光线穿过水样时,水中的悬浮颗粒会使光线发生散射,接收器检测到的光强会减弱。传感器通常将这种变化转换为模拟电压输出,电压越高,代表浊度越低(水越清);电压越低,代表浊度越高(水越浑)。我们常用的是基于TSW-30或类似模块的浊度传感器,输出0-4.5V模拟信号。

PH值检测:核心是PH复合电极,其工作原理是电化学中的电位法。电极的玻璃膜对水溶液中的氢离子(H+)有选择性响应,会产生一个与氢离子浓度(即PH值)相关的电位差。这个微弱的电位差(毫伏级)经过一个高输入阻抗的PH放大器电路(或直接使用集成PH计模块)进行放大和电平转换,变成单片机ADC可以采集的0-3.3V或0-5V标准模拟电压。需要注意的是,PH测量需要温度补偿,因为电极电位受温度影响。简易方案可以加一个DS18B20数字温度传感器进行校正。

TDS(总溶解固体)检测:TDS传感器实际上测量的是水的电导率,然后通过一个经验公式估算出溶解固体总量。它通常有两个电极,通过测量电极间的导电能力来反映水中离子浓度。传感器模块内部一般集成了激励信号发生和信号调理电路,最终输出一个与TDS值成比例的模拟电压信号。市面上常见的TDS模块输出有0-2.3V(对应0-1000ppm)等多种规格。

注意:PH电极和TDS传感器都不能长时间干放。PH电极前端玻璃球泡需要浸泡在3mol/L的KCL保护液中保存。TDS传感器的电极片如果长期暴露在空气中氧化,会影响测量精度。在实际项目搭建和调试间隙,要注意传感器的保养。

2.3 开发环境:Proteus仿真与Keil5编程的结合策略

很多STM32初学者一开始就直接在实物板上调试,一旦程序跑飞或硬件连接有问题,排查起来非常头疼。这个项目采用“先仿真,后实战”的策略,能极大提高成功率。

Proteus用于硬件电路仿真。我们可以在电脑上绘制出包含STM32最小系统、三个传感器模拟接口电路(用可调电位器模拟传感器电压输出)、OLED显示模块、串口等外围电路的原理图。在仿真环境中,我们可以验证单片机引脚分配是否正确,ADC采集逻辑是否正常,甚至模拟传感器电压变化来观察系统响应,这一切都不需要实物焊板子,避免了早期硬件错误导致的芯片烧毁风险。

Keil5 MDK是ARM内核单片机的主流开发IDE。我们在这里编写C语言代码,配置STM32的时钟、GPIO、ADC(多通道扫描+DMA)、定时器、串口等外设,实现数据的采集、处理、显示和上传。Keil5强大的调试功能,可以单步执行、查看变量、观察寄存器,是软件逻辑调试的利器。

两者结合的精髓在于:在Keil5中编写和初步调试代码,生成.hex文件,然后导入到Proteus的STM32模型中进行联合仿真。在仿真中验证整个系统行为无误后,再将完全相同的.hex文件烧录到实物STM32开发板上,连接真实的传感器进行最终测试和校准。这套流程确保了软件硬件的高度一致性,降低了开发难度。

3. 硬件电路设计与Proteus仿真搭建

3.1 核心控制器与电源电路设计

主控芯片我选择了STM32F103C8T6,性价比高,资源足够。在Proteus中,找到“STM32F103C8”模型放置。最小系统必须包括:

  1. 复位电路:一个10kΩ上拉电阻到VCC,一个0.1uF电容到地,中间接RESET引脚。
  2. 时钟电路:两个22pF的电容和一个8MHz的晶振连接在OSC_IN和OSC_OUT引脚,这是主时钟源。如果用到RTC,可能还需要一个32.768kHz的晶振。
  3. 启动模式选择:BOOT0和BOOT1引脚通过跳线或电阻设置。通常调试时BOOT0接低电平(通过10k电阻下拉),从主Flash启动。
  4. 电源去耦:在每个VDD和VSS引脚之间,就近放置一个0.1uF的陶瓷电容,用于滤除高频噪声,这对ADC的稳定工作至关重要。模拟部分(VDDA、VSSA)的供电最好通过一个磁珠或0Ω电阻从数字电源隔离,并单独增加一个1uF或10uF的钽电容。

电源部分,输入可以是USB的5V或者外部适配器。使用AMS1117-3.3稳压芯片将5V降压为3.3V,为整个系统供电。记得在输入输出端加足够容量的电解电容(如100uF)和陶瓷电容(0.1uF)进行滤波。

3.2 传感器接口电路设计

在Proteus中,我们用可调电位器来模拟传感器的模拟电压输出,这是仿真的关键技巧。

  1. 浊度传感器接口:假设模块输出0-3.3V。将其信号线连接至STM32的一个ADC通道,例如PA0(ADC1_IN0)。在Proteus中,用一个电位器代替,一端接3.3V,一端接地,中间抽头接PA0。调节电位器即模拟不同浊度下的电压变化。
  2. PH传感器接口:PH放大器模块输出假设也是0-3.3V对应0-14PH。连接至另一个ADC通道,如PA1(ADC1_IN1)。同样用一个电位器模拟。
  3. TDS传感器接口:连接至第三个ADC通道,如PA2(ADC1_IN2),也用电位器模拟。
  4. 温度传感器(用于PH补偿):使用DS18B20,这是一线式数字传感器,只需一个GPIO引脚(如PB9)和一個4.7kΩ的上拉电阻即可。在Proteus中可以直接找到DS18B20模型。

所有模拟信号线在靠近STM32引脚处,最好串联一个几十欧姆的电阻(如22Ω),并并联一个几十pF的电容到地,组成简单的低通滤波器,抑制高频干扰。

3.3 人机交互与通信接口设计

  1. OLED显示模块:常用的0.96寸OLED屏多采用SSD1306驱动,通过I2C接口通信。在Proteus中,可以找到“OLED12864”模型。将STM32的PB6(SCL)和PB7(SDA)连接到OLED的对应引脚,并加上拉电阻(通常模块已集成)。
  2. 串口通信:用于调试和上传数据。将STM32的PA9(USART1_TX)和PA10(USART1_RX)连接到Proteus中的虚拟终端(Virtual Terminal)或COMPIM模块(用于连接电脑虚拟串口)。这样在仿真时就能看到打印的调试信息。
  3. 按键电路:可以设计两个按键,一个用于切换显示界面,一个用于校准或确认。接GPIO并配置为上拉输入模式,按键另一端接地。

在Proteus中绘制完完整原理图后,务必要使用电气规则检查(ERC)功能,排查短路、断线、未连接网络等错误。

4. 软件编程与Keil5工程配置详解

4.1 Keil5工程建立与基础外设配置

首先,使用STM32CubeMX工具来初始化工程是最佳实践,它能图形化配置引脚和时钟,生成Keil5工程框架,避免手动编写大量底层代码出错。

  1. 时钟树配置:选择外部高速时钟(HSE)为8MHz晶振,经过PLL倍频到72MHz作为系统时钟(SYSCLK)。这是STM32F103的常用高速配置。
  2. ADC配置
    • 启用ADC1。
    • 将通道0、1、2(对应PA0, PA1, PA2)设置为规则通道。
    • 扫描模式(Scan Conversion Mode)使能,这样ADC会自动按顺序转换多个通道。
    • 连续转换模式(Continuous Conversion Mode)使能,让ADC不停歇地工作。
    • 数据右对齐。
    • 采样时间(Sampling Time)设置为较长的周期,例如239.5个周期,以提高采样精度,尤其是对于信号源阻抗较高的PH传感器。
    • 最关键的一步:启用DMA。为ADC1的规则通道创建一个DMA请求,模式设为循环模式(Circular),数据宽度都为半字(16位)。这样ADC每转换完一组数据,就会通过DMA自动搬运到指定的内存数组中,完全无需CPU干预。
  3. GPIO配置:将PA0, PA1, PA2设置为模拟输入模式。配置I2C1(PB6, PB7)为复用开漏输出模式。配置USART1为异步模式,波特率115200。
  4. 生成代码:生成Keil5 MDK-ARM工程。CubeMX会生成完整的初始化代码HAL_Init()SystemClock_Config(), 以及各外设的MX_ADC1_Init()等函数。

4.2 多通道ADC与DMA数据采集实现

这是软件部分的核心。CubeMX生成的代码已经完成了ADC和DMA的硬件初始化。我们需要在用户代码中启动它们,并处理采集到的数据。

// 在main.c的全局变量区定义 uint16_t adc_buf[3]; // DMA搬运目标数组,依次存放通道0,1,2的结果 float voltage_buf[3]; // 转换后的电压值数组 float ph_value, turbidity_value, tds_value; // 最终计算出的参数值 // 在main函数初始化部分,启动ADC和DMA HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buf, 3); // 然后在一个循环(如while(1))或定时器中断中,定期处理adc_buf中的数据 void Process_Sensor_Data(void) { for(int i=0; i<3; i++) { // 将ADC原始值转换为电压值 (假设参考电压Vref=3.3V) voltage_buf[i] = (float)adc_buf[i] * 3.3f / 4095.0f; // 可以根据需要进行软件滤波,比如简单移动平均 // filtered_voltage[i] = moving_average_filter(voltage_buf[i]); } // 将电压值转换为具体的物理量 turbidity_value = convert_to_turbidity(voltage_buf[0]); // 浊度转换 ph_value = convert_to_ph(voltage_buf[1]); // PH转换,可能需要温度补偿 tds_value = convert_to_tds(voltage_buf[2]); // TDS转换 }

convert_to_turbidity,convert_to_ph,convert_to_tds这三个函数是标定函数,需要根据具体传感器的数据手册或通过实验标定得出公式。例如,浊度可能是线性关系:NTU = k * V + b,其中k和b通过测量标准浊度液获得。

4.3 传感器数据标定与滤波算法

标定过程(以PH为例)

  1. 准备两种标准缓冲溶液,例如PH4.01和PH6.86(或PH9.18)。
  2. 将PH电极洗净后插入PH4.01溶液中,待读数稳定后,记录此时ADC转换后的电压值V1。
  3. 同样,测量PH6.86溶液,记录电压值V2。
  4. 理论上PH值与电压成线性关系(能斯特方程简化)。设PH = a * V + b,代入两组值得出斜率a和截距b。
  5. 将a, b写入程序的convert_to_ph函数。对于更精确的测量,可以使用三点标定。

软件滤波: 传感器信号难免有噪声。除了硬件RC滤波,软件上可以采用:

  • 算术平均滤波:连续采样N次取平均值。简单有效,但速度慢。
  • 滑动平均滤波:维护一个长度为N的队列,每次新数据入队,旧数据出队,计算队列平均值。实时性好。
  • 中值滤波:连续采样N次(N为奇数),排序后取中值。对脉冲干扰有很好的抑制效果。

通常可以先中值滤波去脉冲,再滑动平均平滑。在Process_Sensor_Data函数中调用滤波函数处理voltage_buf

4.4 OLED显示与数据输出驱动

使用成熟的OLED驱动库(如ssd1306u8g2的嵌入式版本)可以简化开发。在Keil工程中引入对应的.c.h文件。

// 初始化 ssd1306_Init(); // 在显示任务中 void Display_Update(void) { char str_buf[20]; ssd1306_Fill(Black); // 清屏 ssd1306_SetCursor(0, 0); sprintf(str_buf, "PH: %.2f", ph_value); ssd1306_WriteString(str_buf, Font_7x10, White); ssd1306_SetCursor(0, 20); sprintf(str_buf, "Turb: %.1f NTU", turbidity_value); ssd1306_WriteString(str_buf, Font_7x10, White); ssd1306_SetCursor(0, 40); sprintf(str_buf, "TDS: %.0f ppm", tds_value); ssd1306_WriteString(str_buf, Font_7x10, White); ssd1306_UpdateScreen(); // 刷新显示 }

同时,可以通过串口将数据打印出来,方便在电脑端用串口助手查看或记录。

printf("PH:%.2f, Turb:%.1f, TDS:%.0f\r\n", ph_value, turbidity_value, tds_value);

需要确保printf重定向到了串口。

5. Proteus与Keil5联合仿真调试全流程

5.1 仿真模型准备与工程链接

  1. 在Proteus中完成原理图绘制后,双击原理图中的STM32芯片,在“Program File”一栏,需要加载由Keil5编译生成的.hex文件。
  2. 在Keil5中,确保编译配置正确。在“Options for Target” -> “Output”中,勾选“Create HEX File”。在“Debug”选项卡中,选择“Use Simulator”,这样可以在不连接实物的情况下进行部分软件逻辑调试。
  3. 首先在Keil5中进行纯软件调试,比如测试数据结构、算法逻辑。然后编译生成.hex文件。
  4. 将生成的.hex文件路径指定给Proteus中的STM32模型。

5.2 仿真运行与交互测试

点击Proteus的运行按钮,系统开始仿真。此时你可以:

  • 调节模拟传感器:用鼠标拖动三个电位器的滑臂,改变输入给STM32 ADC引脚的电压。观察OLED显示屏(Proteus中的OLED模型会动态显示内容)上的数值是否随之变化,变化趋势是否正确(例如,PA0电压升高,浊度显示值应降低)。
  • 观察虚拟终端:打开虚拟终端窗口,可以看到STM32串口打印出来的数据格式是否正确,数值是否与电位器设置和显示值匹配。
  • 模拟异常情况:例如将某个电位器电压调到超过3.3V(模拟传感器故障),观察程序是否有相应的处理(比如数据超限提示)。也可以模拟DS18B20读取失败的情况。
  • 使用探针和图表:Proteus的电压探针可以测量任何点的实时电压。模拟图表(Analog Graph)功能可以绘制信号波形,比如可以观察ADC采样点的电压波形。

通过仿真,我们可以在近乎真实的环境中验证硬件连接的正确性、软件驱动是否正常、业务逻辑是否符合预期。将大部分潜在问题在仿真阶段解决,能节省大量的实物调试时间和物料成本。

5.3 从仿真到实物的关键切换

仿真通过后,就可以着手实物制作了。这一步有几个关键点:

  1. 引脚一致性:确保实物焊接的电路与Proteus原理图的引脚连接完全一致。特别是ADC通道、I2C引脚、串口引脚。
  2. 电源与接地:实物中电源噪声更大。务必确保3.3V稳压输出干净,模拟地和数字地单点连接,传感器信号线使用屏蔽线或双绞线以减少干扰。
  3. 下载程序:使用ST-Link或J-Link等调试器,将Keil5生成的同一个.hex文件(或.axf/.elf文件)下载到实物STM32芯片中。
  4. 传感器连接:连接真实的浊度、PH、TDS传感器模块。注意模块的供电电压(通常是5V或3.3V)和信号电平是否与STM32匹配,必要时需要电平转换。

6. 系统校准、优化与常见问题排查

6.1 传感器现场校准与温度补偿

仿真中的电位器是理想电压源,但真实传感器有其特性。

  • 浊度传感器:可能需要零点校准。在纯净水中(浊度理论为0),传感器输出可能不为0,记录此时电压值V_clear,在计算浊度时减去这个偏移量:NTU = k * (V_sample - V_clear)
  • PH传感器
    • 温度补偿:PH电极的斜率与温度有关。公式E = E0 - (2.303 * R * T) / (n * F) * pH中,T是绝对温度。因此,需要实时温度进行补偿。在convert_to_ph函数中,引入温度参数temp(来自DS18B20):pH_corrected = pH_raw / (1.0 + 0.03 * (temp - 25.0)),这是一个简化公式,更精确需查表或使用完整能斯特方程。
    • 定期标定:PH电极会老化,建议每周或每月用标准缓冲液重新标定一次。
  • TDS传感器:同样需要校准。可以配置已知浓度的标准溶液(如342ppm的NaCl溶液)进行单点或两点校准。

6.2 提高测量稳定性的软硬件技巧

  • 硬件上
    • 为ADC提供独立基准源:如果对精度要求高,不要使用芯片内部的VREF,可以外接一个高精度、低温漂的基准电压芯片,如REF3033(3.3V)。
    • 信号隔离:在传感器信号进入ADC引脚前,使用电压跟随器(运放构成)进行隔离,可以避免MCU内部电路对高阻抗传感器信号造成负载效应。
    • 电源隔离:模拟部分(传感器、运放、ADC基准)的供电使用独立的LDO,并与数字部分电源通过磁珠或0Ω电阻连接。
  • 软件上
    • 过采样与求平均:在ADC硬件或软件上,进行多次采样(如16次、64次)然后取平均,可以有效提高分辨率,抑制白噪声。
    • 数字滤波组合拳:如前所述,采用“中值滤波+滑动平均滤波”的组合策略。
    • 数据有效性判断:加入判断逻辑,如果连续多次采样值超出合理范围(如PH值小于0或大于14),则视为传感器故障或接触不良,给出错误提示,而不是显示一个荒谬的数值。

6.3 常见问题与故障排查实录

下表列出了一些在开发和调试过程中可能遇到的典型问题及解决思路:

现象可能原因排查步骤与解决方案
ADC采样值跳动大,不稳定1. 电源噪声大。
2. 模拟信号线受干扰。
3. ADC采样时间太短。
4. 参考电压不稳。
1. 用示波器检查3.3V和GND上的纹波,增加滤波电容。
2. 缩短传感器到MCU的走线,使用屏蔽线,信号线远离数字高频线。
3. 在CubeMX中增加ADC的采样周期(Sample Time)。
4. 检查VDDA和VSSA的供电质量,尝试外接基准源。
PH值读数漂移严重1. PH电极未充分浸泡或老化。
2. 温度变化未补偿。
3. 放大器电路输入阻抗不够高。
1. 将电极浸泡在保护液中至少24小时活化。如老化则更换。
2. 确认DS18B20工作正常,并在PH计算函数中加入温度补偿项。
3. 检查PH放大器模块的输入阻抗,应大于10^12Ω。
OLED显示屏不亮或乱码1. I2C引脚接错或上拉电阻未接。
2. 供电不足。
3. 初始化时序或命令错误。
4. 屏幕本身损坏。
1. 核对SCL/SDA连接,确认有4.7k-10k上拉电阻到3.3V。
2. 测量OLED模块VCC脚电压是否为3.3V/5V(视模块而定)。
3. 用逻辑分析仪抓取I2C波形,对比SSD1306初始化序列。
4. 更换OLED模块测试。
串口打印无数据1. 串口引脚TX/RX接反。
2. 波特率设置不一致。
3. 串口助手未正确打开端口或设置。
4.printf未重定向。
1. 检查MCU的TX是否接USB转串口模块的RX,RX接TX。
2. 确认代码中USART初始化波特率与串口助手设置完全一致。
3. 尝试不同的串口助手软件或重启。
4. 检查是否在代码中重写了_writefputc函数,并链接了MicroLIB库。
DMA搬运的ADC数据不更新1. DMA未正确启动或配置。
2. ADC未在连续转换模式。
3. 内存数组地址或长度错误。
1. 确认HAL_ADC_Start_DMA()被成功调用,且DMA配置为循环模式。
2. 在CubeMX中检查ADC是否使能了“Continuous Conversion Mode”。
3. 检查adc_buf数组地址是否作为参数正确传入,长度是否为通道数。
Proteus仿真能跑,实物不工作1. 实物供电问题(电压不足或电流不够)。
2. 复位电路或晶振电路不正常。
3. BOOT模式设置错误。
4. 程序下载失败或未运行。
1. 用万用表测量MCU的VDD电压是否为稳定的3.3V。
2. 用示波器检查复位引脚电平及晶振是否起振。
3. 确认BOOT0为低电平(接GND)。
4. 检查下载器连接,尝试按一下复位键,或重新下载程序。

一个关键的实操心得:在调试ADC时,如果怀疑是硬件问题,一个非常有效的方法是“软件模拟对比法”。在代码里暂时屏蔽真实的ADC采样,改为用一个固定的、可编程的数值(比如用adc_buf[0] = 2048;模拟中间电压)赋值给adc_buf数组。如果此时显示和计算都正常了,那问题肯定出在ADC硬件电路或配置上;如果问题依旧,那就要重点排查软件计算和显示部分的代码。这种隔离排查法能快速定位问题边界。

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

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

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

立即咨询