前言
本篇基于 STM32F103C8T6 单片机实现水位检测实验,使用 WaterSensor 导电式水位传感器采集水位信号,通过单片机 ADC 采集模拟电压,经过滤波、标定换算得到相对水位百分比,最终把 ADC 原始值、引脚电压、水位占比、水位状态输出到 OLED 屏幕上。
注意:该传感器检测的是相对水位,不是直接测量水深,受水质、安装方式影响,适合教学演示,不适合工业高精度液位检测。
实验整体流程简述: 水面接触传感器电极面积改变 → 电极间导电程度改变 → 传感器 OUT 输出电压改变 → STM32 的 ADC 采集模拟量 → 多次采样取平均滤波 → 根据标定参数计算水位百分比与水位等级 → OLED 屏幕刷新展示数据。

一、实验硬件清单
- STM32F103C8T6 最小系统板
- WaterSensor 水位传感器模块
- 0.96 寸 I2C OLED 显示屏
- 杜邦线若干
- 水杯、自来水(用于实物演示)
二、硬件接线说明
⚠️重点提醒:所有器件 GND 必须共地,否则 ADC 采样数值乱跳、测量失效!STM32 的 PA1 模拟输入引脚最大耐压 3.3V,传感器 5V 供电,需要确认 OUT 输出不能超过 3.3V,超压需要增加分压 / 限压电路,软件无法保护单片机不被过压烧毁。
| 模块引脚 | STM32F103C8T6 引脚 | 功能说明 |
|---|---|---|
| WaterSensor OUT | PA1 | 模拟信号输出,ADC1 通道 1 输入 |
| WaterSensor VCC | 5V | 水位传感器供电 |
| WaterSensor GND | GND | 信号参考地 |
| OLED SCL | PB8 | I2C 时钟(软件 I2C) |
| OLED SDA | PB9 | I2C 数据(软件 I2C) |
| OLED VCC | 5V | OLED 供电 |
| OLED GND | GND | OLED 信号地 |
三、WaterSensor 水位传感器工作原理
WaterSensor 模块探测面上排布很多交错裸露导电电极。
- 传感器干燥状态:电极之间几乎断路;
- 当水淹没探测电极,水中存在大量离子,会让相邻电极之间形成导电通路;
- 被水覆盖的电极面积越大,电极间导电能力越强,模块 OUT 引脚输出模拟电压就越高;
❗关键点:传感器本质检测的是浸水带来的导电变化,不是直接测量水的物理高度。
- 纯净水、蒸馏水离子少,导电弱,同等浸水深度输出电压偏低;
- 自来水、盐水离子多,导电性强,同等浸水深度输出电压偏高;
- 传感器倾斜、水面气泡、表面残留水膜都会改变读数。
四、STM32 ADC 采样原理
STM32F103 内置 12 位 ADC,本次使用 PA1,对应 ADC1 通道 1。
- ADC 参考电压:0 ~ 3.3V
- 12 位 ADC 输出数值范围:0 ~ 4095
- 电压换算公式:
引脚电压(mV) = ADC原始值 × 3300 / 4095
| PA1 引脚电压 | 对应 ADC 数值参考 |
|---|---|
| 0V | 0 |
| 1.0V | 1241 |
| 1.65V | 2048 |
| 2.0V | 2482 |
| 2.8V | 3475 |
| 3.3V | 4095 |
ADC 初始化完成工作:
- 开启 GPIOA、ADC1 外设时钟;
- PA1 设置为模拟输入模式;
- ADC 时钟配置为 PCLK2 六分频;
- 设置为独立模式、单通道单次转换;
- 选择通道 1,配置较长采样时间,适配高阻抗模拟信号;
- 使能 ADC,执行复位与自校准。
因为水位变化速度很慢,长采样时间不会影响实验,还可以让采样结果更稳定。
五、软件滤波逻辑
水位传感器输出很容易受水面晃动、电源干扰,单次 ADC 采样数值会不停跳动,显示效果很差。 本实验采用10 次采样取平均值做简单滤波:
- 循环启动 ADC 转换,读取 10 次 ADC 原始值;
- 将 10 次结果求和,除以 10,得到 ADC 平均值;
- 使用平均值去换算电压、水位百分比;
OLED 界面大约 200ms 刷新一次,兼顾数据稳定性和响应速度。
六、水位标定与百分比计算
传感器输出和水位并不是严格线性,程序使用三点分段线性标定 ,而不是简单直接 ADC/4095。
在头文件中定义三个标定 ADC 参数:
- 空水位(干燥)ADC 值 → 0% 水位
- 半水位 ADC 值 → 50% 水位
- 满水位 ADC 值 →100% 水位
分段换算逻辑:
- 空水位 ≤ ADC ≤ 半水位:
百分比 = (ADC - 空水位ADC) × 50 / (半水位ADC - 空水位ADC) - 半水位 < ADC < 满水位:
百分比 = 50 + (ADC - 半水位ADC) × 50 / (满水位ADC - 半水位ADC)
- ADC 低于空水位标定值,强制输出 0%;
- ADC 高于满水位标定值,强制输出 100%。
✅实际标定操作步骤
- 传感器完全干燥,记录 OLED 上显示 ADC 值,填入空水位参数;
- 将传感器竖直放入水中,淹没一半探测电极,等待读数稳定,记录 ADC 作为半水位参数;
- 浸水到满探测电极位置,稳定后记录 ADC 作为满水位参数;
- 修改头文件中的宏定义,重新编译烧录;
- 分别测试干燥、半淹、满淹,确认百分比显示 0%、50%、100%。
标定一定要使用实验时相同的水质、供电、容器、传感器摆放角度,否则测量结果会出现偏差。
水位等级划分
程序根据百分比划分 4 个状态:
- DRY:0%
- LOW:1% ~ 29%
- MEDIUM:30% ~ 69%
- HIGH:70% ~ 100%
七、OLED 屏幕显示效果
OLED 屏幕输出示例:
cpp
WATER SENSOR
ADC:2482 /4095
PIN:2.000 V
LEVEL:050 %
STATE:MEDIUM
参数说明:
- ADC:滤波之后的 12 位 ADC 平均值,范围 0‑4095
- PIN:换算后的 PA1 引脚输入电压
- LEVEL:经过标定计算的相对水位百分比,不是实际水深毫米数
- STATE:DRY / LOW / MEDIUM / HIGH 四种水位状态
八、源代码存放区
包含WaterSensor.c、WaterSensor.h、main.c 源码。
main.c代码
cpp
/*
* STM32F103C8T6 + WaterSensor water-level teaching demonstration
*
* WaterSensor OUT -> PA1 (ADC1 channel 1)
* OLED SCL -> PB8
* OLED SDA -> PB9
*/
#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "WaterSensor.h"
#define DISPLAY_UPDATE_MS 200u
static void Demo_ShowStartup(void);
static void Demo_ShowWaterData(const WaterSensor_Data *data);
static u8 *Demo_GetLevelText(WaterSensor_Level level);
int main(void)
{
WaterSensor_Data water_data;
delay_init();
NVIC_Configuration();
OLED_Init();
OLED_ColorTurn(0u);
OLED_DisplayTurn(0u);
Demo_ShowStartup();
WaterSensor_Init();
delay_ms(500u);
while (1)
{
WaterSensor_GetData(&water_data);
Demo_ShowWaterData(&water_data);
delay_ms(DISPLAY_UPDATE_MS);
}
}
static void Demo_ShowStartup(void)
{
OLED_Clear();
OLED_ShowString(0u, 0u, (u8 *)"WATER SENSOR", 16u);
OLED_ShowString(0u, 20u, (u8 *)"OUT: PA1 / ADC1", 12u);
OLED_ShowString(0u, 36u, (u8 *)"OLED: PB8 PB9", 12u);
OLED_ShowString(0u, 52u, (u8 *)"INITIALIZING...", 12u);
OLED_Refresh();
}
static void Demo_ShowWaterData(const WaterSensor_Data *data)
{
OLED_Clear();
OLED_ShowString(0u, 0u, (u8 *)"WATER SENSOR", 12u);
OLED_ShowString(0u, 13u, (u8 *)"ADC:", 12u);
OLED_ShowNum(30u, 13u, data->raw, 4u, 12u);
OLED_ShowString(60u, 13u, (u8 *)"/4095", 12u);
OLED_ShowString(0u, 26u, (u8 *)"PIN:", 12u);
OLED_ShowNum(30u, 26u, data->voltage_mv / 1000u, 1u, 12u);
OLED_ShowString(36u, 26u, (u8 *)".", 12u);
OLED_ShowNum(42u, 26u, data->voltage_mv % 1000u, 3u, 12u);
OLED_ShowString(60u, 26u, (u8 *)" V", 12u);
OLED_ShowString(0u, 39u, (u8 *)"LEVEL:", 12u);
OLED_ShowNum(42u, 39u, data->percent, 3u, 12u);
OLED_ShowString(60u, 39u, (u8 *)" %", 12u);
OLED_ShowString(0u, 52u, (u8 *)"STATE:", 12u);
OLED_ShowString(42u, 52u, Demo_GetLevelText(data->level), 12u);
OLED_Refresh();
}
static u8 *Demo_GetLevelText(WaterSensor_Level level)
{
switch (level)
{
case WATER_SENSOR_LEVEL_LOW:
return (u8 *)"LOW";
case WATER_SENSOR_LEVEL_MEDIUM:
return (u8 *)"MEDIUM";
case WATER_SENSOR_LEVEL_HIGH:
return (u8 *)"HIGH";
case WATER_SENSOR_LEVEL_DRY:
default:
return (u8 *)"DRY";
}
}
WaterSensor.c代码
cpp
#include "WaterSensor.h"
/* 最近一次完整采样得到的 ADC 原始值。 */
u16 WaterSensor_Value = 0u;
/* 最近一次完整采样换算得到的 PA1 输入电压,单位为毫伏。 */
u16 WaterSensor_VoltageMv = 0u;
/* 最近一次完整采样换算得到的水位百分比。 */
u8 WaterSensor_Percent = 0u;
/* 驱动初始化标志:0 表示未初始化,1 表示已经初始化。 */
static u8 water_sensor_initialized = 0u;
/*
* 函数作用:初始化水位传感器使用的 GPIO 和 ADC。
* 配置说明:PA1 设置为模拟输入,使用 ADC1 通道 1,软件触发单次转换。
*/
void WaterSensor_Init(void)
{
GPIO_InitTypeDef gpio_init;
ADC_InitTypeDef adc_init;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
/* 当前工程 PCLK2 为 72MHz,六分频后 ADC 时钟为 12MHz。 */
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
gpio_init.GPIO_Pin = GPIO_Pin_1;
gpio_init.GPIO_Mode = GPIO_Mode_AIN;
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_init);
ADC_DeInit(ADC1);
adc_init.ADC_Mode = ADC_Mode_Independent;
adc_init.ADC_ScanConvMode = DISABLE;
adc_init.ADC_ContinuousConvMode = DISABLE;
adc_init.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
adc_init.ADC_DataAlign = ADC_DataAlign_Right;
adc_init.ADC_NbrOfChannel = 1u;
ADC_Init(ADC1, &adc_init);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1u, ADC_SampleTime_239Cycles5);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1) != RESET)
{
}
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) != RESET)
{
}
water_sensor_initialized = 1u;
}
/* 函数作用:查询 WaterSensor_Init() 是否已经执行。 */
u8 WaterSensor_IsInitialized(void)
{
return water_sensor_initialized;
}
/*
* 函数作用:启动一次软件触发 ADC 转换并返回转换结果。
* 使用说明:如果驱动尚未初始化,本函数会自动调用 WaterSensor_Init()。
*/
u16 WaterSensor_ReadAdc(void)
{
if (water_sensor_initialized == 0u)
{
WaterSensor_Init();
}
ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)
{
}
return ADC_GetConversionValue(ADC1);
}
/*
* 函数作用:连续采集 sample_count 次 ADC 并计算平均值。
* 平均采样可以减小电源纹波和传感器输出抖动,使 OLED 显示更加稳定。
*/
u16 WaterSensor_ReadAdcAverage(u8 sample_count)
{
u32 sum;
u8 i;
if (sample_count == 0u)
{
return 0u;
}
sum = 0u;
for (i = 0u; i < sample_count; i++)
{
sum += WaterSensor_ReadAdc();
}
return (u16)(sum / sample_count);
}
/* 函数作用:按默认次数采集 ADC,并保存最近一次 ADC 平均值。 */
u16 WaterSensor_ReadRaw(void)
{
WaterSensor_Value = WaterSensor_ReadAdcAverage(WATER_SENSOR_SAMPLE_COUNT);
return WaterSensor_Value;
}
/*
* 函数作用:将 12 位 ADC 原始值换算为 PA1 引脚电压。
* 换算公式:电压(mV) = ADC值 × 3300 ÷ 4095。
*/
u16 WaterSensor_ConvertToVoltageMv(u16 adc_value)
{
if (adc_value > WATER_SENSOR_ADC_MAX_VALUE)
{
adc_value = WATER_SENSOR_ADC_MAX_VALUE;
}
return (u16)(((u32)adc_value * WATER_SENSOR_ADC_REFERENCE_MV +
(WATER_SENSOR_ADC_MAX_VALUE / 2u)) /
WATER_SENSOR_ADC_MAX_VALUE);
}
/* 函数作用:采集一次完整数据并返回 PA1 引脚电压。 */
u16 WaterSensor_ReadVoltageMv(void)
{
WaterSensor_Data data;
(void)WaterSensor_ReadData(&data);
return data.voltage_mv;
}
/*
* 函数作用:根据空水位、半水位和满水位三个标定点计算水位百分比。
* 换算方式:空水位到半水位映射为 0%~50%,半水位到满水位映射为
* 50%~100%;超出标定范围时限制在 0% 或 100%。
*/
u8 WaterSensor_ConvertToPercent(u16 adc_value)
{
u32 percent;
if (adc_value <= WATER_SENSOR_DRY_ADC)
{
return 0u;
}
if (adc_value >= WATER_SENSOR_FULL_ADC)
{
return 100u;
}
if (adc_value <= WATER_SENSOR_HALF_ADC)
{
percent = ((u32)(adc_value - WATER_SENSOR_DRY_ADC) * 50u +
((WATER_SENSOR_HALF_ADC - WATER_SENSOR_DRY_ADC) / 2u)) /
(WATER_SENSOR_HALF_ADC - WATER_SENSOR_DRY_ADC);
}
else
{
percent = 50u +
((u32)(adc_value - WATER_SENSOR_HALF_ADC) * 50u +
((WATER_SENSOR_FULL_ADC - WATER_SENSOR_HALF_ADC) / 2u)) /
(WATER_SENSOR_FULL_ADC - WATER_SENSOR_HALF_ADC);
}
return (u8)percent;
}
/* 函数作用:采集一次完整数据并返回水位百分比。 */
u8 WaterSensor_ReadPercent(void)
{
WaterSensor_Data data;
(void)WaterSensor_ReadData(&data);
return data.percent;
}
/* 函数作用:根据水位百分比划分 OLED 上显示的水位状态。 */
WaterSensor_Level WaterSensor_ConvertToLevel(u8 percent)
{
if (percent == 0u)
{
return WATER_SENSOR_LEVEL_DRY;
}
if (percent < WATER_SENSOR_LOW_LIMIT)
{
return WATER_SENSOR_LEVEL_LOW;
}
if (percent < WATER_SENSOR_HIGH_LIMIT)
{
return WATER_SENSOR_LEVEL_MEDIUM;
}
return WATER_SENSOR_LEVEL_HIGH;
}
/*
* 函数作用:获取 ADC、电压、水位百分比和水位状态组成的完整数据。
* 参数说明:data 必须指向有效的 WaterSensor_Data 变量。
* 返回说明:读取成功返回 WATER_SENSOR_OK,空指针返回参数错误。
*/
u8 WaterSensor_ReadData(WaterSensor_Data *data)
{
u16 raw;
u16 voltage_mv;
u8 percent;
if (data == 0)
{
return WATER_SENSOR_ERROR_ARGUMENT;
}
raw = WaterSensor_ReadAdcAverage(WATER_SENSOR_SAMPLE_COUNT);
voltage_mv = WaterSensor_ConvertToVoltageMv(raw);
percent = WaterSensor_ConvertToPercent(raw);
data->raw = raw;
data->voltage_mv = voltage_mv;
data->percent = percent;
data->level = WaterSensor_ConvertToLevel(percent);
WaterSensor_Value = raw;
WaterSensor_VoltageMv = voltage_mv;
WaterSensor_Percent = percent;
return WATER_SENSOR_OK;
}
/* 函数作用:为 OLED 教学主程序提供不需要处理返回值的简化读取接口。 */
void WaterSensor_GetData(WaterSensor_Data *data)
{
(void)WaterSensor_ReadData(data);
}
/* 函数作用:兼容参考资料中的 Water_Init() 初始化接口。 */
void Water_Init(void)
{
WaterSensor_Init();
}
/* 函数作用:兼容参考资料中的单次水位百分比读取接口。 */
float Water_read(void)
{
return (float)WaterSensor_ReadPercent();
}
/*
* 函数作用:兼容参考资料中的多次平均读取接口。
* 参数说明:times 为采样次数;小于或等于 0 时按 1 次采样处理。
*/
float Water_out(int times)
{
u32 sum;
int i;
if (times <= 0)
{
times = 1;
}
sum = 0u;
for (i = 0; i < times; i++)
{
sum += WaterSensor_ReadAdc();
}
WaterSensor_Value = (u16)(sum / (u32)times);
WaterSensor_VoltageMv = WaterSensor_ConvertToVoltageMv(WaterSensor_Value);
WaterSensor_Percent = WaterSensor_ConvertToPercent(WaterSensor_Value);
return (float)WaterSensor_Percent;
}
WaterSensor.h代码
cpp
#ifndef __WATER_SENSOR_H
#define __WATER_SENSOR_H
/*
* 文件名称:WaterSensor.h
* 文件编码:UTF-8
* 功能说明:WaterSensor 模拟水位传感器驱动接口。
*
* 硬件连接:
* WaterSensor OUT -> STM32F103C8T6 PA1(ADC1 通道 1)
*
* 水位百分比采用空水位、半水位和满水位三个标定点进行分段线性换算。
* 默认标定值来自参考程序:空水位为 0V、半水位为 2.0V、满水位为
* 2.8V。不同传感器的输出可能存在偏差,实际使用时应根据测量结果修改
* WATER_SENSOR_DRY_ADC、WATER_SENSOR_HALF_ADC 和 WATER_SENSOR_FULL_ADC。
*
* 注意:STM32F103 的 ADC 输入电压不能超过 3.3V。如果传感器 OUT
* 可能高于 3.3V,必须先经过分压或限压电路再连接 PA1。
*/
#include "stm32f10x.h"
#include "stm32f10x_adc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
/* STM32F103 内部 ADC 为 12 位,最大转换结果为 4095。 */
#define WATER_SENSOR_ADC_MAX_VALUE 4095u
/* ADC 参考电压,单位为毫伏;这里表示单片机 ADC 电压,不是模块供电电压。 */
#define WATER_SENSOR_ADC_REFERENCE_MV 3300u
/* 每次获取水位数据时的平均采样次数,增加次数可以让显示结果更稳定。 */
#define WATER_SENSOR_SAMPLE_COUNT 10u
/* 三点标定值:空水位、半水位和满水位对应的 ADC 原始值。 */
#define WATER_SENSOR_DRY_ADC 0u
#define WATER_SENSOR_HALF_ADC 2482u
#define WATER_SENSOR_FULL_ADC 3475u
/* 水位状态分界值:1%~29% 为低水位,30%~69% 为中水位,70%~100% 为高水位。 */
#define WATER_SENSOR_LOW_LIMIT 30u
#define WATER_SENSOR_HIGH_LIMIT 70u
/* 函数执行状态码。 */
#define WATER_SENSOR_OK 0u
#define WATER_SENSOR_ERROR_ARGUMENT 1u
/* OLED 显示所使用的水位状态。 */
typedef enum
{
WATER_SENSOR_LEVEL_DRY = 0, /* 无水或水位百分比为 0%。 */
WATER_SENSOR_LEVEL_LOW, /* 低水位。 */
WATER_SENSOR_LEVEL_MEDIUM, /* 中水位。 */
WATER_SENSOR_LEVEL_HIGH /* 高水位。 */
} WaterSensor_Level;
/* 一次完整采样所包含的水位数据。 */
typedef struct
{
u16 raw; /* 多次采样后的 ADC 平均值,范围为 0~4095。 */
u16 voltage_mv; /* PA1 引脚输入电压,单位为毫伏。 */
u8 percent; /* 标定后的水位百分比,范围为 0~100。 */
WaterSensor_Level level; /* 根据百分比划分的水位状态。 */
} WaterSensor_Data;
/* 最近一次完整采样结果,方便在 Keil 调试窗口或旧例程中直接观察。 */
extern u16 WaterSensor_Value;
extern u16 WaterSensor_VoltageMv;
extern u8 WaterSensor_Percent;
/* 初始化 PA1、ADC1,并完成 ADC 复位和校准。 */
void WaterSensor_Init(void);
/* 查询驱动是否已经完成初始化,返回 1 表示已初始化。 */
u8 WaterSensor_IsInitialized(void);
/* 启动一次 ADC 软件转换并返回 12 位原始结果。 */
u16 WaterSensor_ReadAdc(void);
/* 连续读取指定次数并返回平均值;sample_count 为 0 时返回 0。 */
u16 WaterSensor_ReadAdcAverage(u8 sample_count);
/* 按默认采样次数读取 ADC 平均值,并更新原始值缓存。 */
u16 WaterSensor_ReadRaw(void);
/* 将 ADC 原始值换算为 PA1 输入电压,单位为毫伏。 */
u16 WaterSensor_ConvertToVoltageMv(u16 adc_value);
/* 采集并返回 PA1 输入电压,同时更新全部采样缓存。 */
u16 WaterSensor_ReadVoltageMv(void);
/* 根据三个标定点将 ADC 原始值换算为 0~100% 水位。 */
u8 WaterSensor_ConvertToPercent(u16 adc_value);
/* 采集并返回水位百分比,同时更新全部采样缓存。 */
u8 WaterSensor_ReadPercent(void);
/* 根据水位百分比判断无水、低、中、高四种水位状态。 */
WaterSensor_Level WaterSensor_ConvertToLevel(u8 percent);
/* 读取一组完整数据;参数为空时返回 WATER_SENSOR_ERROR_ARGUMENT。 */
u8 WaterSensor_ReadData(WaterSensor_Data *data);
/* 读取完整数据的简化接口,供当前 OLED 教学演示调用。 */
void WaterSensor_GetData(WaterSensor_Data *data);
/* 以下三个函数用于兼容参考资料中的原有调用方式。 */
void Water_Init(void);
float Water_read(void);
float Water_out(int times);
#endif
九、实物演示章节
9.1 演示准备工作
- 按照接线表完成硬件接线,仔细确认全部 GND 共地;
- 将程序烧录进 STM32,上电,OLED 成功点亮,确认屏幕正常输出;
- 准备一杯自来水,固定传感器尽量保持竖直,不要晃动。
9.2 演示步骤
- 干燥状态演示:传感器悬空干燥,观察屏幕:ADC 数值偏低,LEVEL 为 0%,STATE 显示 DRY;
- 缓慢浸入水中:手拿传感器,缓慢垂直放入水杯,可以看到 ADC 数值不断变大、PIN 电压上升,LEVEL 百分比逐步上涨,状态从 LOW 变为 MEDIUM 再到 HIGH;
- 晃动水面对比滤波效果:手晃动水杯让水面波动,可以看到 ADC 数值小幅抖动,但经过 10 次平均滤波,屏幕百分比不会剧烈乱跳,体现软件滤波作用;
- 半水位、满水位验证标定:将电极刚好淹没一半,观察百分比接近 50%;完全淹没探测区,百分比接近 100%;
- 残留水膜现象演示:把传感器拿出水面,表面会附着一层水膜,此时不会立刻回到 0%,等待擦干电极后,数值才恢复 DRY,演示水膜对测量的干扰;
- 对比不同水质(拓展演示):可以准备纯净水,同样浸水深度,可以观察到相比自来水,ADC 读数明显变低,说明水质离子浓度会直接影响测量结果。

9.3 演示结束后注意事项
- 断电之后再拿传感器,不要带电长时间泡在水里,避免电极电化学腐蚀;
- 使用清水冲洗探测电极,擦干存放;
- 千万不要让模块排针、电路板部分接触到水。
十、实验注意事项与局限
- 共地不可忽略:传感器、单片机、OLED 地必须接在一起,没有统一参考地 ADC 读数完全不可用;
- 电压防护:PA1 最大只能输入 3.3V,传感器 5V 输出,务必万用表实测 OUT 最大电压,超压必须分压,否则烧坏 STM32;
- 模块局限性 :这是教学演示模块,不是工业液位传感器。
- 水质、气泡、倾斜角度、电极水垢、残留水膜都会影响结果;
- 不适合长期浸泡,带电浸泡电极会腐蚀;
- 如果需要做长期检测,可以增加控制电路,只在采样瞬间给传感器电极供电,降低腐蚀速度。
十一、实验总结
WaterSensor 水位传感器依靠水的离子导电特性,把电极浸水面积转换成模拟电压信号;STM32 单片机通过 ADC 外设把模拟电压转为数字量;通过多次采样平均消除抖动,再使用三点标定换算出相对水位,最终在 OLED 屏幕直观展示水位信息。整个实验完整演示了「模拟信号采集‑ADC 转换‑软件滤波‑标定换算‑结果显示」嵌入式模拟量采集的完整流程。