STM32读取电阻式土壤湿度模块+OLED显示完整教程

前言

本篇基于 STM32F103C8T6 最小系统板,搭配电阻式土壤湿度传感器、I2C‑OLED 屏幕,实现土壤湿度采集、ADC 滤波、两点标定、OLED 屏幕数据显示。可以看到 ADC 原始值、引脚电压、相对湿度百分比、土壤干湿状态,适合单片机入门教学、课程实验。

硬件清单

  • STM32F103C8T6 最小系统板
  • 电阻式土壤湿度检测模块
  • 0.96 寸 I2C OLED 显示屏
  • 杜邦线若干

视频讲解:STM32读取电阻式土壤湿度模块+OLED显示完整教程https://www.bilibili.com/video/BV1jEbV6zEjV/?share_source=copy_web&vd_source=91b967b41a17a5686913521545a8c3fb

一、整体实现原理

土壤含水量发生改变,会直接改变土壤导电能力;土壤湿度模块把导电能力变化转化为 AO 模拟输出电压。 STM32 芯片通过 ADC 外设读取该模拟电压,经过多次采样平均滤波消除噪声,再通过两点标定算法换算出相对湿度,最终把各项数据输出到 OLED 屏幕上展示。

完整业务流程: 土壤含水情况发生变化 → 探针导电能力改变 → 模块输出 AO 模拟电压 → STM32 PA1 引脚 ADC 采集 → 16 次平均滤波得到稳定 ADC 数值 → 通过干湿标定点计算相对湿度 → OLED 显示 ADC、电压、湿度、干湿状态。

二、硬件接线说明

⚠️重点提醒:所有器件必须共地!传感器 GND、STM32 GND、OLED GND 接在一起。如果不共地,ADC 读数乱跳、数值固定不变,测量完全失效。

模块引脚 STM32F103C8T6 引脚 功能说明
土壤湿度模块 VCC 3.3V 传感器供电
土壤湿度模块 AO PA1 模拟信号输出,ADC1 通道 1 采集
土壤湿度模块 GND GND 公共参考地
OLED VCC 5V OLED 屏幕供电
OLED SCL PB8 I2C 时钟(软件 I2C)
OLED SDA PB9 I2C 数据(软件 I2C)
OLED GND GND 公共参考地

接线注意点

  1. STM32F103 ADC 输入最大电压为 3.3V,本项目传感器使用 3.3V 供电,AO 输出不会超过 3.3V,可以直接接 PA1。如果传感器改成 5V 供电,AO 输出有可能超过 3.3V,必须增加分压限压电路,严禁直接接入 STM32 ADC 引脚。
  2. 本实验没有使用模块 DO 数字输出引脚。DO 引脚是比较器输出,只能输出高低电平,仅判断是否超过电位器阈值,无法获取连续湿度变化;AO 模拟输出可以采集连续变化电压,适合学习 ADC 采集、滤波、标定算法。

三、电阻式土壤湿度模块工作原理

电阻式土壤湿度传感器依靠两根金属探针工作。探针插入土壤之后,土壤充当导电介质。

  • 土壤干燥:水分少,导电离子少,探针之间等效电阻大;
  • 土壤湿润:水分充足,导电离子变多,探针之间等效电阻变小。

模块内部分压电路,会把电阻变化转换成 AO 引脚输出电压。市面上大部分电阻式土壤湿度模块规律:土壤越湿,AO 输出电压越低,ADC 采样数值越小

⚠️注意:不同厂家模块电路设计不一样,也存在越湿 ADC 值越大的模块。本套程序兼容两种情况,只要正确填写干燥、湿润两组标定 ADC 数值即可。
重要知识点:电阻式传感器检测本质是土壤导电能力,不是直接测量土壤含水量。土壤盐分、肥料浓度、土壤紧实度、探针接触松紧,都会干扰读数。必须经过标定,才可以得到可用的相对湿度。

四、STM32 ADC 采集原理

STM32F103C8T6 内置 12 位 ADC,PA1 引脚对应 ADC1 通道 1。 输入电压范围:0 ~ 3.3V;ADC 转换输出数字范围:0 ~ 4095。

电压计算公式: 引脚电压(V) = ADC原始值 × 3.3 ÷ 4095

工程代码内部转换为毫伏计算: voltage_mv = adc_value * 3300 / 4095;

PA1 电压 对应 ADC 数值
0V 0
0.60V 745
1.65V 2048
2.40V 2978
3.30V 4095

ADC 初始化主要完成:开启 GPIOA、ADC1 时钟;PA1 配置模拟输入;ADC 时钟配置 12MHz;配置单通道单次软件触发转换;设置采样时间;执行 ADC 复位与自校准,丢弃第一次无效转换结果。

五、软件滤波处理

ADC 采样容易受到电源噪声、接触抖动干扰,如果单次采样直接显示,OLED 屏幕数字会不停跳动。 本工程采用16 次采样取算术平均值 的平均滤波算法。 平均ADC值 = 16次ADC采样总和 /16

滤波流程:软件触发 ADC 转换,循环读取 16 次 ADC 结果,完成采样之后求平均值,换算电压,再计算湿度,最后刷新 OLED。

平均滤波优点:抑制随机噪声,显示数据稳定;缺点:会轻微降低响应速度,本项目主循环约 200ms 刷新一次屏幕,适合实验观察。

工程关键函数说明

源文件 函数名 功能
TR_Detection.c TR_Detection_Init() 初始化 PA1、ADC1,执行 ADC 校准
TR_Detection.c TR_Detection_ReadAdc() 单次读取 ADC 通道 1 原始数值
TR_Detection.c TR_Detection_ReadAdcAverage() 16 次采样平均滤波
TR_Detection.c TR_Detection_ConvertToVoltageMv() ADC 值转换成引脚电压(毫伏)
TR_Detection.c TR_Detection_ConvertToMoisturePercent() 两点标定换算相对湿度百分比
TR_Detection.c TR_Detection_ReadData() 一次性读取 ADC、电压、湿度全套数据
main.c Demo_ShowSoilData() OLED 屏幕打印全部测量数据和干湿状态

六、两点标定算法讲解

❗为什么一定要标定? 不同传感器、不同土质、探针插入深度不一样,同样湿度下 ADC 读数完全不同。不能直接拿 ADC0‑4095 直接当做湿度百分比,必须设置两个参考点。

两个标定参考点:

  • dry_raw:干燥土壤对应的 ADC 值,对应湿度 0%
  • wet_raw:充分湿润土壤对应的 ADC 值,对应湿度 100%

常用模块(湿度越大 ADC 数值越小)计算公式: 相对湿度 = (干燥ADC值 - 当前ADC值) ×100 ÷ (干燥ADC值 - 湿润ADC值)

同时程序会对输出做限制:结果强制限定在 0~100%。

  • 当前 ADC ≥干燥 ADC →湿度强制 0%
  • 当前 ADC ≤湿润 ADC →湿度强制 100%
  • 中间区间做线性换算

举个例子:干燥 ADC=3000,湿润 ADC=1500,当前 ADC=2250 (3000‑2250)*100/(3000‑1500)=50%

工程头文件默认标定参数:

cpp 复制代码
#define TR_DETECTION_DRY_ADC    4095u
#define TR_DETECTION_WET_ADC       0u

默认参数只是 ADC 全量程,仅用于程序跑通观察变化趋势,不是真实土壤标定结果

实操标定步骤

  1. 探针擦拭干净,插入干燥待测土壤,保持插入深度固定;
  2. 等待 OLED 上 ADC 读数稳定,记录数值作为 dry_raw;
  3. 向土壤均匀加水,不要把水淋到传感器电路板;
  4. 等待水分扩散,读数稳定,记录此时 ADC 值作为 wet_raw;
  5. 修改头文件标定宏定义,重新编译下载;
  6. 再次测试,观察湿度百分比随土壤干湿变化。

也可以代码运行时动态设置标定参数:

cpp 复制代码
TR_Detection_Calibration calibration;
calibration.dry_raw = 3000u;
calibration.wet_raw = 1500u;
TR_Detection_SetCalibration(&calibration);

标定注意:标定必须使用你实际测量的土壤,探针插入深度、压紧程度前后保持一致。

七、OLED 显示逻辑与干湿状态判断

屏幕输出示例:

cpp 复制代码
SOIL MOISTURE
ADC:2250/4095
PIN:1.813 V
MOIST:050 %
STATE:NORMAL

显示字段含义:

显示内容 说明
ADC 滤波之后 ADC 平均值,范围 0‑4095
PIN PA1 引脚计算得到模拟电压
MOIST 两点标定计算得到相对湿度百分比
STATE 土壤干湿状态

状态阈值逻辑:

  • MOIST<30% → STATE:DRY(干燥)
  • 30% ≤ MOIST<70% → STATE:NORMAL(正常)
  • MOIST ≥70% → STATE:WET(潮湿)

30%、70% 只是实验演示阈值,不是植物浇水标准,实际项目根据植物需求修改阈值。

八、源代码章节

包含工程关键源码,如 TR_Detection.c、TR_Detection.h、main.c 核心片段。

main.c代码

cpp 复制代码
/*
 * STM32F103C8T6 soil-moisture teaching demonstration
 *
 * TR_Detection AO -> PA1 (ADC1 channel 1)
 * OLED SCL       -> PB8
 * OLED SDA       -> PB9
 */
#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "TR_Detection.h"

#define DISPLAY_UPDATE_MS  200u

static void Demo_ShowStartup(void);
static void Demo_ShowSoilData(const TR_Detection_Data *data);
static u8 *Demo_GetMoistureText(u8 moisture_percent);

int main(void)
{
    TR_Detection_Data soil_data;

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

    Demo_ShowStartup();
    TR_Detection_Init();
    delay_ms(500u);

    while (1)
    {
        if (TR_Detection_ReadData(&soil_data) == TR_DETECTION_OK)
        {
            Demo_ShowSoilData(&soil_data);
        }
        delay_ms(DISPLAY_UPDATE_MS);
    }
}

static void Demo_ShowStartup(void)
{
    OLED_Clear();
    OLED_ShowString(0u, 0u, (u8 *)"SOIL MOISTURE", 16u);
    OLED_ShowString(0u, 20u, (u8 *)"AO: PA1 / ADC1", 12u);
    OLED_ShowString(0u, 36u, (u8 *)"OLED: PB8 PB9", 12u);
    OLED_ShowString(0u, 52u, (u8 *)"INITIALIZING...", 12u);
    OLED_Refresh();
}

static void Demo_ShowSoilData(const TR_Detection_Data *data)
{
    OLED_Clear();

    OLED_ShowString(0u, 0u, (u8 *)"SOIL MOISTURE", 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 *)"MOIST:", 12u);
    OLED_ShowNum(42u, 39u, data->moisture_percent, 3u, 12u);
    OLED_ShowString(60u, 39u, (u8 *)" %", 12u);

    OLED_ShowString(0u, 52u, (u8 *)"STATE:", 12u);
    OLED_ShowString(42u, 52u,
                    Demo_GetMoistureText(data->moisture_percent), 12u);

    OLED_Refresh();
}

static u8 *Demo_GetMoistureText(u8 moisture_percent)
{
    if (moisture_percent < 30u)
    {
        return (u8 *)"DRY";
    }
    if (moisture_percent < 70u)
    {
        return (u8 *)"NORMAL";
    }

    return (u8 *)"WET";
}

TR_Detection.c代码

cpp 复制代码
/**
 * @file TR_Detection.c
 * @brief STM32F103C8T6 土壤湿度检测模块驱动实现。
 * @note 文件编码:UTF-8。
 */

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

/** STM32F103 内部 ADC 为 12 位,最大转换值为 4095。 */
#define TR_DETECTION_ADC_MAX          4095u

/** ADC 参考电压,单位为毫伏;本工程模拟电源为 3.3V。 */
#define TR_DETECTION_REFERENCE_MV     3300u

/** 当前使用的干燥、湿润两点标定参数。 */
static TR_Detection_Calibration g_calibration =
{
    TR_DETECTION_DRY_ADC,
    TR_DETECTION_WET_ADC
};

/**
 * @brief 初始化土壤湿度传感器的模拟量采集接口。
 *
 * PA1 被配置为模拟输入,ADC1 工作在单次、独立转换模式。初始化末尾执行
 * STM32F1 所需的复位校准和校准,并丢弃校准后的第一次转换结果。
 */
void TR_Detection_Init(void)
{
    GPIO_InitTypeDef gpio_init;
    ADC_InitTypeDef adc_init;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |
                           RCC_APB2Periph_ADC1, ENABLE);

    /* 本工程 PCLK2 为 72MHz,六分频后 ADC 时钟为 12MHz,不超过 14MHz。 */
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);

    /* PA1 对应 ADC1_IN1,模拟输入模式会关闭数字输入输出电路。 */
    gpio_init.GPIO_Pin = GPIO_Pin_1;
    gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
    gpio_init.GPIO_Mode = GPIO_Mode_AIN;
    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);

    /* STM32F1 ADC 上电后需要依次完成复位校准和启动校准。 */
    ADC_ResetCalibration(ADC1);
    while (ADC_GetResetCalibrationStatus(ADC1) != RESET)
    {
    }

    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1) != RESET)
    {
    }

    /* 丢弃校准后的第一次转换结果,避免其影响后续测量。 */
    (void)TR_Detection_ReadAdc();
}

/**
 * @brief 读取一次 ADC1 通道 1 的转换结果。
 * @return 12 位 ADC 原始值,范围为 0~4095。
 */
u16 TR_Detection_ReadAdc(void)
{
    /* 清除上一次完成标志,然后通过软件启动一次规则组转换。 */
    ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);

    /* 等待本次转换完成。 */
    while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)
    {
    }

    return ADC_GetConversionValue(ADC1);
}

/**
 * @brief 对多次 ADC 采样结果进行算术平均滤波。
 * @param times 采样次数。
 * @return 四舍五入后的 ADC 平均值;采样次数为 0 时返回 0。
 */
u16 TR_Detection_ReadAdcAverage(u8 times)
{
    u8 i;
    u32 sum;

    if (times == 0u)
    {
        return 0u;
    }

    sum = 0u;
    for (i = 0u; i < times; i++)
    {
        sum += TR_Detection_ReadAdc();
    }

    /* 除法前加上除数的一半,使整数计算结果按四舍五入处理。 */
    return (u16)((sum + ((u32)times / 2u)) / (u32)times);
}

/**
 * @brief 将 ADC 原始值换算为 PA1 引脚电压。
 * @param adc_value ADC 原始值。
 * @return 引脚电压,单位为毫伏。
 */
u16 TR_Detection_ConvertToVoltageMv(u16 adc_value)
{
    u32 raw;

    /* 对输入值限幅,防止错误参数造成换算结果超过参考电压。 */
    raw = adc_value;
    if (raw > TR_DETECTION_ADC_MAX)
    {
        raw = TR_DETECTION_ADC_MAX;
    }

    /* 电压 = ADC值 × 参考电压 ÷ ADC满量程,并进行四舍五入。 */
    return (u16)((raw * TR_DETECTION_REFERENCE_MV +
                  TR_DETECTION_ADC_MAX / 2u) / TR_DETECTION_ADC_MAX);
}

/**
 * @brief 使用两点标定法将 ADC 值换算为相对土壤湿度。
 * @param adc_value ADC 原始值。
 * @return 相对湿度,范围为 0~100%。
 *
 * 函数同时兼容"湿度越高 ADC 值越小"和"湿度越高 ADC 值越大"的模块。
 * 超出干燥、湿润标定区间的输入值会被限制为 0% 或 100%。
 */
u8 TR_Detection_ConvertToMoisturePercent(u16 adc_value)
{
    u32 raw;
    u32 span;
    u32 position;
    u32 percent;
    u16 dry_raw;
    u16 wet_raw;

    dry_raw = g_calibration.dry_raw;
    wet_raw = g_calibration.wet_raw;
    raw = adc_value;
    if (raw > TR_DETECTION_ADC_MAX)
    {
        raw = TR_DETECTION_ADC_MAX;
    }

    /* 常见电阻式模块在湿度升高时输出电压和 ADC 值降低。 */
    if (dry_raw > wet_raw)
    {
        if (raw >= dry_raw)
        {
            return 0u;
        }
        if (raw <= wet_raw)
        {
            return 100u;
        }
        span = (u32)dry_raw - wet_raw;
        position = (u32)dry_raw - raw;
    }
    else
    {
        /* 同时支持湿度升高时 ADC 值增大的反向输出模块。 */
        if (raw <= dry_raw)
        {
            return 0u;
        }
        if (raw >= wet_raw)
        {
            return 100u;
        }
        span = (u32)wet_raw - dry_raw;
        position = raw - dry_raw;
    }

    /* 在两个标定点之间进行线性插值,并对结果进行四舍五入。 */
    percent = (position * 100u + span / 2u) / span;
    return (u8)percent;
}

/**
 * @brief 获取一次完整的土壤湿度测量数据。
 * @param data 用于接收 ADC、电压和湿度结果的指针。
 * @return 参数有效时返回成功,否则返回参数错误。
 */
u8 TR_Detection_ReadData(TR_Detection_Data *data)
{
    if (data == 0)
    {
        return TR_DETECTION_ERROR_ARGUMENT;
    }

    data->raw = TR_Detection_ReadAdcAverage(TR_DETECTION_SAMPLE_COUNT);
    data->voltage_mv = TR_Detection_ConvertToVoltageMv(data->raw);
    data->moisture_percent =
        TR_Detection_ConvertToMoisturePercent(data->raw);

    return TR_DETECTION_OK;
}

/**
 * @brief 设置运行时两点标定参数。
 * @param calibration 干燥点和湿润点标定值。
 * @return 标定值有效时返回成功,否则返回参数错误。
 */
u8 TR_Detection_SetCalibration(const TR_Detection_Calibration *calibration)
{
    /* 两个标定值必须位于 ADC 量程内,并且不能相等。 */
    if ((calibration == 0) ||
        (calibration->dry_raw > TR_DETECTION_ADC_MAX) ||
        (calibration->wet_raw > TR_DETECTION_ADC_MAX) ||
        (calibration->dry_raw == calibration->wet_raw))
    {
        return TR_DETECTION_ERROR_ARGUMENT;
    }

    g_calibration = *calibration;
    return TR_DETECTION_OK;
}

/**
 * @brief 读取当前两点标定参数。
 * @param calibration 用于接收标定参数的指针。
 */
void TR_Detection_GetCalibration(TR_Detection_Calibration *calibration)
{
    if (calibration != 0)
    {
        *calibration = g_calibration;
    }
}

TR_Detection.h代码

cpp 复制代码
#ifndef __TR_DETECTION_H
#define __TR_DETECTION_H

/**
 * @file TR_Detection.h
 * @brief 土壤湿度检测模块驱动接口。
 * @note 文件编码:UTF-8。
 *
 * 硬件连接:
 * - 土壤湿度模块 AO 接 PA1,对应 ADC1 通道 1;
 * - 土壤湿度模块 VCC 接 3.3V,GND 接系统地。
 *
 * 湿度百分比由干燥、湿润两个参考点线性换算得到。不同传感器及不同
 * 土壤环境的输出存在差异,正式使用前应记录实际干燥值和湿润值,修改
 * TR_DETECTION_DRY_ADC、TR_DETECTION_WET_ADC,或调用运行时标定接口。
 */

#include "stm32f10x.h"

/** 干燥参考点 ADC 值,对应相对湿度 0%。 */
#define TR_DETECTION_DRY_ADC          4095u

/** 湿润参考点 ADC 值,对应相对湿度 100%。 */
#define TR_DETECTION_WET_ADC          0u

/** 每次读取数据时的 ADC 平均采样次数。 */
#define TR_DETECTION_SAMPLE_COUNT     16u

/** 接口执行成功。 */
#define TR_DETECTION_OK               0u

/** 接口参数无效。 */
#define TR_DETECTION_ERROR_ARGUMENT   1u

/** 土壤湿度两点标定参数。 */
typedef struct
{
    u16 dry_raw;              /**< 干燥参考点 ADC 值,对应 0%。 */
    u16 wet_raw;              /**< 湿润参考点 ADC 值,对应 100%。 */
} TR_Detection_Calibration;

/** 一次完整的土壤湿度测量结果。 */
typedef struct
{
    u16 raw;                  /**< 经过平均滤波的 12 位 ADC 原始值。 */
    u16 voltage_mv;           /**< PA1 引脚电压,单位为毫伏。 */
    u8 moisture_percent;      /**< 标定后的相对湿度,范围为 0~100%。 */
} TR_Detection_Data;

/** 初始化 PA1、ADC1,并执行 ADC 校准。 */
void TR_Detection_Init(void);

/** 启动一次 ADC 转换并返回 12 位原始值。 */
u16 TR_Detection_ReadAdc(void);

/**
 * 连续读取 ADC 并返回平均值。
 * @param times 采样次数;传入 0 时直接返回 0。
 * @return 四舍五入后的 ADC 平均值。
 */
u16 TR_Detection_ReadAdcAverage(u8 times);

/**
 * 将 ADC 原始值换算为 PA1 引脚电压。
 * @param adc_value ADC 原始值。
 * @return 引脚电压,单位为毫伏。
 */
u16 TR_Detection_ConvertToVoltageMv(u16 adc_value);

/**
 * 根据当前两点标定参数换算相对湿度。
 * @param adc_value ADC 原始值。
 * @return 相对湿度,范围为 0~100%。
 */
u8 TR_Detection_ConvertToMoisturePercent(u16 adc_value);

/**
 * 完成平均采样、电压换算和湿度换算。
 * @param data 用于接收测量结果的非空指针。
 * @return TR_DETECTION_OK 或 TR_DETECTION_ERROR_ARGUMENT。
 */
u8 TR_Detection_ReadData(TR_Detection_Data *data);

/**
 * 设置运行时干燥、湿润标定值。
 * @param calibration 标定参数;两点必须不同且不能超过 4095。
 * @return TR_DETECTION_OK 或 TR_DETECTION_ERROR_ARGUMENT。
 */
u8 TR_Detection_SetCalibration(const TR_Detection_Calibration *calibration);

/**
 * 获取当前使用的标定参数。
 * @param calibration 用于接收标定参数的指针;空指针不会执行操作。
 */
void TR_Detection_GetCalibration(TR_Detection_Calibration *calibration);

#endif

九、实物演示章节

实验准备实物

STM32 最小系统板、土壤湿度模块、OLED 显示屏、杜邦线、干土、清水、容器。

演示操作步骤

  1. 按照上面接线表完成接线,再次确认全部 GND 共地,检查传感器供电为 3.3V,确认接线没有短路。
  2. 将程序编译下载到 STM32 开发板,上电,OLED 屏幕成功点亮,输出土壤湿度相关数据。
  3. 场景 1:探针放置空气中 探针悬空,相当于干燥环境,可以观察 ADC 数值变大,电压升高,MOIST 湿度百分比变低,STATE 显示 DRY 干燥。
  4. 场景 2:探针插入干燥土壤 把探针插入干土,保持稳定,等待读数稳定,记录当前 ADC 数值,这就是干燥标定点。
  5. 场景 3:向土壤加水,模拟浇水 缓慢向土壤加水,不要打湿传感器电路板。等待水分渗透,观察 ADC 数值下降,电压降低,湿度百分比上升,状态从 DRY 切换 NORMAL,继续加水切换 WET。
  6. 标定效果演示 把上面记录干、湿两组 ADC 数值替换代码中标定点,重新烧录。再次干湿切换,可以看到湿度百分比可以精准落在 0‑100 区间跟随土壤变化。

现象异常排查

  1. OLED 不亮:检查 5V 供电、SCL PB8、SDA PB9 接线,确认 I2C 设备地址是否匹配。
  2. ADC 数值固定不变:检查 AO 引脚是否真正接到 PA1,确认共地,确认传感器 3.3V 供电正常。
  3. 数值不停乱跳:检查地线是否接牢,增加滤波电容,确认 16 次平均滤波代码正常运行。

十、传感器使用注意事项

  1. 探针腐蚀问题:电阻式探针长期通电会发生电解腐蚀。课堂短时实验可以持续上电;长期野外采集项目,建议测量瞬间才给传感器供电,采集完成立刻断电,延长探针寿命。
  2. 盐分肥料干扰:传感器测导电率,土壤施肥、含盐量高,相同含水量 ADC 读数会改变。更换土壤必须重新标定,不能直接复用旧标定参数。
  3. 探针插入影响:插入深度、土壤空隙、压紧程度都会改变测量结果,做对比实验,探针插入条件必须统一。
  4. 水分扩散需要时间:浇水之后不要立刻读数,等待水分在土壤扩散均匀。严禁水直接淋到传感器电路板。
  5. 百分比只是相对值:OLED 上面湿度是两点标定得到的相对百分比,不是专业设备测量的体积含水率。如果需要高精度土壤含水率,需要称重法或者专业土壤传感器。

十一、总结

电阻式土壤湿度传感器把土壤导电能力转换为模拟电压输出;STM32 通过 ADC 外设读取模拟信号,经过多次采样平均滤波抑制噪声,使用两点标定算法,把 ADC 原始值转换成相对湿度百分比,最终在 OLED 屏幕展示 ADC、电压、湿度和干湿状态。

相关推荐
不悔哥1 小时前
开源 LVGL:把手机级界面塞进 MCU
单片机·智能手机·开源
影寂ldy2 小时前
C# UdpClient 单播 三种通信版本
stm32·单片机·c#
szxinmai主板定制专家2 小时前
基于 RK3576 的智能割草机器人解决方案,从样机到量产
人工智能·嵌入式硬件·计算机视觉·fpga开发·机器人·rk3588+fpga·割草机器人
单片机仿真设计2 小时前
【proteus仿真】基于单片机的水肥一体化智能监控系统(仿真图+程序)
stm32·单片机·嵌入式硬件·proteus
单片机仿真设计2 小时前
【proteus仿真】基于 STM32 单片机破碎机监测保护系统(仿真图+程序)
stm32·单片机·proteus
国科安芯2 小时前
基于抗辐射MCU的卫星分布式控制系统CANFD总线架构研究
网络·人工智能·分布式·单片机·嵌入式硬件·架构·抗辐射
G***技11 小时前
告别“云端依赖”:LH707 如何重构 AI 视频监控的底层逻辑?
人工智能·嵌入式硬件
ShallWeL15 小时前
Orin 上多模型常驻与显存预算
人工智能·嵌入式硬件·nvidia·orin
亮工硬件15 小时前
1-11 STM32内部FLASH(数据掉电保存)
笔记·stm32·单片机·嵌入式硬件·学习