资讯动态

STM32水质检测系统开源:从硬件设计到Proteus仿真全攻略

发布时间:2026/9/18 1:51:03 来源:尧图企业网站定制
嵌入式圈子里的朋友应该对这类项目不陌生STM32做控制器配合几个传感器把水温、pH值、浊度这些参数实时测出来显示在屏幕上超限就报警。这套水质检测系统我把源码、原理图、Proteus仿真工程全部开源了今天这篇就直接把整个项目的来龙去脉、硬件设计思路、代码里每个模块怎么写、仿真怎么搭以及我踩过的坑全部分享出来。不管你是要做课程设计、毕业设计还是单纯想学一下STM32的ADC采集、传感器驱动、OLED显示这些常用外设的配合使用这个项目都能作为不错的参考。项目用到的核心芯片是STM32F103C8T6属于性价比很高、资料非常多的一款芯片网上随便一搜就是海量教程非常合适入门。1. 系统整体设计与方案选型1.1 为什么选择STM32F103C8T6STM32F103系列在嵌入式开发里的地位不用多说C8T6这颗芯片更是经典中的经典。72MHz的主频64KB Flash20KB SRAM对于水质检测这种量级的数据处理完全绰绰有余。关键是这颗芯片内置了3个12位精度的ADC可以同时采集多路模拟信号——水质检测正好需要采集pH传感器、浊度传感器这些模拟量输出单芯片搞定所有采集需求不用外扩ADC芯片既省成本又省PCB面积。多路ADC采集这个点很多人容易忽略实际做项目才发现省事的程度完全不同。我以前做过一个项目用51单片机模拟量多了就得外挂ADC模块接线麻烦不说还得调试SPI或者I2C通信。用STM32C8T6就舒服多了PA0到PA7都是ADC输入通道直接连传感器输出就行。1.2 核心传感器选型逻辑水质检测这个系统核心是选对传感器。我在这版设计里用了三种传感器配合覆盖了水质检测最基础的几个维度DS18B20数字温度传感器测量水温。这个传感器用单总线协议一根线就能通信驱动代码网上非常多移植起来几乎零成本。pH传感器模块检测酸碱度。市面上的pH模块输出的是模拟电压信号通过ADC采集后按照公式换算成pH值。浊度传感器模块检测水的浑浊程度。同样是模拟量输出电压值越高代表水越浑浊。选这三种传感器的原因是它们足够经典、成本可控更重要的是它们代表了两种最常见的传感器接口方式DS18B20是数字接口pH和浊度是模拟接口。一个项目里把这两种接口都覆盖到了做一次就能学到两种驱动方式的写法。如果以后想做TDS水质检测原理也完全一样就是把传感器换成TDS探头代码只需要改一下换算公式。顺便提一下传感器采样策略这三路信号变化速率都不快水温更是缓慢变化的物理量所以采样频率不用太高1秒采一次或者2秒采一次都够。采样太快反而会引入噪声滤起来麻烦。1.3 系统功能划分与交互设计整个系统的功能可以拆成这样几个模块数据采集层三种传感器分别采集温度、pH、浊度原始信号数据处理层对模拟量进行软件滤波按传感器特性曲线换算成真实物理量显示交互层OLED屏幕无缝隙显示实时数据按键可以切换显示界面报警反馈层当某个参数超过设定阈值蜂鸣器响、LED灯闪数据输出层通过串口把数据发给上位机方便PC端监控记录交互设计上我选择了0.96寸I2C接口OLED屏四根线就能接好比起LCD1602省了一堆数据线接线难度直接下降一个档次。按键用了三个一个切换显示页面一个加阈值一个减阈值。这样就能实现在设备上直接调整报警上限和下限不用重新烧程序才能改阈值。2. 硬件原理图核心模块解析2.1 原理图整体模块划分原理图我这版按照功能划分成了几个区域STM32最小系统、电源电路、传感器接口、显示模块接口、报警电路。这种划分方式在画原理图的时候特别重要方便自己检查也方便别人看你的原理图。2.2 STM32最小系统电路要点最小系统这块很多人画PCB时会忽略一些细节我单独说几个关键点。复位电路用的是经典的10k电阻加104电容组合。注意STM32F103是低电平复位NRST引脚直接接10k电阻到3.3V然后104电容接地。如果NRST引脚悬空或者处理不当芯片上电后可能进入异常状态复位按键按下去也没反应。晶振电路这里要特别留意STM32F103C8T6需要两个晶振一个是8MHz主晶振OSC_IN和OSC_OUT另一个是32.768kHz的RTC低速晶振。很多省成本的板子会把RTC晶振省掉但如果你的程序里需要用到RTC实时时钟功能就麻烦了。水质检测系统如果要做数据记录RTC是个很实用的功能所以这版保留了它。BOOT0和BOOT1引脚的处理也有讲究。BOOT0加下拉电阻接地保证从Flash启动BOOT1如果不用就直接接地。这里踩过一个坑如果BOOT0悬空有些芯片会因为引脚电平不稳定导致启动异常程序烧进去跑不起来。2.3 电源电路设计电源部分整个系统用5V USB供电因为pH传感器模块和浊度传感器模块需要5V供电而STM32芯片需要3.3V。所以电源电路的核心就是AMS1117-3.3稳压芯片把5V转成3.3V。去耦电容的布局比原理图上看着更有讲究。AMS1117的输入端和输出端各接一个10uF钽电容和104陶瓷电容作用是滤除低频纹波和高频噪声。每个芯片的电源引脚旁边还必须放一个104去耦电容STM32这种高速芯片在开关瞬间会产生很大的电流突变去耦电容就是给它提供局部电荷存储的没有这个电容ADC采集数据容易不稳定。传感器接口的供电我在原理图上做了跳线处理传感器VCC可以通过跳线帽选择接5V还是3.3V。因为不同型号的传感器模块对电压的要求不一样有的模块虽然标着5V供电但信号输出范围可以达到0-5V而STM32的ADC引脚不允许超过3.3V。预留跳线帽就能灵活适配钝化风险。2.4 传感器接口与信号调理传感器接口这里主要说模拟信号处理。pH传感器模块的输出信号是毫伏级别并且是单端输出直接进STM32的ADC是可以的但要注意共地处理传感器模块的地和STM32的地必须连在一起。浊度传感器模块的输出是0-4.5V的模拟电压这个电压范围超过了STM32 ADC的0-3.3V输入范围直接接进去会烧引脚。解决方案有两种一是用电阻分压把信号衰减到0-3.3V以内二是用运算放大器加电平偏移电路把整体电压范围映射到ADC的测量区间。我这版为了保持原理图简洁用的是电阻分压方案分压系数取0.68倍左右这样4.5V会被映射到约3.06V在安全范围内。不过分压方案也有问题就是会降低有效测量精度。后来实测觉得分压电阻的精度对结果影响不小建议直接用1%精度的金属膜电阻别用5%的碳膜电阻。这种细节看着不起眼但直接影响ADC采出来的数准不准。2.5 显示与报警电路OLED接口我用了I2C方式SCL接PB6SDA接PB7这是STM32F103的I2C1引脚。注意OLED模块一般需要1-10k的上拉电阻大多数现成模块上已经自带了直接用就行。如果是自己做板子集成OLED别忘加上拉电阻。报警电路就是蜂鸣器加LED。蜂鸣器我用了有源蜂鸣器通过单片机一个引脚接三极管驱动。PB5引脚输出高电平让三极管导通蜂鸣器响。三极管选的是S8050限流电阻用1k。LED指示灯直接串联一个330欧姆电阻接到PB4上低电平点亮。3. 软件核心逻辑与代码实现3.1 工程结构与初始化流程软件工程我建议用标准外设库StdPeriph_Lib来写虽然现在HAL库很流行但标准库的代码逻辑更直观对于教学和理解芯片内部机制更有帮助。工程目录分成了几个文件夹User存放main.c和中断处理文件Hardware存放每个外设的驱动文件System存放时钟、延时等基础函数。main函数里的初始化顺序很关键延时函数初始化用SysTick实现GPIO初始化按键、LED、蜂鸣器引脚I2C初始化OLED用ADC初始化PA0、PA1两个模拟输入通道USART初始化串口调试用OLED清屏并显示初始界面初始化顺序错乱会导致一些奇怪的问题比如ADC都开始采数据了但OLED还没初始化好显示就是乱的。我习惯把OLED初始化放在最后因为它耗时长而且不急于在这一刻显示什么其他硬件准备好后再刷新界面就行。3.2 DS18B20温度采集驱动DS18B20的驱动核心就是用GPIO模拟单总线时序。里面几个坑值得好好说说。DS18B20对时序要求比较严格尤其是初始化时序里的480us低脉冲和15-60us的延时窗口时序不对会直接导致设备不响应。最好的办法是写好延时函数然后用逻辑分析仪或者示波器去验证。温度读取流程分三步发送复位脉冲发送跳过ROM命令0xCC发送读取暂存器命令0xBE然后循环读取9个字节DS18B20的温度分辨率默认是12位也就是0.0625摄氏度。温度数据是补码格式需要判断正负。有一个很实用的技巧把高字节和低字节拼接后如果是负数再取反加一这样正负温度都能正确处理。DS18B20转换需要时间默认12位精度下大约750ms这个期间不能读温度数据否则读出来是上一轮的值。所以主循环里要做一个状态机发完转换命令后过750ms再发起读取不能每循环一次就读一次。3.3 ADC多通道采集与软件滤波ADC采集是整个系统的技术核心我用的ADC1的通道0和通道1分别对应PA0和PA1引脚。配置ADC的时候有几个点需要注意首先是采样时间。STM32的ADC采样时间可以配置我用的55.5个采样周期时钟设为12MHz分频。这个采样时间对于pH传感器输出的缓慢变化信号来说绰绰有余如果想要更高精度可以配置到239.5个周期但没必要会拖慢采样速度。多通道采集要使用ADC的规则组扫描模式。配置好两个通道的序列顺序后启动一次软件转换转换完成后从ADC1-DR寄存器里取值。这里有个常见的坑必须先判断EOC标志位确认转换完成后再读数据寄存器否则读到的可能是上一次的数据。软件滤波方面我用了最常用的中位值平均滤波法连续采集11次数据去掉最大值和最小值剩下9个求平均。这种滤波方式对抑制脉冲干扰效果很好代码逻辑也简单不会给单片机带来太大负担。ADC原始值转真实物理量pH传感器的换算公式是线性关系pH 7.0 (2.5 - V_adc) / 0.18其中V_adc是ADC采集到的电压值。这个公司的校准点一般出厂标定在pH7时输出2.5V灵敏度为180mV/pH。如果传感器标定不同只需要修改这个公式里的两个参数就行。浊度传感器的换算公式稍微复杂一点我这里用的是分段线性拟合。电压低于2.5V时对应浊度0-100NTU电压越高浊度越大。准确的做法是拿到传感器数据手册根据里面的特性曲线做线性插值。3.4 OLED显示界面与按键交互OLED驱动我用的0.96寸SSD1306方案I2C接口。驱动代码核心就两个接口函数一个写命令一个写数据。再加上取模得到的字库就能显示字符和数字了。显示界面我设计了三个页面页面一常规监测页同时显示温度、pH值、浊度三行数据页面二阈值设置页显示当前报警阈值可以通过按键加减调整页面三数据趋势页用简单的进度条显示每个参数的实时状态页面切换通过KEY1按键实现每按一次切换一个页面三个页面循环。KEY2按键是阈值加KEY3按键是阈值减。按键处理我用了状态机方式区分了按下瞬间和按住的逻辑避免按一次跳好几个数。消除抖动用的是延时二次确认法简单可靠。3.5 报警逻辑与串口输出报警逻辑采用窗口比较器思路温度、pH值、浊度三个参数任何一个超过设定阈值范围就触发报警。报警优先级做了区分温度超限和pH超限用的蜂鸣器响铃模式不一样长响和间隔响从声音就能分辨是哪个参数出问题。串口输出这块我用的是USART1波特率115200。每2秒向串口发送一帧数据格式是JSON方便上位机解析。比如{temp:25.3,ph:7.02,turbidity:12.5}这个格式用串口助手看一目了然如果想要写上位机软件、或者接ESP8266做物联网直接解析这个JSON就行非常方便。4. Proteus仿真搭建与调试实录4.1 仿真工程搭建步骤Proteus仿真这个项目的时候最重要的限制是很多传感器模型你在Proteus元件库里根本找不到。Proteus里有DS18B20仿真模型直接用就行。但pH传感器和浊度传感器没有现成模型我这里用了替代方案。具体操作是在Proteus里放一个电位器把滑动端接STM32的PA0和PA1引脚。通过调整电位器旋钮改变电压值模拟传感器的模拟量输出变化。根据公式电位器输出电压在0-3.3V之间变化时pH值在3.4-9.8之间线性变化这足够演示系统功能了。在Proteus里放GND和VCC网络的时候要注意网络标签名称必须和原理图里一致否则仿真的时候电气连接是断开的。这是新手在Proteus里最容易犯的错误。4.2 程序烧录与仿真运行把Keil生成的hex文件加载到Proteus的STM32芯片里就能开始仿真。双击芯片在Program File路径里选择编译生成的hex文件。建议选用默认的8MHz外部晶振仿真模式让OLED亮度正常时序不紊乱。仿真运行后OLED屏幕会显示当前的温度、pH值和浊度。手动调整电位器的阻值可以看到对应参数的变化。比如把接pH传感器通道的电位器调到输出电压为2.5V显示出来的pH值就应该是7.0左右这说明AD采样和公式换算逻辑是对的。4.3 仿真调试中发现的问题第一次仿真运行的时候OLED屏幕直接是白屏没有任何显示。排查思路先看I2C通信有没有报错再检查OLED的复位时序是否正确。最后发现问题出在I2C时序延时不够上改成模拟I2C并延长半个时钟周期后屏幕就正常亮了。仿真中还会遇到的一个问题是ADC读取的值一直在跳动导致显示数值不稳定。用时间型中值滤波后数值稳定了很多。仿真环境其实比实物干净得多跳变纯粹是ADC内部噪声和采样时机的问题。这恰好说明滤波算法在仿真阶段就要加上不要等实物再调。此外proteus仿真蜂鸣器的声音效果跟实物有差异软件自带的蜂鸣器模型听不到声音就以为没工作。实际开发时把仿真运行起来看到LED在报警状态下正常闪烁蜂鸣器引脚的状态用逻辑笔检测到的与设计一致时就说明报警逻辑无误了。5. 项目扩展思路与实用建议5.1 从地面系统升级到IoT版本这个水质检测系统目前是本地显示和报警如果你想把它升级成物联网版本最省事的方式是加一块ESP8266或ESP32模块。STM32通过串口把数据发给ESP8266ESP8266通过WiFi把数据上传到云平台手机端就能实时查看。这里有一个设计建议如果确定要升级IoT版本在画原理图时就应该预留USART2的接口和3.3V电源引出脚。我的第一版原理图没预留后面飞线改接不仅影响美观信号质量也有损耗。5.2 传感器校准实操经验传感器校准这块是整个系统精度是否能兑现的关键。DS18B20出厂前已经校准过直接用就行误差在0.5度以内。pH传感器和浊度传感器是模拟输出的必须手动校准。pH传感器校准方法配置标准缓冲液校准时用pH4.00和pH6.86两种溶液先测中性液体调整零点再测酸性液体调整斜率。本设计固件中加入的两点校准功能可以带你从容走完整个校准流程校准参数自动保存在FLASH里掉电不丢失。浊度传感器的校准更麻烦一些需要有标准浊度液。实际项目中我是在蒸馏水接近0NTU和已知浊度的标准液100NTU之间做两点校准的。如果手头没有标准液用干净的自来水作为零点然后用清水加入少量面粉搅拌后的液体作为高点虽然精度不高但验证系统逻辑完全够用。5.3 代码架构的可移植性分析这个项目的代码虽然针对F103C8T6写的但大部分驱动代码可以直接移植到其他芯片上。DS18B20驱动和OLED驱动用的都是GPIO模拟时序只要修改引脚定义和底层读写函数F4系列、G0系列都能跑。ADC采集部分需要根据芯片型号修改初始化的寄存器配置但逻辑思路完全一样。我封装驱动函数时特意把传感器读到的原始值和转换后的物理量分开存储这样即使后面换了传感器型号只需要修改换算公式就能适配主逻辑不用动。这种分层设计的理念在你做更大的项目时会非常受用。6. 常见问题与排查技巧速查表6.1 硬件篇常见问题与排查问题现象可能原因排查方法芯片不启动程序不运行BOOT0电平不对、电源不稳定用万用表测BOOT0引脚电平和3.3V电压OLED白屏不显示I2C地址不对、复位时序不对先跑OLED扫描程序检测设备地址常见地址是0x3C或0x3D蜂鸣器一直响驱动管烧坏、GPIO配置错检测GPIO输出电平和三极管基极电压ADC采到的值一直满量程引脚被拉高、模拟输入超过范围万用表测引脚电压看是否超过3.3V按键没反应按键抖动未处理、GPIO模式配错按键引脚接上拉电阻确认GPIO配成输入上拉模式6.2 软件篇常见问题与排查问题现象可能原因排查方法温度一直显示85度DS18B20未接好或时序不匹配检测单总线GPIO状态重新检查延时函数pH读数随温度波动很大缺少温度补偿应用温度补偿公式或对模拟数据加强滤波程序卡死在ADC等待循环ADC未触发转换检查ADC的EOC标志逻辑或加超时退出机制OLED显示乱码取模方式不对确认取模软件的字模格式是列行式还是行列式串口数据乱码波特率不匹配核对STM32和上位机的波特率配置6.3 仿真与实物的差异问题Proteus仿真和实物会有些差异这点提前有个心理准备。仿真中GPIO响应速度和中断时序比实物快所以一些延时参数在仿真中能用、实物上可能不行。我建议把延时时间都定的稍微宽裕一点比如DS18B20的复位延时标准时序要求至少480us我实际写代码时用了600us这样在仿真和实物上都能稳定工作。另一个典型差异是模拟传感器的零漂。仿真里电位器输出什么电压ADC就看到什么电压非常干净。实物上传感器模块的输出会随着温度变化、供电波动发生漂移所以实物调试时发现显示数值比仿真时偏大或者偏小不要慌先做软件校准再做硬件调整实在不行才换传感器。我在实际调试过程中发现对ADC数据做软件滤波的效果立竿见影。把一个导致数值跳变的通道从毛刺看不清楚的波动滤波后直接变成平滑的数据显示界面看起来专业不少。建议这类功能放在底层封装好这样不管后面加多少个传感器都能统一使用。如果你打算在这个项目的基础上做毕业设计或者产品原型我建议优先把IoT远程监控功能加上现在这个东西比较热门做出来不管是答辩还是展示都能加分不少。还有一点在做PCB的时候记得给传感器接头留出标准接口方便后期更换不同型号探头不然每次换传感器都要动烙铁。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价