**摘要:**本文针对 FDC2212 电容传感芯片配合 STM32G030 实现液面探测时,IIC 读取的电容值存在跳动、导致每次下探到液面的深度不一致的问题,从硬件布局、驱动时序、采样滤波、判定算法四个层面给出可落地的排查顺序与解决方案,并给出关键代码示例。
一、问题现象与本质分析
典型现象是:探头以固定速度下探,理论上每次接触液面时应该记录到几乎相同的 Z 轴位置,但实际每次触发点相差几十微米到几毫米不等,重复性差。同时观察 FDC2212 的原始转换值(DATA_CHx),会发现即使探头静止不动,读数也在持续跳动。
需要先明确一点:FDC2212 输出的是频率/转换计数值,不是直接的电容值,其分辨率极高(28 位),因此它对噪声、寄生电容、温漂和参考时钟抖动都非常敏感。读数跳动是"果",真正的原因通常分布在以下几个环节:
- 传感端:探头电极的寄生电容不稳定、屏蔽不完整、外部干扰耦合。
- 时钟端:FDC2212 使用外部晶振作为参考,晶振抖动或负载电容不匹配会直接放大读数噪声。
- 驱动端:IIC 时序、寄存器配置(转换时间、驱动电流、时钟分频)不合理。
- 算法端:单次采样直接判定、没有滤波、没有基线跟踪、没有迟滞,导致判定点抖动。
二、先量化:确认跳动到底有多大
在动手改硬件之前,先把"跳动"量化出来,否则无法判断优化是否有效。建议做如下测试:
- 探头悬空静止,连续采集 1000 次 DATA_CHx,记录最大值、最小值、标准差。
- 探头固定在某一固定高度(例如距液面 1 mm),重复上述采集。
- 记录每次下探的触发点位置,统计重复性误差。
如果静止状态下标准差已经达到几百甚至上千个 LSB,说明噪声主要来自硬件或配置;如果静止时很稳、只有下探过程中跳动,则更可能是机械振动、线缆晃动或算法判定问题。
三、硬件层面的关键措施
3.1 参考时钟必须稳定
FDC2212 的转换结果与参考时钟频率成正比,时钟抖动会 1:1 传递到读数上。务必使用质量较好的外部晶振(常见 40 MHz),并注意:
- 晶振负载电容按规格书匹配,不要凭经验取值。
- 晶振尽量靠近芯片,走线短且包地,远离 IIC 和电源开关节点。
- 不要用 MCU 的 MCO 输出或内部 RC 作为 FDC2212 的参考时钟。
3.2 电极与屏蔽
- 传感电极到芯片的走线尽量短,最好小于几厘米,并使用同轴或屏蔽线。
- 屏蔽层接到板子的模拟地,不要与数字地混接后再单点接地,避免地弹。
- 电极背面加保护环(Guard),由 FDC2212 的驱动信号驱动,抑制外部电场耦合。
- 探头线缆在下探过程中会弯折、靠近金属结构件,寄生电容随之变化,这是"每次深度不同"的常见隐藏原因,建议固定线缆走向并做应力释放。
3.3 电源与去耦
FDC2212 的模拟电源需要干净。建议 AVDD 与 DVDD 分别用 LDO 供电,靠近引脚放置 1 μF + 0.1 μF 去耦电容,模拟地与数字地单点连接。STM32G030 的 IIC 上拉电阻建议 2.2 kΩ~4.7 kΩ,并在 SDA/SCL 上串 22~33 Ω 电阻抑制振铃。
四、寄存器配置优化
FDC2212 的噪声与转换时间、驱动电流、时钟分频强相关。核心思路是在满足响应速度的前提下尽量加长转换时间,因为转换时间越长,等效带宽越窄,噪声越低。
| 寄存器 | 作用 | 优化建议 |
|---|---|---|
| CLOCK_DIVIDERS_CHx | 传感器时钟分频 | 按规格书推荐值配置,保证传感器频率在 10 kHz~10 MHz 范围内 |
| SETTLECOUNT_CHx | 建立时间 | 适当加大,保证每次转换前稳定 |
| RCOUNT_CHx | 转换时间(参考时钟周期数) | 在允许的采样率内尽量取大,例如 0x200~0x800 |
| DRIVE_CURRENT_CHx | 传感器驱动电流 | 从 0 开始逐步增大,直到振幅达到量程的 50%~80%,不要过驱 |
| CONFIG(休眠/激活) | 工作模式 | 使用连续转换模式,避免频繁启停引入的建立误差 |
特别注意:驱动电流过大会让传感器进入非线性区,读数跳动反而加剧。建议用示波器观察传感器波形,确认振幅合适。
五、STM32G030 驱动与 IIC 读取
5.1 读取时序要正确
FDC2212 的 DATA 寄存器是 28 位,分两个 16 位寄存器存放,读取时必须先读 MSB 再读 LSB,且两次读取之间不能被新的转换结果打断。推荐做法是:
c
#define FDC2212_ADDR (0x2A << 1)
#define REG_DATA_CH0 0x00
#define REG_STATUS 0x18
/* 读取一次 CH0 的 28 位转换结果 */
uint32_t FDC2212_ReadChannel0(void)
{
uint8_t buf[2];
uint32_t msb, lsb;
/* 先读 MSB 寄存器 */
HAL_I2C_Mem_Read(&hi2c1, FDC2212_ADDR, REG_DATA_CH0, I2C_MEMADD_SIZE_8BIT,
buf, 2, 100);
msb = ((uint32_t)buf[0] << 8) | buf[1];
/* 再读 LSB 寄存器 */
HAL_I2C_Mem_Read(&hi2c1, FDC2212_ADDR, REG_DATA_CH0 + 1, I2C_MEMADD_SIZE_8BIT,
buf, 2, 100);
lsb = ((uint32_t)buf[0] << 8) | buf[1];
return ((msb & 0x0FFF) << 16) | lsb;
}
注意 MSB 寄存器只有低 12 位有效,必须做掩码,否则高位状态位会污染结果,表现为读数周期性跳变。
5.2 检查 IIC 通信本身是否可靠
- 读取 STATUS 寄存器,确认转换完成标志,避免读到"正在更新"的中间值。
- 检查 HAL_I2C 返回值,通信失败时不要用旧值参与判定。
- STM32G030 的 IIC 时钟建议 100 kHz~400 kHz,过高时在长线缆下容易出错。
- 如果使用软件 IIC,注意 SCL 高电平时间与总线电容,必要时降低速率。
六、软件滤波与判定算法
即使硬件优化到位,读数仍会有一定噪声,因此算法层必须做滤波和稳健判定。这是解决"每次下探深度不同"最直接有效的一环。
6.1 多级滤波
- 中值滤波:连续采 5~9 个点取中值,剔除偶发尖峰(IIC 误码、电磁脉冲)。
- 滑动平均:对中值结果再做 8~16 点滑动平均,降低白噪声。
- 一阶低通 :
y = y + α(x - y),α 取 0.1~0.3,兼顾响应速度与平滑度。
c
#define MED_N 5
#define AVG_N 8
static uint32_t med_buf[MED_N];
static uint32_t avg_buf[AVG_N];
static uint8_t med_idx, avg_idx;
static float lpf_y = 0.0f;
static uint32_t median5(uint32_t *a)
{
uint32_t t[MED_N];
for (int i = 0; i < MED_N; i++) t[i] = a[i];
for (int i = 0; i < MED_N - 1; i++)
for (int j = 0; j < MED_N - 1 - i; j++)
if (t[j] > t[j + 1]) { uint32_t tmp = t[j]; t[j] = t[j + 1]; t[j + 1] = tmp; }
return t[MED_N / 2];
}
float FDC2212_GetFiltered(void)
{
uint32_t raw = FDC2212_ReadChannel0();
med_buf[med_idx++ % MED_N] = raw;
uint32_t med = median5(med_buf);
avg_buf[avg_idx++ % AVG_N] = med;
uint32_t sum = 0;
for (int i = 0; i < AVG_N; i++) sum += avg_buf[i];
float avg = (float)sum / AVG_N;
lpf_y += 0.2f * (avg - lpf_y);
return lpf_y;
}
6.2 基线跟踪与差分判定
不要用绝对阈值判定液面,因为环境温漂、湿度、容器材质都会让基线缓慢漂移。正确做法是:
- 下探前在空气中采集一段数据,作为动态基线。
- 判定量使用
Δ = 当前值 - 基线,而不是绝对值。 - 基线用慢速低通持续跟踪(时间常数几秒),只在"未接触液面"状态下更新。
6.3 迟滞 + 连续确认
单点超过阈值就触发,是导致重复性差的直接原因。建议:
c
#define TH_ON 120.0f /* 触发阈值 */
#define TH_OFF 60.0f /* 释放阈值,形成迟滞 */
#define CONFIRM_N 3 /* 连续确认次数 */
static uint8_t confirm_cnt = 0;
static uint8_t touched = 0;
void Liquid_Detect_Task(float delta)
{
if (!touched) {
if (delta > TH_ON) {
if (++confirm_cnt >= CONFIRM_N) {
touched = 1;
confirm_cnt = 0;
/* 记录当前 Z 轴位置作为液面位置 */
Liquid_OnDetected();
}
} else {
confirm_cnt = 0;
}
} else {
if (delta < TH_OFF) {
touched = 0;
}
}
}
阈值应通过实测确定:让探头缓慢接近液面,记录 Δ 随距离变化的曲线,取曲线拐点附近、且明显高于噪声标准差 5~8 倍的值作为 TH_ON。
6.4 下探速度与采样率匹配
如果下探速度过快而采样率不足,两次采样之间探头已经移动了较大距离,判定点自然离散。计算方式:
单次判定位移 = 下探速度 ÷ 有效采样率
例如下探速度 5 mm/s、有效采样率 50 Hz,则每次采样间隔对应 0.1 mm 位移,重复性误差至少在这个量级。若要求更高精度,需要降低下探速度或在接近液面时减速(两段式下探)。
七、推荐的排查顺序
- 静止采集 1000 点,量化噪声水平,确认问题严重程度。
- 检查参考晶振、去耦、屏蔽与线缆固定,排除硬件干扰。
- 核对寄存器配置,尤其是 RCOUNT、DRIVE_CURRENT 和 MSB 掩码。
- 加入中值 + 滑动平均 + 低通三级滤波。
- 改为动态基线 + 差分判定 + 迟滞 + 连续确认。
- 调整下探速度与采样率匹配,必要时两段式下探。
- 重复测试,统计触发点重复性,验证改善效果。
八、总结
FDC2212 读数跳动导致下探深度不一致,很少是单一原因造成的。硬件上要保证参考时钟稳定、电极屏蔽良好、线缆走向固定;配置上要在响应速度允许的前提下加长转换时间、合理设置驱动电流;软件上必须做多级滤波、动态基线跟踪和带迟滞的连续确认判定。按"先量化、再硬件、后算法"的顺序逐项排查,通常可以把触发点重复性从毫米级改善到几十微米级。