液位传感器模块
原理 · 硬件 · 程序 三位一体演示
基于 STM32F103C8T6 + 0.96寸 OLED + 电阻式液位传感器, 通过 ADC 采样实现水位的实时采集与显示。 传感器裸露铜线浸入水中时电阻变化,经分压后产生可测量的模拟电压信号, MCU 以 12 位精度将电压转换为 0~100% 液位百分比并实时刷新 OLED 屏幕。
项目概览
本演示项目使用电阻式液位传感器采集水位信号,MCU 通过 ADC 通道读取模拟电压,并在 OLED 屏幕上实时显示液位百分比。
1 传感器信号采集
电阻式传感器输出随水位变化的模拟电压,经分压后送入 ADC1_IN0 (PA0)。
- 12 位分辨率 (0 ~ 4095)
- 采样周期 239.5 个 ADC 时钟
- ADC 时钟 9 MHz
2 数据换算与限幅
将原始 ADC 数值换算为 0 ~ 100 的液位百分比,并做限幅保护。
- 换算公式:ADC/4095 × 100 × 4
- 上限 100 截断
- 1 秒刷新一次
3 OLED 实时显示
通过软件 I²C (PB14/PB15) 驱动 0.96 寸 OLED,居中显示当前液位。
- 从机地址 0x78
- 显示格式 level:XX
- 8×16 ASCII 字符
传感器原理与功能
本模块板载电阻式液位传感器,基于电导变化检测水位,适用于连续液位监测场景。
电阻式水位传感器 · 工作原理
传感器由 10 根平行裸露导线 等距排布在 PCB 上(每根代表 10% 液位刻度)。 当水位上升浸没导线时,水(含有微量离子)的导电性 将原本不相连的导线两两接通,形成分压电路。浸没的导线越多,输出电压越高。
- 工作电压
DC 3.3 ~ 5 V - 输出形式
模拟电压 0 ~ Vref - 检测精度
10% 步进(10 段) - 特点
结构简单 / 成本低 / 实时性好 - 局限性
接触液体 / 易腐蚀 / 不可用于污浊或导电性差液体
原理图 & PCB 封装展示
本模块板载两组 3Pin 接口:J1 接电阻式水位传感器(模拟量),J2 接非接触式液位传感器(数字量)。
i 接口说明
- J1 · 电阻式
GND / VCC / I/O - J2 · 非接触式
VCC / SIGNAL / GND - 主控
STM32F103C8T6 - 屏幕
0.96" OLED (I²C)
! 注意:原理图与代码引脚不一致
原理图中 I/O 接 PA4、SIGNAL 接 PA6; 但示例程序实际使用的是 PA0 (ADC1_IN0)。 实际接线需将传感器信号线接到 PA0,或修改源码匹配原理图引脚。
ii PCB 引脚分布
J1(电阻式)
- 1
I/O - 2
VCC - 3
GND
J2(非接触式)
- 1
GND - 2
SIG - 3
VCC
程序源码分析
本节分析核心源码:main.c 主循环、adc.c ADC 驱动、gpio.c 引脚初始化。代码由 STM32CubeMX + Keil 工具链生成,使用 HAL 库。
/* main.c - 主循环 */ #include "main.h" #include "adc.h" #include "gpio.h" #include "./HAL/OLED/OLED.h" uint32_t Time_Now; uint8_t level; int main(void) { HAL_Init(); SystemClock_Config(); // HSE 8M × PLL9 = 72 MHz MX_GPIO_Init(); MX_ADC1_Init(); OLED_Init(); while (1) { if (HAL_GetTick() - Time_Now >= 1000) // 每 1 s 刷新一次 { HAL_ADC_Start(&hadc1); if (HAL_ADC_PollForConversion(&hadc1, 999) == HAL_OK) level = HAL_ADC_GetValue(&hadc1)/4095.0*100*4; HAL_ADC_Stop(&hadc1); if (level >= 100) level = 100; // 限幅 OLED_Clear(); OLED_CenterPrintf(24, "level:%d", level); OLED_Update(); Time_Now = HAL_GetTick(); } } }
/* adc.c - ADC1 通道 0 初始化 (PA0) */ ADC_HandleTypeDef hadc1; void MX_ADC1_Init(void) { ADC_ChannelConfTypeDef sConfig = {0}; hadc1.Instance = ADC1; hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 1; HAL_ADC_Init(&hadc1); sConfig.Channel = ADC_CHANNEL_0; // PA0 sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; HAL_ADC_ConfigChannel(&hadc1, &sConfig); } void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if (adcHandle->Instance == ADC1) { __HAL_RCC_ADC1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_0; // PA0 GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } }
/* gpio.c - GPIO 时钟使能 */ void MX_GPIO_Init(void) { /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOD_CLK_ENABLE(); // LED 等外设 __HAL_RCC_GPIOA_CLK_ENABLE(); // PA0 / OLED SCL } /* 注:PB14/PB15 (I2C for OLED) 的初始化在 OLED.c 中的 OLED_GPIO_Init() 完成 */ /* SCL = PB15, SDA = PB14, 开漏输出 */
A 系统时钟 72 MHz
外部晶振 HSE 8 MHz,通过 PLL ×9 升频到 72 MHz 主频。 ADC 时钟来自 PCLK2 (72 MHz) 经 8 分频,得到 9 MHz。
B ADC 通道选择
程序使用 ADC_CHANNEL_0,对应 PA0 引脚; 采样时间选最长档 239.5 cycles,以获得更稳定的读数。
C 换算公式
原始 ADC 范围 0~4095,公式为 ADC/4095 × 100 × 4。 ×4 是该传感器的校准系数,因分压电阻设计使最大有效输出约为满量程的 1/4。
D 非阻塞时序
用 HAL_GetTick() 差值判断 1 秒周期,while(1) 不会因等待 ADC 而阻塞。 每次循环 Start → Poll → GetValue → Stop,单次轮询模式。
E OLED 软件 I²C
OLED 的 SCL/SDA 使用 PB15/PB14,通过 GPIO_MODE_OUTPUT_OD 模拟 I²C 总线。 从机地址 0x78。
! 注意
原理图标注 I/O 接 PA4、SIGNAL 接 PA6,但本程序绑定 PA0。实际接线需按程序连 PA0,或修改 ADC_CHANNEL。
主程序流程图
下图展示从 MCU 上电到 OLED 初始化再到定时循环采集的完整流程,包含循环回路。
液位实时模拟演示
拖动滑块或点击「自动模拟」,右侧 OLED 屏幕会按主程序逻辑显示 level:XX,严格遵循源码换算公式。OLED 显示与实物一致:黑色背景、白色像素点阵。