STM32驱动HS-S37A非接触式水位传感器+OLED显示完整实验
2026/9/9 1:25:12 网站建设 项目流程

STM32驱动HS-S37A水位传感器+OLED完整实验讲解

好久没写这种“从零到一”的完整实验了。前两天收拾柜子,翻出一块HS-S37A非接触式水位传感器和一块0.96寸I2C接口的OLED屏,正好手边有块STM32F103C8T6最小系统板,索性就把这套组合完整地跑了一遍:传感器采集水位状态,STM32读取信号,再把结果实时显示在OLED上。整个过程从接线、写驱动、调参到最终显示,一路踩了几个不大不小的坑,这里把完整思路和代码细节都整理出来,给正在做类似小项目的朋友一个可以直接参考的样板。

这套方案的核心价值在于:用非接触式传感器替代传统的浮球或电极式水位检测,传感器贴在容器外壁就能感知水位变化,不接触液体、不污染水质、也不会被腐蚀,非常适合鱼缸水位监测、热水器防干烧、智能灌溉、饮水机缺水提醒这类场景。而STM32负责信号采集与逻辑判断,OLED提供直观的人机交互界面,三者组合起来就是一个很典型的“传感器+主控+显示”嵌入式最小系统,拿来练手或者改造成毕设项目都非常合适。

先说清楚,这个实验对基础的要求不算高:你需要会建STM32工程(标准库或者HAL库都行,本文用标准库)、能烧录程序、看得懂GPIO和I2C的基本概念就行。如果你这些还不太熟,也不着急,文章里我会把关键部分的原理掰开揉碎地讲,照着抄也能跑起来。

1. HS-S37A水位传感器:非接触检测的核心原理与接线细节

1.1 非接触式水位检测到底是怎么实现的

HS-S37A本质上是一个基于电容感应原理的非接触式液位传感器。很多人第一次听到“非接触”都会觉得玄乎:不碰到水,怎么知道有没有水?

原理其实不复杂。传感器内部有一个高频振荡电路,当传感器贴附在容器(比如亚克力水箱、塑料水管的管壁)外侧时,传感器与管壁、管壁内的水共同构成一个电容。水的介电常数(大约80)和空气的介电常数(约1)差异巨大,因此当管壁内的水位到达或超过传感器感应区域时,整个结构的等效电容会发生明显变化,这个变化会引起内部振荡电路的振荡频率或幅度发生改变,传感器内部电路检测到这个变化后,通过比较器输出对应的电平信号。

HS-S37A的输出是数字电平:当感应区域检测到有水时,输出低电平;没水时,输出高电平。它的工作电压范围比较宽(典型5V,但3.3V也能工作),输出的数字信号可以直接接到STM32的GPIO上,不需要额外的信号调理电路。

为了验证这个原理,我专门做了个简单测试:把传感器贴在透明亚克力杯外侧,用注射器往杯里注水。实测发现,当水面上升到传感器感应区中部时,输出电平跳变非常干脆,几乎没有中间状态,这对数字采集来说非常友好。不过要注意,传感器的感应区域是有方向的,一般壳体上会有一条标记线或者感应面标识,安装时必须让感应面紧贴容器壁,且保证感应区域和液面升降方向垂直。

1.2 实际接线:传感器、STM32和OLED的电气连接

我这次实验用到的硬件清单如下:

器件型号/规格数量
主控板STM32F103C8T6最小系统板1
水位传感器HS-S37A非接触式水位传感器(3线制)1
显示模块0.96寸OLED,I2C接口(SSD1306驱动)1
下载器ST-Link V2或USB转TTL1
电源5V USB供电或AMS1117-3.3稳压1
辅助材料亚克力水箱/塑料水管,杜邦线,面包板若干

HS-S37A三根线的定义是:红线接电源正极(VCC),黄线接信号输出(OUT),黑线接地(GND)。在这里我踩了一个很多新手容易忽略的坑:传感器虽然可以5V供电,但信号输出引脚的电平是和供电电压一致的。如果传感器接5V,那输出高电平就是5V,直接进STM32的GPIO虽然大多数情况下能识别,但长期运行对引脚不友好。稳妥的做法是把传感器接在3.3V上,或者用电阻分压/电平转换电路把信号降到3.3V。我实测下来,HS-S37A在3.3V供电下工作完全正常,输出跳变依然干脆,所以建议直接接3.3V。

OLED屏是标准的0.96寸I2C接口模块,四根线:VCC接3.3V,GND接地,SCL接PB6(I2C1时钟线),SDA接PB7(I2C1数据线)。STM32F103C8T6的I2C1引脚默认就复用在这两个脚上,用标准库直接配置即可。

完整的接线关系如下表所示:

STM32引脚外设引脚说明
3.3VOLED VCC、HS-S37A VCC电源
GNDOLED GND、HS-S37A GND公共地
PB6OLED SCLI2C1 时钟线
PB7OLED SDAI2C1 数据线
PA0HS-S37A OUT传感器信号输入,配置为上拉输入

这里补充一下为什么把传感器信号接在PA0上。PA0在F103上可以配置成EXTI0外部中断输入,后续如果做低功耗场景,可以直接用外部中断唤醒MCU,而不是一直轮询。这次实验我先用轮询的方式读取,但在代码结构上做了预留,方便扩展。

注意:不同卖家提供的HS-S37A模块,线序颜色可能不一样,接线前务必对照商家给的规格书确认,不要想当然按颜色接。

2. 软件架构:从STM32工程搭建到OLED驱动与传感器轮询

2.1 基于标准库的工程初始化

本次代码使用STM32标准外设库(Standard Peripheral Library),版本是3.5,开发环境用Keil MDK5。工程创建这里不展开细说,重点讲几个关键初始化部分。

首先是GPIO初始化:PA0作为传感器输入,需要配置为浮空输入或上拉输入。考虑到传感器输出高电平是推挽输出,而低电平是开漏/强拉低,用上拉输入更保险,避免悬空时电平漂移。

void GPIO_InitConfig(void) { GPIO_InitTypeDef GPIO_InitStructure; // 使能GPIOA和GPIOB时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); // PA0: 水位传感器信号输入,上拉输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOA, &GPIO_InitStructure); // PB6: I2C1_SCL 复用开漏输出 // PB7: I2C1_SDA 复用开漏输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); }

I2C1外设的初始化也要一并完成,配置为标准模式100KHz速率:

void I2C_InitConfig(void) { I2C_InitTypeDef I2C_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_ClockSpeed = 100000; // 100kHz I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_Address = 0x00; // 主模式地址无所谓 I2C_Init(I2C1, &I2C_InitStructure); I2C_Cmd(I2C1, ENABLE); }

2.2 OLED驱动核心逻辑:SSD1306的I2C时序和显存操作

OLED屏的控制芯片是SSD1306,它只认识I2C时序的命令和数据。这里有一个新手很容易卡住的点:SSD1306的I2C地址到底是什么?不同厂家的模块,地址可能是0x3C,也可能是0x3D,具体取决于模块上DC引脚(或SA0引脚)的电平设置。我手上这块地址是0x3C,代码里封装了一个宏定义,万一你的屏幕不亮,第一个要排查的就是这个地址对不对。

SSD1306的通信流程是:主机发送起始位,发送从机地址(7位地址左移一位加上读写位),然后连续发送控制字节和数据字节。控制字节为0x00表示后续是命令,0x40表示后续是显示数据。这个细节非常重要,因为SSD1306通过控制字节来区分命令和数据的。

我用标准库的I2C外设封装了两个最底层的函数:

// 向OLED写一个字节,mode=0表示命令,mode=1表示数据 void OLED_WriteByte(uint8_t data, uint8_t mode) { while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); I2C_GenerateSTART(I2C1, ENABLE); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, OLED_ADDR, I2C_Direction_Transmitter); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); if (mode == 0) I2C_SendData(I2C1, 0x00); // 控制字节:命令 else I2C_SendData(I2C1, 0x40); // 控制字节:数据 while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C1, data); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTOP(I2C1, ENABLE); }

SSD1306内部有一块128x64位的显存(GDDRAM),每8个像素点组成一页,一共8页。写入显存后,屏幕并不会自动刷新,而是需要设置列起始地址和页地址,然后按顺序写入显示数据。这就是为什么驱动代码里初始化函数后面跟了一长串地址设置命令的原因。

显示字符串的时候,我维护了一个全字模库(ASCII字符集用取模软件生成,8x16大小),通过查表方式逐字符写入显存。核心思想是:先把OLED的显存理解为一个坐标空间,x范围0~127,y范围0~63,每显示一个字符需要先设置好这个字符左上角的坐标。

// 显示一个字符串 void OLED_ShowString(uint8_t x, uint8_t y, char *str) { while (*str != '\0') { OLED_ShowChar(x, y, *str); x += 8; if (x > 120) // 超出右边界换行 { x = 0; y += 2; } str++; } }

2.3 传感器轮询逻辑:软件滤波与状态判断

水位传感器的读取在逻辑上很简单:读PA0的电平,低电平代表检测到水,高电平代表没水。但实际应用中,液面晃动、气泡经过感应区、传感器贴附不紧密等情况都可能导致电平短暂抖动,因此我加了一个简单的软件滤波:连续读取10次,如果10次中低电平出现了8次以上,才判定为“有水”,否则判定为“无水”。

uint8_t WaterLevel_Detect(void) { uint8_t count = 0; uint8_t i; for (i = 0; i < 10; i++) { if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0) count++; Delay_Ms(5); // 每5ms读一次 } if (count >= 8) return 1; // 有水 else return 0; // 无水 }

这里的5ms延时不是随便写的。一方面,10次共耗时50ms,这个时间足够覆盖液面抖动的常见周期;另一方面,这个延时不会对OLED显示刷新造成明显影响,因为人眼本来就需要几十毫秒才能感知画面变化。如果你要做更快速的响应,可以适当减少采样次数或缩短间隔,但要注意别引入误判。

3. 完整代码实现与屏幕显示设计

3.1 主循环:数据采集、状态判断与显示刷新

主程序逻辑非常清爽:初始化外设,清屏,进入主循环,每轮循环做一次水位检测,然后把结果和附加信息通过OLED显示出来。

int main(void) { uint8_t waterLevel; char displayBuf[20]; Delay_Init(); GPIO_InitConfig(); I2C_InitConfig(); OLED_Init(); OLED_Clear(); // OLED显示固定提示信息 OLED_ShowString(0, 0, "HS-S37A Sensor"); OLED_ShowString(0, 2, "STM32F103C8T6"); while (1) { waterLevel = WaterLevel_Detect(); if (waterLevel) { OLED_ShowString(0, 4, "Water: YES"); OLED_ShowString(0, 6, "Level: HIGH"); } else { OLED_ShowString(0, 4, "Water: NO "); OLED_ShowString(0, 6, "Level: LOW "); } // 显示当前引脚电平值,方便调试 sprintf(displayBuf, "PA0:%d", GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)); OLED_ShowString(0, 5, displayBuf); Delay_Ms(200); } }

这里的OLED坐标参数需要注意:OLED_ShowString的第二个参数y是页索引,0代表第0页(屏幕最顶端),6代表第6页(屏幕靠下的位置)。一行字符的高度是2页,所以坐标0、2、4、6就是从上到下四行8x16字符的显示位置。这个设计保证每行字符不会重叠。

3.2 OLED初始化的关键配置序列解析

很多人在移植OLED驱动时,ifdef比较多,看着就头疼。其实SSD1306的初始化序列是固定的,这里我给出一个精简版,并标注每一条命令的含义:

void OLED_Init(void) { Delay_Ms(100); // 上电等待屏幕内部复位完成 OLED_WriteByte(0xAE, 0); // 关闭显示 OLED_WriteByte(0xD5, 0); // 设置时钟分频因子 OLED_WriteByte(0x80, 0); OLED_WriteByte(0xA8, 0); // 设置驱动路数 OLED_WriteByte(0x3F, 0); // 64路 OLED_WriteByte(0xD3, 0); // 设置显示偏移 OLED_WriteByte(0x00, 0); OLED_WriteByte(0x40, 0); // 设置起始行 OLED_WriteByte(0x8D, 0); // 电荷泵开关 OLED_WriteByte(0x14, 0); // 开启电荷泵 OLED_WriteByte(0x20, 0); // 设置内存寻址模式 OLED_WriteByte(0x02, 0); // 页寻址模式 OLED_WriteByte(0xA1, 0); // 段重映射 OLED_WriteByte(0xC8, 0); // 扫描方向 OLED_WriteByte(0xDA, 0); // 设置COM硬件配置 OLED_WriteByte(0x12, 0); OLED_WriteByte(0x81, 0); // 设置对比度 OLED_WriteByte(0xCF, 0); OLED_WriteByte(0xD9, 0); // 设置预充电周期 OLED_WriteByte(0xF1, 0); OLED_WriteByte(0xDB, 0); // 设置VCOMH电压倍率 OLED_WriteByte(0x40, 0); OLED_WriteByte(0xA4, 0); // 显示使用GDDRAM内容 OLED_WriteByte(0xA6, 0); // 正常显示(非反色) OLED_WriteByte(0xAF, 0); // 开启显示 OLED_Clear(); }

这条初始化序列有几个关键点:

  • 电荷泵必须开启:0x8D命令配合0x14参数,为屏幕内部DC-DC转换器供电。如果这一步没做,屏幕上什么都显示不出来。这是OLED模块最常见的不亮原因之一。
  • 对比度设置:0x81命令后跟0xCF,这个值可以在0~255之间调整。如果你觉得显示偏暗或偏亮,可以修改这个值,效果立竿见影。
  • 页寻址模式:0x20命令后跟0x02,表示使用页寻址。这种模式下,你写完一页(128字节数据)后,列地址会自动回到起始位置,页地址保持不变或由软件控制切换,便于逐页刷新。

3.3 字模与取模方式

显示汉字和特殊符号需要字模库。我用的是PCtoLCD2002软件,设置如下:取模方式选择“逐行式”,取模走向选择“顺向”,输出数制“十六进制”,每行显示数“16”。以16x16大小的汉字为例,每个汉字占32字节。这里我举例,如果要显示“水”字,字模数组大致长这样:

const uint8_t HZ_Water[32] = { 0x00, 0x00, 0x7F, 0xFC, 0x08, 0x20, 0x08, 0x40, 0x08, 0x80, 0x09, 0x00, 0x0A, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00 };

字模显示函数要比显示字符多一个步骤:先写上半部分8行(取前16字节),再写下半部分8行(取后16字节),因为16x16的汉字在8页结构中占两页。

提示:如果你是第一次用PCtoLCD2002,注意一定要把取模的“字节内位序”设为“从高位开始”,否则字会“反”着显示。这也是新手非常容易踩的坑。

4. 实测过程、调参经验与踩坑记录

4.1 实测场景:从亚克力杯到塑料水管

我先在亚克力杯上做第一次实测。传感器用双面胶贴在杯壁外侧,感应面朝向杯内,垂直放置。杯里的水位用注射器逐步增加,每次增加5mm左右,同时观察OLED显示状态。

结果发现一个问题:当水位低于传感器感应区下边缘约2mm时,OLED已经显示“Water: YES”了。也就是说,传感器存在一个“提前触发”现象。原因在于传感器的感应区域不是一个绝对的点,而是一个有一定高度的区域,而且液面靠近但尚未完全到达时,电容变化已经足以触发内部比较器翻转。

这个现象本身不影响使用,但如果你做的是“水位上限报警”类应用,就需要知道传感器触发点对应的实际水位高度,必要时在安装位置上做补偿。比如希望水位到达10cm位置时报警,实际上传感器在9.8cm左右就触发了,那你可以把传感器稍微上移一点来校准。

随后我又把传感器贴在透明塑料水管外壁测试。这种细管场景下,管内水量少,传感器的感应灵敏度会下降。实测外径20mm、壁厚2mm的塑料管,传感器贴着管壁时依然能可靠检测到管内是否充满水。但如果管子壁厚超过5mm,或者管道直径小于8mm,触发就会变得不稳定。这种情况下建议选择灵敏度更高的型号,或把传感器贴在管径较粗的部位。

4.2 干扰与误判的排查链路

实验过程中遇到过两次“传感器输出状态和实际水位对不上”的情况,排查过程值得分享。

第一次,液面明明没有到达传感器感应区,OLED却显示“有水”。我第一反应是怀疑GPIO配置问题,但用万用表量传感器输出脚,发现确实是低电平,说明传感器自己触发晚了(或者说提前了)。进一步观察发现,当时传感器附近的管壁外表面有水流淌过后留下的水膜,这层水膜改变了传感器附近的介质环境,导致等效电容变大。用干布擦净管壁后,现象消失。这提醒我:非接触式传感器虽然不接触液体,但传感器感应面周围不能有积水或高介电常数物质,安装时要注意保持感应面清洁干燥。

第二次,传感器输出高电平(无水状态),但实际水箱里明明有半箱水。排查后发现,双重原因叠加:传感器贴在容器上的位置偏高,而水位虽然过半,但还没到达传感器的感应区;此外,容器壁是磨砂亚克力,厚度偏厚,导致灵敏度下降。重新调整安装位置后问题解决。

4.3 OLED显示异常的几个经典坑

OLED部分的坑主要出现在初次上电阶段:

第一坑:屏幕全黑,背光也没有。用万用表量OLED模块的VCC和GND之间电压,发现只有1.8V,检查后发现是杜邦线接触不良。OLED模块电流虽然不大,但压降不容忽视,尤其是用了较长杜邦线的时候。建议3.3V和GND各用两根杜邦线并联供电,或者直接焊接,能显著减少接触电阻。

第二坑:屏幕亮但显示乱码。这是因为I2C速率不匹配或地址错误。我先用逻辑分析仪抓包,发现从机地址发送的是0x3D(7位地址模式下读写位和地址没拼对),改成0x3C后正常。标准库里I2C_Send7bitAddress函数会自动处理读写位,所以地址参数直接填0x3C即可,不需要自己左移一位。

第三坑:某些区域出现“鬼影”,显示内容残留。这是SSD1306的显存更新不完整导致的。我的解决办法是每次刷新前先调用OLED_Clear()清屏,虽然会引入一点点闪烁,但能确保显示内容干净。如果你对闪烁敏感,可以把静态内容(如标题行)和动态内容(如水位状态)分开区域刷新,只在动态区域做清屏和重写。

5. 拓展思路:如何把实验做成一个完整的应用项目

5.1 增加声光报警功能

在水位监测的基础上,很容易扩展报警功能。当检测到水位过低时,可以驱动一个无源蜂鸣器发声,同时让一个LED闪烁。硬件上只需要把蜂鸣器接在某个PWM输出引脚上,用定时器产生不同频率的方波即可。代码层面,在主循环检测到水位为0时,控制PB1输出不同占空比的PWM信号,或者用GPIO翻转模拟。这样做之后,这套系统就可以直接改造成热水器防干烧提醒器。

5.2 接入Wi-Fi模块实现远程水位监控

如果你有ESP8266或ESP32模块,可以通过串口与STM32通信:STM32把水位状态封装成简单的协议帧,比如帧头是0xAA,数据位0x01表示有水、0x00表示无水,再加上帧尾0x55。ESP8266通过AT指令或MQTT协议把状态上报到云平台,手机端就能实时查看。这种方案在智能鱼缸、远程灌溉系统里非常常见。

5.3 用多个HS-S37A实现多水位监测

一个鱼缸或者水箱如果只检测一个水位,很多时候不够用。比如需要监控“低水位报警”和“高水位停机”两个状态,那就需要两个传感器,分别贴在容器的上、下两个位置。每个传感器的信号线接一个GPIO,程序里分别读取并做逻辑判断。STM32F103C8T6的GPIO资源非常充足,接四五个传感器一点问题都没有。不过要注意,多个传感器同时工作时,要确保它们的供电稳定,避免共地干扰导致误判。

5.4 从轮询改成外部中断模式

在低功耗场景中,MCU不能一直醒着轮询传感器。可以把PA0配置成EXTI0外部中断,在下降沿(检测到水)和上升沿(水位撤离)时唤醒MCU,其他时间进入Stop模式。这样一来,平均功耗能大幅下降,适合电池供电的户外灌溉装置。代码改造也不复杂,主要是配置EXTI中断线0和NVIC中断优先级,在中断服务函数里置一个标志位,主循环查询标志位后刷新显示。

5.5 HS-S37A与其他传感器的组合应用

水位传感器单独用略显单薄,如果加上DS18B20温度传感器,可以做一个“水位+温度”一体化的养殖水箱监测器。DS18B20是单总线协议,占用一个GPIO,和OLED、水位传感器用同一个STM32完全不成问题。显示界面可以分两屏:一屏显示水位状态,一屏显示实时水温,用按键或自动轮播切换。这种组合项目的完成度比单片传感器高很多,作为毕业设计或者课程设计作品拿出来,说服力强不少。

6. 调试工具与代码问题定位的个人经验

整个实验做下来,最花时间的不是写代码,而是查问题。这里分享几个非常实用的调试工具和方法。

逻辑分析仪是排查I2C问题的神器。我用的是一根USB逻辑分析仪(8通道,24MHz采样率),在OLED不显示的时候,用它抓一下SCL和SDA上的波形,马上能看出有没有数据在传输、时序是否正确、ACK位是否正常。相比用示波器,逻辑分析仪的优势是可以解码协议,直接看到主机发了什么地址、什么数据。

串口打印在传感器调参阶段帮了大忙。我习惯在传感器状态变化的瞬间,通过UART1打印一条带时间戳的日志,方便复盘。如果你手头没有USB转TTL模块,也可以用OLED临时充当调试输出,把关键变量打印到屏幕上。这里要注意,sprintf在STM32标准库里比较占Flash和RAM,如果空间紧张,可以用简单的数字转字符串函数替代。

代码版本管理方面,虽然这种小实验可能不需要上Git,但至少要在改代码前备份一份能跑的版本。有一次我调整了OLED的初始化时序,结果屏幕死活不亮,来回改了一个多小时,最终发现是漏了一条命令。如果当时有备份,直接对比差异,几秒钟就能定位问题。

我想特别强调一下延时函数的准确性。标准库的Delay_Ms如果有微秒级的偏差,在OLED初始化、传感器轮询采样这些对时序有要求的场景里,会产生一些莫名其妙的问题。建议用定时器做一个严谨的延时,比如用SysTick做1ms基准,而不是用简单的空循环。实测中,如果Delay不准确,传感器滤波采样的时间间隔会漂移,偶尔会出现判断结果抖动的现象。

整个系统跑起来后,功耗表现也挺有意思。STM32F103C8T6在72MHz主频下,再加上OLED和传感器的工作电流,整体功耗大约在30mA左右。如果后续你用电池供电,别忘了让OLED进入休眠模式(通过0xAE命令关闭显示)并降低MCU主频。对于大多数桌面供电场景,这点功耗可以忽略。

最后说一个很多人会忽略的小事:传感器的固定方式。双面胶临时测试没问题,但长期使用会因为水汽、温度变化导致感应面贴合不均匀,进而影响检测的一致性。建议在正式项目中用硅胶或环氧树脂把传感器牢固地贴在容器壁指定的感应位置,确保感应面和容器壁之间没有空气间隙。空气的介电常数低,如果存在间隙,相当于在水和传感器之间加了一层“绝缘垫”,会明显降低检测灵敏度。这个细节直接决定你设备的长期稳定性。

这个实验做下来,从最底层的GPIO配置到传感器数据的采集判断,再到OLED的驱动显示,虽然每一部分拆开看都不复杂,但组合在一起就是一套完整的嵌入式应用开发流程。如果你正在学STM32,对传感器和显示模块的配合还不太熟,这个组合是个特别好的练手项目。照着我上面的步骤和代码走一遍,相信你能很快跑通,并在跑通的基础上做出自己的扩展功能。

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

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

立即咨询