欢迎光临
我们一直在努力

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

前言

本篇文章基于 STM32F103C8T6 最小系统板,搭配 HS‑S37A 接触式电阻型水位传感器以及 OLED 显示屏完成水位检测实验。 HS‑S37A 会把探头浸水程度转化为模拟电压信号,STM32 通过 ADC 采集该电压,经过多次平均滤波、三点分段校准之后,最终在 OLED 屏幕上输出 ADC 采样值、引脚电压、相对水位百分比以及水位状态。本实验偏向教学演示,适合单片机入门学习传感器 AD 采集、滤波、数据校准相关知识点。

视频讲解:STM32驱动HS‑S37A水位传感器+OLED完整实验讲解https://www.bilibili.com/video/BV1bMtB6bESD/?share_source=copy_web&vd_source=91b967b41a17a5686913521545a8c3fb

一、实验实现整体流程

整体工作逻辑: 水位升高或者降低 → 探头浸入水中导电检测条数量改变 → HS‑S37A 模块 OUT 引脚输出模拟电压改变 → STM32 PA0(ADC1 通道 0)采集模拟量 → 16 次平均滤波得到稳定 ADC 数值 → 三点分段校准换算得到相对水位百分比 → OLED 屏幕刷新显示各项测量数据。

简单概括:HS‑S37A 把浸水深浅转换成模拟电压,STM32 把模拟电压转为数字量,经过滤波校准,输出相对水位信息到 OLED。

二、硬件接线说明

⚠️重点提醒:所有模块必须共地,否则 ADC 采样数值乱跳、数据异常。电压是电位差,GND 不连通,单片机无法正确识别 OUT 引脚电压。

模块引脚STM32F103C8T6 引脚功能说明
HS‑S37A VCC 5V 水位传感器供电
HS‑S37A OUT PA0 模拟信号输出,接入 ADC1 通道 0
HS‑S37A GND GND 公共地
OLED VCC 5V OLED 供电
OLED SCL PB8 软件 I2C 时钟
OLED SDA PB9 软件 I2C 数据
OLED GND GND OLED 电源地

OUT 引脚电压保护

STM32F103C8T6 的 ADC 参考电压为 3.3V,PA0 引脚输入模拟电压最大不能超过 3.3V。 HS‑S37A 使用 5V 供电时,OUT 输出电压有可能超过 3.3V,会损坏单片机 ADC 外设。 如果无法确认模块最大输出电压,建议增加分压电路:

HS‑S37A OUT →10K 电阻 →PA0 引脚;PA0 引脚同时接 20K 电阻到 GND。 该分压电路可以把最高 5V 输入降压至约 3.33V,适配本实验采样。 注意:OLED 上面显示的 PIN 电压,是分压之后 PA0 引脚电压,不是 HS‑S37A 原始 OUT 输出电压。

如果不想增加分压电路,则单独对模块供5V的电,然后测量满量程时,模块OUT引脚的输出电压,如果该输出电压大于3.3V,那建议使用3.3V供电,如果满量程下输出电压小于3.3V供电,则可使用5V供电。

三、HS‑S37A 传感器工作原理

HS‑S37A 属于接触式电阻型模拟水位传感器。 传感器探头表面有多条交错裸露导电走线。干燥状态下走线之间几乎不导通;当探头浸入导电液体,水会在走线之间形成导电通路。

水位越高,被水浸没的导电走线就越多,等效导电能力变强,模块 OUT 引脚输出模拟电压随之变大。

✔关键点:该传感器检测的不是水压!依靠的是水的导电特性。和工业压力式液位传感器、超声波液位计原理完全不一样。

简单信号流向:水位变化 →浸水导电条数量变化 →等效导电能力变化 →OUT 模拟电压改变 →STM32 ADC 采集 →滤波校准 →OLED 显示水位。

注意:纯净水导电能力很差,尽量使用自来水做实验,纯净水会导致测量效果很差。

四、STM32 ADC 采样原理

STM32F103C8T6 内置 12 位 ADC,本次使用 PA0 对应 ADC1 通道 0。

  • 输入电压范围:0V ~ 3.3V
  • ADC 转换输出数字范围:0 ~ 4095
  • 引脚电压计算公式:引脚电压 = ADC原始值 × 3.3V / 4095

本次实验实测参考数据:

水位位置ADC 原始值理论计算电压OLED 实际显示电压
一半量程 1076 约 0.867V 约 0.874V
满量程 1636 约 1.318V 约 1.3V

理论和实测电压存在微小偏差属于正常现象,来源包括 3.3V 电源精度、ADC 量化误差、传感器输出波动、采样时刻数据变化。 程序内部使用整数运算完成 ADC 转毫伏,避免单片机消耗资源运行浮点运算。

五、采样与平均滤波逻辑

水面晃动、电源噪声、液体接触状态改变,都会造成 ADC 采样数值不停跳动。如果直接使用单次采样值显示,OLED 上面数字会不停闪烁抖动。

本实验采用16 次采样取平均值滤波方案: 平均ADC值 = 16次ADC采样总和 ÷16

执行逻辑:

  • 启动 ADC 转换,读取一次 PA0 通道 ADC 原始值
  • 判断采样次数是否达到 16 次,没有达到就继续读取
  • 采集满 16 次之后,计算 ADC 平均值
  • 将平均值换算引脚电压
  • 三点校准得到相对水位百分比
  • 判断水位状态,刷新 OLED 屏幕
  • 对应工程关键函数说明

    文件函数名功能
    HS_S37A.c HS_S37A_Init() 初始化 PA0、ADC1 通道 0,执行 ADC 自校准
    HS_S37A.c HS_S37A_ReadRaw() 获取单次 ADC 原始采样值
    HS_S37A.c HS_S37A_ReadAverage() 16 次采样求平均滤波
    HS_S37A.c HS_S37A_RawToMilliVolts() ADC 数值转换为毫伏电压
    HS_S37A.c HS_S37A_SetCalibration() 设置干燥、半量程、满量程三点校准参数
    HS_S37A.c HS_S37A_RawToPercent() 三点分段插值计算相对水位百分比
    HS_S37A.c HS_S37A_GetLevel() 根据百分比划分四种水位状态
    main.c Demo_ShowWaterData() OLED 显示全部测量数据

    六、三点分段校准原理

    为什么不能只用一个满量程点做线性换算

    最开始直接使用单一直线换算水位,实际测试发现误差巨大。 例如 ADC=1076,探头实际已经到一半水位,旧算法只算出 36%;ADC=1636 探头已经满水位,旧算法仅仅输出 54%。

    原因:传感器 ADC 输出和实际浸水深度不是简单线性关系。水的导电率、电极走线形状、浸水面积都会改变输出特性。因此采用三点分段校准。

    预设校准参数:

    • 干燥点 ADC:0
    • 半量程 ADC:1076
    • 满量程 ADC:1636

    两段映射关系:

  • ADC 0 ~1076 →映射水位百分比 0% ~50%
  • ADC 1076 ~1636 →映射水位百分比 50% ~100%
  • ADC 大于等于 1636,直接限制输出 100%
  • 第一段计算公式 百分比 = (当前 ADC – 干燥 ADC) ×50 ÷ (半量程 ADC – 干燥 ADC)

    第二段计算公式 百分比 =50 + (当前 ADC‑半量程 ADC) ×50 ÷ (满量程 ADC‑半量程 ADC)

    重新校准操作

    更换传感器、更换液体、更换容器,必须重新采集三个点位 ADC 数值,调用接口更新校准参数。

    //函数调用示例
    HS_S37A_SetCalibration(干燥ADC,半量程ADC,满量程ADC);
    //本实验默认参数示例
    HS_S37A_SetCalibration(0u, 1076u, 1636u);

    要求三个参数必须从小到大,最大值不超过 4095。函数返回 1 代表设置成功;返回 0 代表参数非法,保留原有校准参数。

    ⚠️注意:三点校准只是分段线性逼近,输出的是教学演示用相对水位百分比,不是精确水深厘米数值,不能当做高精度液位测量。

    七、OLED 屏幕显示内容与水位状态判定

    OLED 屏幕输出示例:

    HS‑S37A WATER
    ADC:1076/4095
    PIN:0.867 V
    LEVEL:050 %
    STATE:MEDIUM

    字段含义:

    显示字段说明
    ADC 经过 16 次滤波之后 ADC 采样值,最大值 4095
    PIN PA0 引脚换算得到电压
    LEVEL 三点校准输出相对水位百分比
    STATE 划分的水位状态

    水位状态划分:

    水位百分比状态字符串含义
    0% ~5% DRY 探头干燥
    6% ~35% LOW 低水位
    36% ~89% MEDIUM 中等水位
    90% ~100% HIGH 高水位 / 满量程

    八、实物演示

    演示操作步骤

  • 按照硬件接线表完成接线,确认全部模块 GND 共地,检查分压电路(如果使用)。
  • 烧录完整工程代码到 STM32F103C8T6 开发板,上电。此时探头悬空干燥,OLED 显示 DRY 干燥状态。
  • 手持传感器探头,缓慢垂直放入装自来水容器,观察屏幕 ADC 数值、PIN 电压持续上涨,LEVEL 百分比同步上升。
  • 探头放到一半浸水位置,可以看到 ADC 稳定在 1076 附近,LEVEL 显示 50%,状态 MEDIUM。可以观察如果不开启滤波,数值会来回跳动;开启 16 次平均滤波后,读数会变得稳定。
  • 继续把探头浸到满量程,ADC 来到 1636,LEVEL 输出 100%,状态切换为 HIGH。
  • 将探头拿出水面,水位慢慢回落,状态依次切换 MEDIUM→LOW→DRY。
  • 晃动容器制造水面波动,可以看到 ADC 数值小幅波动,滤波之后百分比不会剧烈跳变。
  • 演示现象总结

  • 探头浸水越多,ADC 采样数值越大;
  • 滤波可以抑制水面抖动带来的数据跳动;
  • 三点校准修正传感器非线性,让百分比和实际浸水程度匹配;
  • 如果换成纯净水,同样浸水深度 ADC 数值会明显变小,印证传感器依赖水的导电性。
  • 实验结束注意事项:断电,把探头擦拭干净保持干燥。电极长期泡水通电会发生电化学腐蚀,会造成传感器读数漂移损坏。

    九、源代码专区

    💡这里包括头文件、c 文件、main 函数。

    main.c代码

    /*
    * STM32F103C8T6 + HS-S37A water-level teaching demonstration
    *
    * HS-S37A OUT -> PA0 (ADC1 channel 0)
    * OLED SCL -> PB8
    * OLED SDA -> PB9
    */
    #include "delay.h"
    #include "sys.h"
    #include "oled.h"
    #include "HS_S37A.h"

    #define DISPLAY_UPDATE_MS 200u

    static void Demo_ShowStartup(void);
    static void Demo_ShowWaterData(const HS_S37A_Data *data);
    static u8 *Demo_GetLevelText(HS_S37A_Level level);

    int main(void)
    {
    HS_S37A_Data water_data;

    delay_init();
    NVIC_Configuration();
    OLED_Init();
    OLED_ColorTurn(0u);
    OLED_DisplayTurn(0u);

    Demo_ShowStartup();
    HS_S37A_Init();
    delay_ms(500u);

    while (1)
    {
    HS_S37A_GetData(&water_data);
    Demo_ShowWaterData(&water_data);
    delay_ms(DISPLAY_UPDATE_MS);
    }
    }

    static void Demo_ShowStartup(void)
    {
    OLED_Clear();
    OLED_ShowString(0u, 0u, (u8 *)"HS-S37A WATER", 16u);
    OLED_ShowString(0u, 20u, (u8 *)"OUT: PA0 / 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 HS_S37A_Data *data)
    {
    OLED_Clear();

    OLED_ShowString(0u, 0u, (u8 *)"HS-S37A WATER", 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(HS_S37A_Level level)
    {
    switch (level)
    {
    case HS_S37A_LEVEL_LOW:
    return (u8 *)"LOW";

    case HS_S37A_LEVEL_MEDIUM:
    return (u8 *)"MEDIUM";

    case HS_S37A_LEVEL_HIGH:
    return (u8 *)"HIGH";

    case HS_S37A_LEVEL_DRY:
    default:
    return (u8 *)"DRY";
    }
    }

    HS_S37A.c代码

    #include "HS_S37A.h"
    #include "stm32f10x_adc.h"
    #include "stm32f10x_gpio.h"
    #include "stm32f10x_rcc.h"

    /* 编译阶段检查默认校准点,防止校准区间颠倒或重合。 */
    #if (HS_S37A_HALF_CALIBRATION <= HS_S37A_DRY_CALIBRATION)
    #error "HS_S37A_HALF_CALIBRATION must be greater than HS_S37A_DRY_CALIBRATION"
    #endif

    #if (HS_S37A_FULL_CALIBRATION <= HS_S37A_HALF_CALIBRATION)
    #error "HS_S37A_FULL_CALIBRATION must be greater than HS_S37A_HALF_CALIBRATION"
    #endif

    /* 当前使用的三个校准点,可由 HS_S37A_SetCalibration() 动态更新。 */
    static uint16_t hs_s37a_dry_raw = HS_S37A_DRY_CALIBRATION;
    static uint16_t hs_s37a_half_raw = HS_S37A_HALF_CALIBRATION;
    static uint16_t hs_s37a_full_raw = HS_S37A_FULL_CALIBRATION;

    /*
    * 函数名称:HS_S37A_Init
    * 功能说明:初始化 PA0 和 ADC1 通道 0,并执行 ADC 自校准。
    */
    void HS_S37A_Init(void)
    {
    GPIO_InitTypeDef gpio_init;
    ADC_InitTypeDef adc_init;

    /* 开启 GPIOA 和 ADC1 的外设时钟。 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |
    RCC_APB2Periph_ADC1, ENABLE);

    /* PCLK2 为 72MHz,6 分频后 ADC 时钟为 12MHz,不超过 14MHz 上限。 */
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);

    /* PA0 配置为模拟输入,关闭数字输入输出电路。 */
    gpio_init.GPIO_Pin = GPIO_Pin_0;
    gpio_init.GPIO_Mode = GPIO_Mode_AIN;
    gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &gpio_init);

    /* ADC1 使用独立、单通道、单次转换和右对齐模式。 */
    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_0, 1u,
    ADC_SampleTime_239Cycles5);

    /* 使能 ADC,并等待复位校准和自校准完成。 */
    ADC_Cmd(ADC1, ENABLE);
    ADC_ResetCalibration(ADC1);
    while (ADC_GetResetCalibrationStatus(ADC1) != RESET)
    {
    }
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1) != RESET)
    {
    }
    }

    /*
    * 函数名称:HS_S37A_SetCalibration
    * 功能说明:动态设置干燥、半量程和满量程三个 ADC 校准点。
    * 返回说明:设置成功返回 1;校准点无效时返回 0。
    */
    uint8_t HS_S37A_SetCalibration(uint16_t dry_raw, uint16_t half_raw,
    uint16_t full_raw)
    {
    /* 校准值必须依次增大,并且不能超过 12 位 ADC 的满量程。 */
    if ((dry_raw >= half_raw) || (half_raw >= full_raw) ||
    (full_raw > HS_S37A_ADC_FULL_SCALE))
    {
    return 0u;
    }

    hs_s37a_dry_raw = dry_raw;
    hs_s37a_half_raw = half_raw;
    hs_s37a_full_raw = full_raw;
    return 1u;
    }

    /* 启动一次软件触发转换,等待转换完成后读取 ADC 原始值。 */
    uint16_t HS_S37A_ReadRaw(void)
    {
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)
    {
    }

    return ADC_GetConversionValue(ADC1);
    }

    /* 对多次 ADC 采样求平均,减小水面波动和电气噪声造成的跳动。 */
    uint16_t HS_S37A_ReadAverage(uint8_t sample_count)
    {
    uint8_t i;
    uint32_t sum;

    /* 采样次数为 0 时自动修正为 1,避免除零。 */
    if (sample_count == 0u)
    {
    sample_count = 1u;
    }

    sum = 0u;
    for (i = 0u; i < sample_count; i++)
    {
    sum += HS_S37A_ReadRaw();
    }

    /* 加上除数的一半,使整数除法结果实现四舍五入。 */
    return (uint16_t)((sum + (sample_count / 2u)) / sample_count);
    }

    /* 根据 3.3V 参考电压,将 12 位 ADC 数值换算成毫伏。 */
    uint16_t HS_S37A_RawToMilliVolts(uint16_t raw)
    {
    if (raw > HS_S37A_ADC_FULL_SCALE)
    {
    raw = HS_S37A_ADC_FULL_SCALE;
    }

    return (uint16_t)(((uint32_t)raw * HS_S37A_ADC_REFERENCE_MV +
    (HS_S37A_ADC_FULL_SCALE / 2u)) /
    HS_S37A_ADC_FULL_SCALE);
    }

    uint8_t HS_S37A_RawToPercent(uint16_t raw)
    {
    uint32_t span;
    uint32_t position;
    uint32_t percent;

    /* 校准区间以外的数值直接限制到 0% 或 100%。 */
    if (raw <= hs_s37a_dry_raw)
    {
    return 0u;
    }
    if (raw >= hs_s37a_full_raw)
    {
    return 100u;
    }

    /*
    * 使用两段线性插值补偿传感器的非线性响应:
    * 干燥点 -> 0%,半量程点 -> 50%,满量程点 -> 100%。
    */
    if (raw <= hs_s37a_half_raw)
    {
    /* 干燥点到半量程点对应 0%~50%。 */
    span = hs_s37a_half_raw – hs_s37a_dry_raw;
    position = raw – hs_s37a_dry_raw;
    percent = (position * 50u + (span / 2u)) / span;
    }
    else
    {
    /* 半量程点到满量程点对应 50%~100%。 */
    span = hs_s37a_full_raw – hs_s37a_half_raw;
    position = raw – hs_s37a_half_raw;
    percent = 50u + (position * 50u + (span / 2u)) / span;
    }

    return (uint8_t)percent;
    }

    /* 按照头文件中定义的百分比边界划分水位状态。 */
    HS_S37A_Level HS_S37A_GetLevel(uint8_t percent)
    {
    if (percent <= HS_S37A_DRY_MAX_PERCENT)
    {
    return HS_S37A_LEVEL_DRY;
    }
    if (percent <= HS_S37A_LOW_MAX_PERCENT)
    {
    return HS_S37A_LEVEL_LOW;
    }
    if (percent <= HS_S37A_MEDIUM_MAX_PERCENT)
    {
    return HS_S37A_LEVEL_MEDIUM;
    }

    return HS_S37A_LEVEL_HIGH;
    }

    /* 读取并整理一次完整的水位数据,供主程序直接显示或处理。 */
    void HS_S37A_GetData(HS_S37A_Data *data)
    {
    /* 允许调用者传入空指针,避免发生非法内存访问。 */
    if (data == 0)
    {
    return;
    }

    data->raw = HS_S37A_ReadAverage(HS_S37A_AVERAGE_SAMPLES);
    data->voltage_mv = HS_S37A_RawToMilliVolts(data->raw);
    data->percent = HS_S37A_RawToPercent(data->raw);
    data->level = HS_S37A_GetLevel(data->percent);
    }

    HS_S37A.h代码

    #ifndef __HS_S37A_H
    #define __HS_S37A_H

    #include "stm32f10x.h"

    /*
    * HS-S37A 水位传感器驱动
    *
    * 硬件连接:HS-S37A OUT -> PA0(ADC1 通道 0)。
    * 注意:PA0 上的模拟电压必须保持在 0~3.3V 范围内。如果传感器使用
    * 5V 供电,应确认 OUT 的最高电压不超过 3.3V,否则必须增加分压电路;
    * 如果所用模块支持,也可以将传感器改为 3.3V 供电。
    */

    /* ADC 参考电压、12 位满量程数值以及每次测量的平均采样次数。 */
    #define HS_S37A_ADC_REFERENCE_MV 3300u
    #define HS_S37A_ADC_FULL_SCALE 4095u
    #define HS_S37A_AVERAGE_SAMPLES 16u

    /*
    * 三点校准的默认 ADC 数值:
    * 干燥位置 -> 0%
    * 一半量程 -> 50%
    * 满量程位置 -> 100%
    *
    * 1076 和 1636 来自当前模块的实测结果。后续可以直接修改这些宏,
    * 也可以在程序运行时调用 HS_S37A_SetCalibration() 设置新的校准值。
    */
    #define HS_S37A_DRY_CALIBRATION 0u
    #define HS_S37A_HALF_CALIBRATION 1076u
    #define HS_S37A_FULL_CALIBRATION 1636u

    /* 水位状态的百分比边界:0~5% 干燥,6~35% 低水位,36~89% 中水位。 */
    #define HS_S37A_DRY_MAX_PERCENT 5u
    #define HS_S37A_LOW_MAX_PERCENT 35u
    #define HS_S37A_MEDIUM_MAX_PERCENT 89u

    /* 水位状态枚举。 */
    typedef enum
    {
    HS_S37A_LEVEL_DRY = 0, /* 干燥 */
    HS_S37A_LEVEL_LOW, /* 低水位 */
    HS_S37A_LEVEL_MEDIUM, /* 中水位 */
    HS_S37A_LEVEL_HIGH /* 高水位或满量程 */
    } HS_S37A_Level;

    /* 一次完整水位测量的数据。 */
    typedef struct
    {
    uint16_t raw; /* 经过平均滤波的 ADC 原始值,范围为 0~4095 */
    uint16_t voltage_mv; /* PA0 引脚电压,单位为毫伏 */
    uint8_t percent; /* 校准后的相对水位,范围为 0~100 */
    HS_S37A_Level level; /* 根据百分比得到的水位状态 */
    } HS_S37A_Data;

    /* 初始化 PA0、ADC1 通道 0,并完成 ADC 自校准。 */
    void HS_S37A_Init(void);

    /*
    * 设置干燥、半量程和满量程三个 ADC 校准点。
    * 设置成功返回 1;参数无效时返回 0,并保留原来的校准值。
    */
    uint8_t HS_S37A_SetCalibration(uint16_t dry_raw, uint16_t half_raw,
    uint16_t full_raw);

    /* 启动一次 ADC 转换并返回原始采样值。 */
    uint16_t HS_S37A_ReadRaw(void);

    /* 连续采样 sample_count 次并返回四舍五入后的平均值。 */
    uint16_t HS_S37A_ReadAverage(uint8_t sample_count);

    /* 将 ADC 原始值换算成 PA0 引脚上的毫伏值。 */
    uint16_t HS_S37A_RawToMilliVolts(uint16_t raw);

    /* 根据三点分段校准参数,将 ADC 原始值换算成相对水位百分比。 */
    uint8_t HS_S37A_RawToPercent(uint16_t raw);

    /* 根据水位百分比判断当前水位状态。 */
    HS_S37A_Level HS_S37A_GetLevel(uint8_t percent);

    /* 完成平均采样、电压换算、百分比校准和状态判断。 */
    void HS_S37A_GetData(HS_S37A_Data *data);

    #endif

    十、传感器影响因素和使用局限

    HS‑S37A 依靠液体导电完成检测,测量结果会被很多条件干扰:

  • 液体电导率:自来水、盐水、纯净水导电能力差距很大,相同浸水深度 ADC 读数不一样;
  • 探头姿态:探头倾斜,浸水导电面积改变,输出数值变化;实验尽量垂直放置探头;
  • 水面波动:水花、水滴造成电极接触状态变化;
  • 供电电压:模块供电电压、STM32 的 3.3V 参考电压波动,会影响 ADC 结果;
  • 温度、杂质污染物:水里杂质盐分,探头表面污垢,都会改变导电效果;
  • 电极腐蚀:电极长时间通电泡水里,会电化学腐蚀,后期读数漂移不准。
  • 适用场景:课堂教学演示、漏水检测、雨水检测。不适合工业长期浸泡高精度液位测量。

    十一、实验总结

    HS‑S37A 水位传感器利用水的导电特性,将探头浸水深度转化模拟电压信号;STM32F103 单片机 ADC 外设采集模拟电压,通过 16 次平均滤波抑制噪声,使用三点分段校准修正传感器非线性,最后在 OLED 屏幕输出 ADC、电压、相对水位百分比和水位状态。

    本实验非常适合学习 ADC 采集、软件滤波、传感器校准,需要注意共地、电压保护、传感器本身的使用限制。

    赞(0)
    未经允许不得转载:171主机测评 » STM32驱动HS‑S37A水位传感器+OLED完整实验讲解
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址