ARTICLE DETAIL

资讯详情

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

STM32驱动HS-S37A非接触式水位传感器并OLED显示完整实战

STM32驱动HS-S37A非接触式水位传感器并OLED显示完整实战 说句大实话玩嵌入式这些年我觉得最容易被新手低估的传感器就是“水位检测”。传统的浮球开关和电极式探头要么机械结构容易卡死要么金属电极长期泡水里会电解腐蚀放到饮水机、鱼缸、加湿器这些场合卫生和寿命都是大问题。所以当我在项目里用到 HS-S37A 这种非接触式电容水位传感器时第一反应是这东西早该普及了。这次就完整记录一遍我用 STM32 驱动 HS-S37A 水位传感器再把水位数据实时显示到 OLED 屏幕上的全过程从原理、接线、CubeMX 配置到 HAL 库代码和踩坑记录一次性讲透。1. 实验整体设计为什么选HS-S37A和OLED1.1 HS-S37A原理非接触式电容水位检测HS-S37A 的核心原理说白了就是“水的介电常数和空气差太多”。空气的介电常数大约是 1而水在常温下能到 80 左右。传感器探头上有一对铜箔电极电极之间就等效于一个电容器。当容器里的水位升高水覆盖到探头贴着的区域时电极间的等效介电常数会明显变大电容值也随之上升。这个电容变化非常微小所以模块里集成了高精度的电容检测芯片先把电容值转成数字量再通过内部算法输出水位状态。我经常给新手打的比方是你拿两手放在一个感应式台灯旁边手靠得越近灯的亮度变化越明显。HS-S37A 就是这个原理只不过它感应的对象换成了水而且感知的是“贴着容器外壁的那层液体”。非接触式水位传感器工作原理图里最关键的检测链路是铜箔电极感知电容变化 - 内部CDC电容数字转换器采集 - MCU逻辑判断水位等级 - IIC/模拟量输出。因为探头不用接触液体就没有电极腐蚀和污染的问题容器里想放什么液体都行这也是它被大量用在即热饮水机、咖啡机、加湿器里的原因。1.2 系统架构传感器、主控、显示怎么连这套实验的硬件架构非常清晰HS-S37A 水位传感器作为采集端STM32 作为主控核心OLED 作为显示端。传感器本身提供两路输出一路是 IIC 数字接口可以直接读到水位百分比另一路是模拟量输出 AO电压会随着水位变化可以接到 STM32 的 ADC 引脚做二次确认。有人会问既然 IIC 已经能读水位了为什么还要模拟量我实测下来的体会是IIC 读到的是模块内部算法处理后的等级数据直接用很方便而模拟量是原始信号线性映射出来的电压值更适合自己做标定和细分。如果你想把水位精确到 10% 一档或者想接一个变量来表示“半杯水”模拟量会比固定等级更灵活。我在实验里两路都接了目的就是互相校验。OLED 我选的是最常见的 0.96 寸 SSD1306 驱动模块IIC 接口和 HS-S37A 挂在同一条 I2C 总线上。这样接线非常省事整个系统只需要用到 STM32 的 4 个引脚2 个 I2C1 个 ADC1 个地哪怕是面包板上搭实验线也不会乱。STM32 这边我选了 F103C8T6也就是大家俗称的“蓝丸”价格便宜、资料多HAL 库工程用 CubeMX 生成开发效率很高。如果你用的是其他型号比如 C6T6、G431或者国产 APM32 这类兼容芯片思路完全一样CubeMX 里重新选型号生成工程就行。2. 硬件接线与工程配置2.1 材料清单与引脚分配我这次实验用到的材料清单如下材料说明STM32F103C8T6 最小系统板主控建议选带ST-Link下载口的HS-S37A 水位传感器模块非接触式电容水位检测带探头0.96寸 OLED SSD1306IIC接口7针或4针均可ST-Link V2 下载器烧录和调试面包板、杜邦线若干搭建电路塑料水杯最好用薄壁无金属的杯子壁厚2~3mm以内效果最好引脚分配我直接给结论HS-S37A 引脚STM32引脚说明VCC3.3V传感器供电3.3V~5V均可GNDGND共地SCLPB6I2C1_SCLSDAPB7I2C1_SDAAOPA0ADC1_IN0读取模拟电压OLED 引脚STM32引脚说明VCC3.3V模块供电GNDGND共地SCLPB6与传感器共用I2C1_SCLSDAPB7与传感器共用I2C1_SDA这里要注意两个设备共用 I2C 总线的前提是地址不冲突。OLED 的 SSD1306 常见地址是 0x3CHS-S37A 默认地址按我手头模块的 datasheet 是 0x027位地址。你拿到不同批次的模块可能地址有差异所以我在后面的排查部分会专门讲怎么扫描 I2C 地址。2.2 CubeMX配置细节使用 STM32 HAL 库点亮 OLED 屏幕全流程以及整个工程的生成都是从 CubeMX 开始的。打开 CubeMX 新建工程芯片型号选 STM32F103C8Tx接下来按这几个关键点配置。SYS 选项卡里Debug 一定要选 Serial Wire否则第一次下载后 SWD 引脚被禁用下次就连接不上了。Timebase Source 推荐改成 TIM1 或者 SysTick 之外的定时器。为什么因为 HAL_Delay 依赖 SysTick如果后面你在中断里调用 HAL_Delay 或者某些外设回调用了延时SysTick 优先级处理不当会导致 delay 卡死这是一个非常经典的坑。RCC 选项卡把 HSE 设成 Crystal/Ceramic Resonator外部晶振用 8MHz。说到晶振有人问过 STM32 晶振电容计算简单说一下如果晶振负载电容 CL 是 12.5pF那么匹配电容大约取 C 2 * CL - 杂散电容杂散一般估 3~6pF算下来大概 18~22pF。对低速 I2C 和普通跑代码来说用 20pF 左右的电容基本不会出问题。I2C1 开启后把 I2C Speed Mode 设为 Standard Mode速度 100kHz。100kHz 对两个设备来说都够用而且长线抗干扰更好。ADC1 里使能 Channel0对应 PA0采样时间我这里建议选 239.5 Cycles读数会更稳。时钟树里把主频配置到 72MHz这个不用多说F103 的常规操作。生成工程的时候Toolchain 选 MDK-ARM版本选你 Keil 能打开的就行。代码生成那边我习惯勾选 “Generate peripheral initialization as a pair of .c/.h files per peripheral”这样 I2C 和 ADC 的初始化代码会单独拆出来后续改起来思路清楚。2.3 编译环境常见坑Keil、ST-Link与驱动工程生成后用 Keil5 打开先设置一下烧录器。Options for Target - Debug 里选择 ST-Link Debugger然后进 Settings如果能看到 SW Device 里识别出芯片说明连接正常。很多新手第一次插上 ST-Link电脑设备管理器里出现一个带黄色感叹号的“STM32 Virtual ComPort”然后怀疑自己板子坏了。其实这个感叹号大多不影响 SWD 下载它只是 ST-Link 的虚拟串口驱动没装好。你先下载安装 STSW-LINK009 驱动如果还感叹号可以用驱动强制更新或者换一根 USB 线试试我之前遇到过 ST-Link 固件版本过旧导致串口枚举不正常升级 ST-Link 固件后问题消失。真正麻烦的报错是error: no stm32 target found! if your product embeds debug authentication, pl... 这种。我碰到的情况基本就三类第一ST-Link 没接好检查 SWDIO、SWCLK、GND 三根线第二程序里禁用了 SWD或者 CubeMX 中 Debug 没选 Serial Wire解决办法是按住板子复位键点下载的同时松开复位有时候能抢在程序跑飞前连上第三目标板供电不稳单独给板子供 5V并且 ST-Link 的 3.3V 输出不要同时给太多模块供电。还有一个容易忽略的点就是 HS-S37A 和 OLED 都挂在 3.3V 下如果蓝丸板子上的 LDO 电流输出能力一般同时带动传感器、OLED、ST-Link 会显得吃力。我建议 ST-Link 供电只给板子传感器和 OLED 从板子的 3.3V 引脚取电但杜邦线尽量短、粗一点。3. 软件实现从寄存器到OLED显示3.1 IIC读取传感器数据先把 HS-S37A 当成一个 IIC 从机设备来看。它的寄存器映射不同厂家略有差异我手头这个模块读水位状态的时候是先发设备地址加寄存器地址然后连续读两个字节低字节就是水位百分比。写代码之前我习惯先写一个简单的“IIC扫描函数”把总线上所有能响应 ACK 的地址打印出来。这样不用猜设备地址非常实用。#define HS37A_ADDR (0x02 1) /* 7位地址0x02HAL中要左移1位 */ #define HS37A_REG (0x01) /* 以手头模块为例的水位寄存器 */ uint8_t hs37a_read_level(void) { uint8_t buf[2] {0}; HAL_StatusTypeDef status; status HAL_I2C_Mem_Read(hi2c1, HS37A_ADDR, HS37A_REG, I2C_MEMADD_SIZE_8BIT, buf, 2, 100); if (status ! HAL_OK) { return 0xFF; /* 读取失败统一返回0xFF调用方自行处理 */ } return buf[0]; /* 低字节是水位百分比 */ }这里解释一个关键参数HAL_I2C_Mem_Read中第二个参数是“8位地址”也就是把 7 位地址左移一位后的值。很多新手在这里纠结看到 0x01 或者 0x02 就直接填进去结果通信失败。记住一条规则ST 的 HAL 库要求传的是“写地址”或“读地址”的完整字节所以 7 位地址都要 1。同理OLED 的 0x3C 在实际代码里要写成0x3C 1。读回来的水位百分比我这里模块是 0~100 的整数步进 25%也就是 0、25、50、75、100 这五档。你手里的模块如果读出来是别的格式比如寄存器高位低位组合或者连续几个水位阈值一定以厂家 datasheet 为准。我见过有兄弟不看手册强行按网上别人的代码来读读出来全是 255其实就是寄存器地址和字节序对不上。3.2 ADC读取模拟量做二次确认除了 IIC我还把 AO 引脚接到了 PA0用 ADC1 通道 0 去读。这里不是为了折腾而是为了验证传感器读数的一致性。接好水杯后空杯状态下 AO 电压大约 0.5V满水时大约 2.2V不同模块会有差异所以我没有硬编码电压范围而是先采集空杯和满水两个端点再线性映射。HAL 库的 ADC 读取函数封装得很简单uint16_t get_adc_raw(void) { uint16_t value 0; HAL_ADC_Start(hadc1); if (HAL_ADC_PollForConversion(hadc1, 10) HAL_OK) { value HAL_ADC_GetValue(hadc1); } HAL_ADC_Stop(hadc1); return value; } float get_adc_voltage(void) { return get_adc_raw() * 3.3f / 4095.0f; }换算成水位百分比时我用线性映射#define V_EMPTY (0.50f) /* 实测空杯电压 */ #define V_FULL (2.20f) /* 实测满杯电压 */ int adc_to_percent(float voltage) { float percent (voltage - V_EMPTY) / (V_FULL - V_EMPTY) * 100.0f; if (percent 0) percent 0; if (percent 100) percent 100; return (int)percent; }这个电压值记得加上滑动平均滤波。我实验中直接读 ADC 时虽然采样时间选得比较长但电源上有一点纹波读数偶尔会跳 1~2 个百分比。我加了一个简单的 8 次平均数据就非常稳了。很多人觉得模拟量输出是多余的但在 HS-S37A 自校准出问题的时候模拟量能帮你快速判断是传感器硬件问题还是算法问题。因为模拟量直接从原始信号变换过来不受内部逻辑判断的影响这在我后面排查故障时帮了大忙。3.3 OLED显示水位与进度条OLED 显示这部分我用的标准 SSD1306 驱动GND 和 VCC 接好SCL/SDA 接到 I2C1 总线上。初始化函数网上有大量现成的核心是把 SSD1306 的显存配置写对128x64 分辨率、I2C 地址 0x3C。我在工程里做得很简单先清屏然后定时读取 HS-S37A 的水位值刷新屏幕文字和一个简易的百分比进度条。void oled_show_water(uint8_t level) { char buf[16]; OLED_ShowString(0, 0, Water Level:); sprintf(buf, IIC:%3d%%, level); OLED_ShowString(0, 2, buf); /* 画一个简易进度条20列像素为一格满格100% */ uint8_t blocks level / 5; /* 0~100映射到0~20格 */ OLED_ShowString(0, 4, [ ]); for (uint8_t i 0; i blocks; i) { OLED_DrawPoint(i 1, 4 * 8, 1); /* 逐点画实心格 */ } }代码里我只写了关键逻辑实际工程中 OLED 的 128x64 显示字符串、画点函数用你熟悉的任意 SSD1306 库都可以。重点讲两个细节第一OLED 和 HS-S37A 共用 I2C读传感器和刷屏不要太频繁。我实测 100ms 读一次传感器、200ms 刷一次屏幕完全足够刷太快既浪费 MCU 资源也可能因为总线上连续占用导致传感器偶发 NACK。第二OLED 的显示内容要和自己做的标定一致别 IIC 读到的是 75%ADC 换算出来是 70%看起来就矛盾了。我在实验里把两路数据显示成两行只是方便观察真正用的时候选一路作为主数据源。OLED 显示汉字也是一个常见需求SSD1306 要显示汉字需要自己做取模。取模软件生成 16x16 点阵数据然后在驱动里写一个可以把点阵数据映射到显存的函数。这个内容展开又是一个大话题这次实验里我用英文就能把状态讲清楚所以先不折腾中文显示。4. 实测效果与问题排查4.1 实测现象与标定把 HS-S37A 的探头贴在塑料杯外壁用透明胶带固定注意探头要与杯壁贴合紧密中间不能有气泡。上电后屏幕先显示 0%然后我往杯子里倒水水位每上升一截IIC 读到的百分比会跳变一档。从 0 到 25 到 50 到 75 到 100响应时间大概在几百毫秒这个速度对水箱液位监控来说完全够用。我同时盯着 ADC 换算出来的电压值它随着水位上升是连续变化的不会像 IIC 那样跳变。这正好说明了一个设计技巧如果你要做“水满自动停”这种控制用 IIC 的 100% 档位判断就足够了但如果你想做“剩余水量百分比实时显示”用 ADC 电压线性映射会更平滑视觉体验更好。标定的流程我也建议你记录一次。先把空杯状态下的 AO 电压记下来再把探头贴在杯壁缓慢加水到接近杯口记录满水电压。这两个数值填入前面代码里的V_EMPTY和V_FULL整个系统就完成标定了。不同的杯子壁厚、杯子材质、探头贴法都会影响这两个端点所以拿到传感器别急着抄网上别人的标定值自己实测一次最靠谱。4.2 高频问题速查表我把这次实验以及群里朋友反馈过的典型问题整理成表方便你直接对照排查现象可能原因解决方法OLED 不显示I2C 地址填错、供电不足、接线松动先扫描 I2C 地址确认 0x3C检查 OLED VCC 是否 3.3V杜邦线重新插拔传感器读回 0xFF从机地址错误、寄存器地址不对、I2C 总线冲突确认 HS37A 地址参考 3.1 的扫描方法核对寄存器映射水位一直显示 100% 或 0%传感器探头没贴紧容器壁、杯壁太厚、有金属干扰重新贴探头选择薄壁塑料容器远离金属物体IIC 通信偶发失败上拉电阻缺失、总线过长STM32 内部上拉一般够用但长杜邦线建议外接 4.7k 上拉到 3.3V加水后读数不变传感器误触发自校准把当前状态当成了基准断电重新上电空杯状态下等待稳定后再加水OLED 和传感器同时使用但屏幕闪总线地址冲突或刷屏太频繁检查地址把显示刷新间隔拉长到 200ms 以上ADC 读数跳变电源纹波大、未加滤波加滑动平均滤波给传感器单独去耦电容4.3 HS-S37A自校准带来的两个坑HS-S37A 有一个“自校准”功能这个功能本来是方便用户免配置的但它也给实际使用埋了两个坑。第一个坑是误校准。模块上电后会把当前状态作为初始基准。如果容器里已经有一半水而你在这个时候上电模块可能会把“半水”误认为 0% 基准导致加满水也显示不到 100%。解决办法很简单每次系统上电时保证容器处于空杯或已知状态等待几秒让传感器完成校准再进行水位检测。我用在产品里的时候会在上电后加一个延时 2 秒再开始读取并且把传感器初始化状态通过 OLED 打出来。第二个坑是长期漂移。传感器检测电容变化本质是对“当前值和基准值差异”的判定。如果环境湿度、容器内的液体介质发生较大变化可能导致基准漂移。比如你上午测的是清水下午换成盐水或者牛奶介电常数不一样读数就会异常。做项目时一定要明确 HS-S37A 适合的场景是“检测同一种介质的两个状态有水/无水”而不是“精确测量不同液体的水位”。5. 扩展思路与个人心得5.1 这套实验还能往哪些方向扩展这套 STM32 HS-S37A OLED 的实验稍微改一改就能变成很多实际项目的基础。比如热词里有人提到“基于 stm32 的智能台灯”你可以把水位传感器换成监测桌面水杯如果水杯没水了OLED 显示提醒台灯灯光颜色也自动变化又比如“stm32 鱼缸”场景HS-S37A 贴在鱼缸外壁检测水位低于阈值时驱动一个小水泵自动补水这个组合做出来就是一套全自动的鱼缸水位控制器。如果加上无线模块比如 ESP8266或者接机智云这类云平台就变成一套远程水箱监测系统。手机端能看到当前水位水少了推送提醒这已经是一个很完整的物联网课设或者毕业设计方向了。再进阶一点用多个 HS-S37A 探头分布在不同高度就能判断“1/4 水位”“1/2 水位”“满水”每个探头对应一个补水阶段控制器可以分级加水避免一次加太多导致溢出。我在单位做过一次智能加湿器的预研用的就是类似思路HS-S37A 贴在储水箱外壁ST 单片机检测水位低于 25% 时开进水阀高于 90% 时关阀OLED 显示实时水位。整个模块在加湿器原型机上跑了半个多月没出现电极腐蚀问题这也侧面说明非接触式方案在消费家电里的实用性。5.2 关于HS-S37A的两个经验细节最后分享两个容易被忽略的小细节都是实际操作中攒下的经验。第一个是探头贴装。HS-S37A 的探头本身是柔性或半刚性的铜箔电极贴到容器外壁后一定要保证贴合面没有空隙。我试过用热熔胶固定和用透明胶带固定热熔胶固定效果最好因为它能把边缘也压紧透明胶带如果粘不牢中间留一点空气读数就会明显偏低。还有一点杯子里的水如果晃动剧烈水位读数也会跟着波动做实验的时候等水面静止后再读数做产品的时候建议加一点软件滤波或者滞后判断。第二个是电源隔离。HS-S37A 的模拟量输出 AO 比较敏感如果和电机、继电器、水泵共用电源水位的模拟量读数会跳得很难看。我在一次实验里把水泵直接接到同一个 3.3V 上水泵一启动ADC 读数直接从 3 成飙到 8 成差点以为传感器坏了。后来把水泵单独用 5V 和 MOSFET 驱动数字地与模拟地单点连接问题才解决。如果你只是做显示实验问题不大但只要接水泵、电磁阀这类执行器供电设计一定要提前规划。
返回列表