STM32驱动WaterSensor水位传感器+OLED显示完整原理与实验

前言

本篇基于 STM32F103C8T6 单片机实现水位检测实验,使用 WaterSensor 导电式水位传感器采集水位信号,通过单片机 ADC 采集模拟电压,经过滤波、标定换算得到相对水位百分比,最终把 ADC 原始值、引脚电压、水位占比、水位状态输出到 OLED 屏幕上。

视频讲解:STM32驱动WaterSensor水位传感器+OLED显示完整原理与实验https://www.bilibili.com/video/BV1pqt26rES1/?share_source=copy_web&vd_source=91b967b41a17a5686913521545a8c3fb

注意:该传感器检测的是相对水位,不是直接测量水深,受水质、安装方式影响,适合教学演示,不适合工业高精度液位检测。

实验整体流程简述: 水面接触传感器电极面积改变 → 电极间导电程度改变 → 传感器 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 模块探测面上排布很多交错裸露导电电极。

  1. 传感器干燥状态:电极之间几乎断路;
  2. 当水淹没探测电极,水中存在大量离子,会让相邻电极之间形成导电通路;
  3. 被水覆盖的电极面积越大,电极间导电能力越强,模块 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 初始化完成工作:

  1. 开启 GPIOA、ADC1 外设时钟;
  2. PA1 设置为模拟输入模式;
  3. ADC 时钟配置为 PCLK2 六分频;
  4. 设置为独立模式、单通道单次转换;
  5. 选择通道 1,配置较长采样时间,适配高阻抗模拟信号;
  6. 使能 ADC,执行复位与自校准。

因为水位变化速度很慢,长采样时间不会影响实验,还可以让采样结果更稳定。

五、软件滤波逻辑

水位传感器输出很容易受水面晃动、电源干扰,单次 ADC 采样数值会不停跳动,显示效果很差。 本实验采用10 次采样取平均值做简单滤波:

  1. 循环启动 ADC 转换,读取 10 次 ADC 原始值;
  2. 将 10 次结果求和,除以 10,得到 ADC 平均值;
  3. 使用平均值去换算电压、水位百分比;

OLED 界面大约 200ms 刷新一次,兼顾数据稳定性和响应速度。

六、水位标定与百分比计算

传感器输出和水位并不是严格线性,程序使用三点分段线性标定 ,而不是简单直接 ADC/4095

在头文件中定义三个标定 ADC 参数:

  • 空水位(干燥)ADC 值 → 0% 水位
  • 半水位 ADC 值 → 50% 水位
  • 满水位 ADC 值 →100% 水位

分段换算逻辑:

  1. 空水位 ≤ ADC ≤ 半水位: 百分比 = (ADC - 空水位ADC) × 50 / (半水位ADC - 空水位ADC)
  2. 半水位 < ADC < 满水位: 百分比 = 50 + (ADC - 半水位ADC) × 50 / (满水位ADC - 半水位ADC)
  • ADC 低于空水位标定值,强制输出 0%;
  • ADC 高于满水位标定值,强制输出 100%。

✅实际标定操作步骤

  1. 传感器完全干燥,记录 OLED 上显示 ADC 值,填入空水位参数;
  2. 将传感器竖直放入水中,淹没一半探测电极,等待读数稳定,记录 ADC 作为半水位参数;
  3. 浸水到满探测电极位置,稳定后记录 ADC 作为满水位参数;
  4. 修改头文件中的宏定义,重新编译烧录;
  5. 分别测试干燥、半淹、满淹,确认百分比显示 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

参数说明:

  1. ADC:滤波之后的 12 位 ADC 平均值,范围 0‑4095
  2. PIN:换算后的 PA1 引脚输入电压
  3. LEVEL:经过标定计算的相对水位百分比,不是实际水深毫米数
  4. 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 演示准备工作

  1. 按照接线表完成硬件接线,仔细确认全部 GND 共地;
  2. 将程序烧录进 STM32,上电,OLED 成功点亮,确认屏幕正常输出;
  3. 准备一杯自来水,固定传感器尽量保持竖直,不要晃动。

9.2 演示步骤

  1. 干燥状态演示:传感器悬空干燥,观察屏幕:ADC 数值偏低,LEVEL 为 0%,STATE 显示 DRY;
  2. 缓慢浸入水中:手拿传感器,缓慢垂直放入水杯,可以看到 ADC 数值不断变大、PIN 电压上升,LEVEL 百分比逐步上涨,状态从 LOW 变为 MEDIUM 再到 HIGH;
  3. 晃动水面对比滤波效果:手晃动水杯让水面波动,可以看到 ADC 数值小幅抖动,但经过 10 次平均滤波,屏幕百分比不会剧烈乱跳,体现软件滤波作用;
  4. 半水位、满水位验证标定:将电极刚好淹没一半,观察百分比接近 50%;完全淹没探测区,百分比接近 100%;
  5. 残留水膜现象演示:把传感器拿出水面,表面会附着一层水膜,此时不会立刻回到 0%,等待擦干电极后,数值才恢复 DRY,演示水膜对测量的干扰;
  6. 对比不同水质(拓展演示):可以准备纯净水,同样浸水深度,可以观察到相比自来水,ADC 读数明显变低,说明水质离子浓度会直接影响测量结果。

9.3 演示结束后注意事项

  1. 断电之后再拿传感器,不要带电长时间泡在水里,避免电极电化学腐蚀;
  2. 使用清水冲洗探测电极,擦干存放;
  3. 千万不要让模块排针、电路板部分接触到水。

十、实验注意事项与局限

  1. 共地不可忽略:传感器、单片机、OLED 地必须接在一起,没有统一参考地 ADC 读数完全不可用;
  2. 电压防护:PA1 最大只能输入 3.3V,传感器 5V 输出,务必万用表实测 OUT 最大电压,超压必须分压,否则烧坏 STM32;
  3. 模块局限性 :这是教学演示模块,不是工业液位传感器。
    • 水质、气泡、倾斜角度、电极水垢、残留水膜都会影响结果;
    • 不适合长期浸泡,带电浸泡电极会腐蚀;
  4. 如果需要做长期检测,可以增加控制电路,只在采样瞬间给传感器电极供电,降低腐蚀速度。

十一、实验总结

WaterSensor 水位传感器依靠水的离子导电特性,把电极浸水面积转换成模拟电压信号;STM32 单片机通过 ADC 外设把模拟电压转为数字量;通过多次采样平均消除抖动,再使用三点标定换算出相对水位,最终在 OLED 屏幕直观展示水位信息。整个实验完整演示了「模拟信号采集‑ADC 转换‑软件滤波‑标定换算‑结果显示」嵌入式模拟量采集的完整流程。

相关推荐
子文雨1 小时前
基于 uC3845 与 ATtiny13 双芯片架构的 8.4V/2.5Ah 锂电池智能充电器设计
单片机·嵌入式硬件·算法
快秃头的码农2 小时前
esp32-s3移植lvgl总结
嵌入式硬件·esp32-s3
恒锐丰科技林技术员3 小时前
EG1191 宽压降压 DC‑DC 芯片解析:高性价比工业电源方案
经验分享·嵌入式硬件·硬件工程
千秋岁rr3 小时前
8TIM编码器接口
stm32
智购科技无人售货机工厂店4 小时前
2026自动售货机大屏交互设计原则:从信息层级到视觉动线的工程实践~YH
运维·python·单片机·嵌入式硬件·自动化·交互
艾芯微科技7 小时前
SKY13370‑374LF|0.5‑6GHz SPDT 射频开关,WLAN/LTE 射频前端实用器件
网络·单片机·嵌入式硬件·集成测试·51单片机
jianqiang.xue8 小时前
收官:从v0.1到“研发新常态“,一套嵌入式智能体的落地与边界
stm32·单片机·物联网·架构·esp32
HUI-4748 小时前
EG1131|SOT23‑6 30V/1.5A 电流模式同步降压 DC‑DC,小体积消费类电源优选
嵌入式硬件·硬件工程
天空'之城9 小时前
单片机基础核心知识点汇总(三十)
单片机·嵌入式硬件