尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32水质检测开源项目:pH/TDS/温度监测系统设计与仿真

STM32水质检测开源项目:pH/TDS/温度监测系统设计与仿真 这项目是我整理的STM32水质检测开源资料包含完整代码、原理图和Proteus仿真文件尤其适合刚学完单片机基础、想做点有闭环感小项目的朋友。常见的水质检测DIY方案大多只给个TDS模块的串口打印这套不太一样它把pH值、TDS溶解性固体、水温三个指标做进了同一块板子数据走OLED显示同时通过串口往电脑端传还带超限报警。你既能拿来当学习模板逐行读代码也能直接打样做一套放在鱼缸或小型养殖场景里用。我要先说明一点这套系统属于“传感器信号采集数据处理人机交互”的典型单片机综合应用难度中等偏上但拆开看每个模块都不复杂。只要你有STM32裸机开发基础能跑通点灯和串口打印跟上这篇文章完全没问题。下面我按从需求到硬件的逻辑把整个系统的架构、原理图设计、代码组织、仿真验证和实物调试全部过一遍。1. 这套检测系统在做什么从应用场景倒推硬件选型1.1 系统的构成与工作流程先看整体框架。这套水质检测系统的核心采集对象是三个物理量pH酸碱度、TDS溶解性总固体、水温。其中TDS可以粗略理解成水中可溶性杂质的总量单位是ppm或mg/L数值越高说明水里溶解的矿物质、盐分越多。整套系统的数据流是这样的三路传感器信号进入STM32F103系列主控芯片芯片内部ADC完成模拟量转换经过滤波和换算后得到真实物理量然后同时做三件事刷新OLED屏幕上的实时数据、通过串口发送给上位机、判断数值是否超过设定的报警阈值并驱动蜂鸣器。供电部分用USB 5V输入板载AMS1117稳压到3.3V给主控传感器部分看模块要求决定走5V还是3.3V。这个流程看起来常规但真正决定项目质量的是后面几个细节模拟前端怎么设计、换算公式怎么标定、滤波怎么处理。我建议你不要把目标只定在“跑起来”而是想清楚每个指标背后的物理意义这样移植到其他场景也只是换传感器的问题。1.2 传感器选型对比为什么选这三类先看pH传感器。市面上常见有两种方案一种是不带变送模块的复合电极输出的是毫伏级电压信号需要自己搭高输入阻抗的放大电路另一种是集成变送模块直接把pH值换算成0~3.3V或0~5V的模拟电压单片机直接采样就行。这套开源项目用的是模块化方案原因很简单给初学者的项目如果卡在模拟前端调试上很容易劝退。模块输出的电压和pH值近似线性对应代码里做一次线性映射就能显示。TDS传感器基本就是电导率测量的变种。典型方案是给两个探针施加交流激励信号通过检测溶液中的电流大小推算电导率再换算成TDS。这里有个很关键的点绝对不能加直流直流会导致探针极化测量值会一直漂。开源的TDS模块已经把交流激励和I/V转换电路做好了输出也是模拟电压对单片机来说又是一个ADC通道的事。温度传感器我推荐DS18B20而不是NTC热敏电阻。DS18B20是数字输出走单总线协议不需要ADC通道而且它在这里还有个额外作用TDS和pH都受温度影响用同一个MCU读到的温度值去做软件补偿比模块自带的简陋补偿要方便。至于为什么不选NTC因为需要自己搭电桥、标定B值、查表精度还容易受自热影响徒增工作量。1.3 主控选型STM32F103C8T6够用吗主控选择的是STM32F103C8T6这颗芯片几乎成了开源项目的默认配置72MHz主频64KB Flash20KB RAM3个USART2个ADC各有10个通道绰绰有余。我们需要的资源如下ADC至少2路pH信号和TDS信号如果用带变送模块的传感器就只需要2路模拟输入。定时器做ADC采样触发、DS18B20时序延时至少1个通用定时器。USART1路用于调试和上位机通讯。I2C或SPI驱动OLED屏I2C方式只占用2个引脚。GPIO控制蜂鸣器、读取按键。F103C8T6的成本低、资料多Keil工程模板网上遍地都是用这块芯片最大的好处是遇到问题你能搜到无数解决方案。真没必要上F407或者G030之类的性能过剩对学习不是好事代码复杂度和调试点都会变多。2. 原理图拆解传感器接口、电源树与ADC采样链路上的关键细节2.1 电源分配3.3V/5V轨与参考电压稳定性原理图里最容易翻车的不是信号链路而是电源。这套系统我设计的是USB供电经过一个自恢复保险丝之后分为两路一路直接给AMS1117-3.3稳压给主控、OLED、DS18B20供电另一路5V给pH变送模块和TDS模块供电。这里插一个重要经验ADC采样参考电压不稳采集值就会飘。很多初学者把VDDA和VDD直接连在一起板子上没有做任何滤波结果发现采样数据在几个LSB之间乱跳。正确做法是VDDA通过一个磁珠或10Ω电阻接到3.3V然后在VDDA引脚对地并联一个1uF陶瓷电容加一个0.1uF高频退耦电容。这套原理图里我特意加了这两个电容布局的时候要尽量靠近MCU的VDDA引脚不要走太远的过孔。另外就是地线处理。pH和TDS模块的模拟地不要单独铺一块“干净地”然后再单点连接在两层小板子上这样搞往往适得其反直接共地反而噪声更小。只要模块输出信号线和GND双绞走线远离PWM驱动的蜂鸣器走线区域就可以了。2.2 pH前端为什么不能把传感器直接怼到MCU引脚上就算你买的是带变送模块的pH传感器我还是建议你在原理图里预留一个电压跟随级。pH玻璃电极的内阻极高动辄几十兆欧到几百兆欧如果后级电路的输入阻抗不够高信号会被分压吃掉测出来的pH值大概率是错的。具体做法是用一个轨到轨输入的运算放大器比如LMV321或MCP6001接成电压跟随器输入接pH模块的电压输出输出接MCU的ADC引脚。运放在这里起到两个作用缓冲阻抗和隔离。如果用的是不带变送模块的裸电极那这个运放前面还要加一级偏置电路把正负几百毫伏的电极电压抬升到0~3.3V范围内常用的加法电路就行。我见过太多新手第一次用pH裸电极直接上ADC采样结果不是零就是满量程就是这个原因。代码里对pH模块输出电压的换算公式也很直白pH (Vout_offset - Vref) / slope这里的Vref在25度时约等于1.23V对应pH7中性点的模块输出电压slope约为每pH单位59mV乘以电路增益。不同模块的基准点不一样强烈建议拿到模块后先测一下pH7标定液的实际输出电压再把系数写进程序。2.3 TDS探头交流激励和I/V转换是怎么回事如果你买的是成品TDS模块原理图里基本不用额外处理输出电压范围在0~2.3V左右直接进MCU的ADC就行。但我还是要说说模块内部做了什么不然测出来的数据出问题你根本没法排查。TDS模块内部有一个方波发生器产生大约1kHz的交变信号加到两个不锈钢探头上。溶液相当于一个电阻阻值范围从几百欧兆几兆欧不等。通过该电阻的电流经过运算放大器做I/V转换输出一个和电导率成比例的交流电压再通过整流滤波变成直流电平给MCU。这个方案的核心就是利用交流信号防止电解极化。在这套系统里TDS模块输出的模拟电压接入到STM32的一个ADC通道。测量范围一般能到0~1000ppm甚至更宽具体看探头常数。代码中换算用的是电导率到TDS的经验公式我后面会详细给出。2.4 引脚分配与外围电路速查表我把这套系统用到的引脚分配整理成一个表方便你画原理图时对照也方便后面读代码时快速对应。功能模块MCU引脚接口类型备注pH变送模块PA0ADC1_IN0电压跟随器输出接入TDS模块PA1ADC1_IN1模块GND与MCU共地DS18B20PA2单总线4.7k上拉电阻到3.3VOLED I2CPB6/ PB7I2C1地址常见0x3C或0x3D蜂鸣器PB8推挽输出三极管驱动低电平有效或高电平有效以实物为准按键S1/S2PB9/PB10上拉输入用于页面切换和阈值调整USART1PA9/PA10TTL串口连接到USB转串口模块蜂鸣器电路要注意不要直接拿MCU引脚驱动蜂鸣器引脚带不动反电动势还会打坏芯片。用一颗NPN三极管或一颗NMOS管做开关蜂鸣器并联一个续流二极管方向要接反二极管阴极接电源正阳极接集电极这个电路几乎每个项目都要用我建议你直接背下来。3. 固件侧核心逻辑从原始电压到水质指标的计算与处理3.1 工程结构与模块划分代码工程我按功能拆成了几个文件不搞all-in-one的main.c大杂烩main.c初始化外围设备、调度主循环。adc.cADC初始化、多通道采样、平均值滤波。ds18b20.c单总线时序、温度读取。oled.cSSD1306驱动、字符显示。sensor.cpH和TDS的电压到物理量换算、温度补偿。uart.c串口格式化输出。alarm.c阈值判断和蜂鸣器控制。这种一个功能一个文件的习惯一定要从这种小项目就开始培养。后面你做大一点的项目比如加Wi-Fi模块或者SD卡存储每个文件依然只有单一的职责维护起来不费劲。主循环我建议写成无阻塞轮询结构不要在延时函数里浪费CPU。比如OLED刷新可以放到1Hz的频率串口打印放到2Hz而ADC采样用定时器触发采样周期1ms。这样整个系统后续再加按键扫描、菜单逻辑才不会卡顿。3.2 ADC多通道采样与滑动平均滤波怎么去掉毛刺ADC这里的代码逻辑比很多人想象中要绕一点。F103的ADC有两个每个有多路通道但如果要同时采pH和TDS两个通道一般就是在同一次转换中做规则序列扫描或者用两次转换分别读取。我用的方案是定时器触发ADC以1kHz的速率连续采样每次转换完成在中断里把两个通道的数据放进各自的环形缓冲区然后主循环里做100次平均。这么做的原因是水质传感器的输出不是瞬时值它有响应时间而且水体本身在不停流动单次采样会叠加很多随机噪声。滤波不要用很激进的算法比如中值滤波窗口长度不好定或者卡尔曼滤波物理模型不好建。滑动平均在这个场景下最简单可靠队列长度设为16或32每次采完新值踢掉最旧的值算平均值。实测下来原始ADC跳动能到±10个LSB的信号滑动平均之后能压在±2个LSB以内。注意滑动平均的窗口长度不要设太大否则对真实的水质变化响应会滞后得很明显鱼缸里加了净水剂都得等半天才看到数值变化。下面是核心代码片段使用HAL库风格#define ADC_BUF_SIZE 16 uint16_t ph_buf[ADC_BUF_SIZE]; uint16_t tds_buf[ADC_BUF_SIZE]; uint8_t buf_index 0; void ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) { if (hadc-Instance ADC1) { ph_buf[buf_index] adc_val[0]; tds_buf[buf_index] adc_val[1]; buf_index (buf_index 1) % ADC_BUF_SIZE; } } uint16_t getAverage(uint16_t *buf) { uint32_t sum 0; for (int i 0; i ADC_BUF_SIZE; i) { sum buf[i]; } return (uint16_t)(sum / ADC_BUF_SIZE); }3.3 物理量换算公式pH、TDS和温度补偿到底怎么算ADC原始值变成电压很简单12位ADC参考电压3.3Vvoltage adc_value / 4096.0 * 3.3。接下来才是关键。pH换算假设pH变送模块在25℃pH7时输出电压V_ref而每变化1个pH单位电压变化V_step约59.16mV/0.25≈0.25V取决于模块增益。代码里用线性关系计算float adcToPH(float voltage_mv) { // V_ref是中性点输出电压V_step是每pH对应的毫伏数 // 具体数值需根据模块实测标定 float ph 7.0f (voltage_mv - PH_V_REF) / PH_V_STEP; return ph; }这个公式里的PH_V_REF和PH_V_STEP一定要自己标定不同模块差异很大。一个土办法是买pH4.00、6.86、9.18三种标准缓冲液测出三点电压后做线性拟合。不加这一步你测出来的pH显示值可能整体偏移0.5以上测矿泉水显示pH5这种明显不合理的结果都是标定系数不对导致的。TDS换算TDS模块输出的电压V_tds单位V换算成TDS值的常用经验公式来自开源社区float adcToTDS(float voltage) { // 0~2.3V对应0~1000ppm近似线性指数补偿更准 float tds (133.42f * voltage * voltage * voltage - 255.86f * voltage * voltage 857.39f * voltage) * 0.5f; return tds; }这个三次多项式本质上是对电导率曲线的拟合而且最后乘以0.5是把电导率换算成TDS的系数。如果你用的模块官方给了另一个多项式以官方为准。要注意这个公式的适用范围一般在0~1000ppm超出范围时最好稀释测量。温度补偿水温对电导率的影响大约是每度2%pH的电极斜率也会随温度变化。代码里做了一个简化补偿以25℃为基准float tempCompensate(float value, float temp, float coeff) { return value / (1.0f coeff * (temp - 25.0f)); }coeff对TDS一般取0.02对pH的电极斜率取0.003。这个做法的意思是以25℃时的测量值为基准温度高于25度时电导率升高就往回调小一点。对学习项目来说这个精度已经够了。3.4 显示与串口输出逻辑让人一眼看懂数据的排版OLED显示用的是SSD1306驱动芯片的0.96寸屏I2C接口。这类屏幕驱动代码网上很多核心就是初始化序列 显存操作 刷新。我建议用带显存缓冲的驱动方式程序在内存里维护一个128x8字节的缓冲区所有绘制操作先写进缓冲区最后一次I2C批量发送到屏幕。这种方式虽然占用1KB RAM但避免了频繁调用I2C导致的显示闪烁。显示页面我做了两页第一页显示pH和温度第二页显示TDS和系统状态。用按键S1切换页面S2长按进入阈值设置模式。这样屏幕信息不拥挤也顺带演示了菜单状态机的写法。串口协议我用最简单的文本格式PH: 7.32, TDS: 245ppm, TEMP: 25.6C每500ms发送一行直接用printf重定向到USART1波特率115200。如果你接了ESP8266模块后续改成JSON格式发到云平台就是物联网水质监测了。4. Proteus仿真驱动整条链路搭建、加载固件与验证水位4.1 仿真电路搭建要点STMCube、Proteus和固件的连接方式Proteus仿真文件里核心器件是STM32F103C8芯片右侧放一个电位器或者电压源模拟pH模块的输出再放一个电压源模拟TDS模块的输出DS18B20用Proteus自带的模型OLED用I2C虚拟显示器代替。搭建时最容易踩的坑是Proteus里的STM32模型不认编译出来的.hex文件需要先加载到芯片模型里才能跑。双击芯片模型在Program File一栏选择Keil编译生成的hex文件。如果文件选错或者芯片型号不对仿真会一直停在0x08000000处不动看起来就像卡死了实际上芯片根本没跑起来。仿真环境里没有真实的传感器所以pH和TDS信号是用电压源模拟的。你可以把pH信号源的电压设成1.23V看看显示是不是7.00设成2.00V看看是不是pH值向下偏移用来验证代码的线性映射逻辑是否正确。TDS信号源设成0V、1V、2V分别验证数值范围这样你就等于把固件里的信号链路单独抽出来测了一遍。4.2 模拟传感器信号与整机验证流程我给一个可以照着做的仿真验证流程这样你加载完固件不会迷茫知道每一步该看到什么静态验证pH信号源输出1.23VTDS信号源输出0.5VDS18B20设成25℃。OLED第一页应显示pH≈7.0第二页TDS≈90ppm左右具体以公式为准。串口虚拟终端能收到数据帧。动态验证缓慢调节pH信号源电压每调0.1V观察显示值是否按比例变化。注意响应会有几百毫秒延迟这不是Bug是滑动平均滤波导致的说明滤波生效了。报警验证把TDS信号源电压调到2.2V以上对应超过1000ppm的阈值蜂鸣器应该响OLED上可以加一个超限提示。仿真和实物的差异你心里要有数Proteus里的电压源是理想信号没有噪声所以ADC读取值非常稳定。实物上会有工频干扰、电源纹波、传感器极化等问题所以仿真通过不算完实物还要再调一遍滤波参数。4.3 仿真结果怎么解读从虚拟终端和OLED反推代码问题我在调试过程中总结了一个“先看值再看量最后看变化”的思路。先说先看值虚拟终端或者OLED显示的数值如果差得离谱检查代码里的参考电压是不是3.3V如果参考电压设成5V而实际芯片供电是3.3V所有采样读数都会偏高50%这类错误非常高发。再看量如果显示的数值在合理范围内但小数点后面一直跳检查滤波队列长度是不是太短。如果只有1~2个LSB的跳动属于正常的采样噪声不要再加滤波了否则响应会越来越慢。最后看变化手动改变电压源看显示值是否有响应。如果完全不动查ADC通道配置和DMA有没有开着曾经就有人查了半天发现DMA的buffer地址写到了只读区数据一直没更新。5. 实物调试与避坑采集噪声、标定步骤和那些文档没写的事5.1 采样噪声排查为什么实物数据比仿真跳得厉害从仿真转到实物第一反应通常都是“怎么噪声这么大”。我的排查顺序是先电源后信号再布局。电源方面的坑我前面说过了VDDA没加滤波电容是最常见的。第二个常见问题是用USB供电时电脑的USB口噪声比较大可以换一个质量稍好的5V适配器试试。如果板子上有其他数字器件比如按键或者通信芯片的翻转噪声耦合到了模拟电源可以在模拟电源入口加一个LC滤波器效果立竿见影。信号线上的问题主要是共模噪声。pH和TDS模块的输出信号线如果和蜂鸣器控制线、按键线扎在一起走蜂鸣器一旦动作ADC采样值就会突跳。解决办法是信号线远离数字线或者加一个100Ω电阻串联到ADC引脚做低通滤波配合引脚对地的100nF电容RC截止频率大概在16kHz左右能滤掉大部分高频噪声。5.2 传感器的标定与探头保养决定数据可信度的隐藏环节传感器标定这步强烈建议别省。pH探头买回来第一次用前要在3M KCl溶液里浸泡几个小时让电极活化。标定步骤是把探头放入pH6.86的标准缓冲液等示数稳定记录ADC电压值。把探头放入pH4.00的标准缓冲液稳定后记录第二个电压值。用这两点计算斜率和零点填到代码的PH_V_STEP和PH_V_REF。TDS探头标定相对简单配置一盆标准TDS溶液通常是氯化钠溶液或者在已知TDS的水里校正系数。我的经验是TDS模块的绝对精度本来就一般拿来做趋势监测非常合适但如果你要求它测出来的ppm值和专业水质检测笔完全一致比较难误差10%以内已经算不错了。探头保养直接影响寿命比标定还重要。pH探头不能干放不用的时候要泡在保护液里。TDS探头上的电极用久了会结垢要用软毛刷轻轻刷掉水垢不要用硬物刮也不要泡在洗涤剂里。这些细节如果忽略探头的寿命可能从一年缩短到一个月。5.3 从仿真到量产的预期管理这套系统的精度和定位最后给你交个底这套系统的精度定位是“监测级”而不是“计量级”。什么叫监测级就是你能看到水质的变化趋势能用来判断鱼缸该不该换水、净水器滤芯是不是失效了、配置营养液时浓度对不对。但你不能指望它达到实验室分析仪的精度毕竟工业pH计一台要几千块探头就几百块。我实际测下来pH显示值和手持pH计之间的误差在0.2个pH单位以内是可能的前提是标定得好。TDS的误差稍大尤其是超过500ppm以后三次多项式会明显偏离真实电导率。所以这套系统更适合做相对变化的监测一旦数值发生明显偏移你再去用专业仪器确认这个定位比较合理。另外提醒一下长期运行时的维护周期TDS传感器建议一个月清洗一次pH探头每月重新标定一次如果你发现读数开始系统性偏移先不要怀疑电路坏了大概率是探头需要清洗或标定了。这套系统的电路结构比较简单故障率最高的元器件其实是蜂鸣器前面的三极管和OLED屏的排线备几个原件备着就行。实际做完这整套流程我对“开源项目”的理解又深了一层开源的本质不是给你一个答案而是把整个思考链路摊开给你看。源码、原理图、仿真文件都是静态的真正有价值的是那些配平电路时的考量、选型取舍的权衡、以及从仿真走向实物时碰到的种种意外。把这篇项目的每一个环节都走通之后你再去看其他STM32传感器套件会发现底层逻辑都是相通的——无非是信噪比、标定和交互这三件事。这也是我当初把这个项目整理成完整开源包分享出来的原因希望这篇文章能帮你在复现的过程中少走几步弯路。
返回列表