RC充放电检测法:从原理到实战

各位同学,今天我们来聊聊触摸检测里最经典、也最基础的方法——RC充放电检测法。说实话,这个方法我用了快十年了,从最早的电阻触摸屏到现在的电容式触摸按键,它始终是嵌入式工程师的必修课。

为什么这么说?因为它的原理简单,实现成本低,而且抗干扰能力还不错。我当年做第一个触摸项目时,就是用几个电阻电容加一个IO口搞定的。嗯,咱们一步步来看。

RC电路原理:电容充放电的本质

先说说RC电路。说白了,就是一个电阻和一个电容串联在一起。当给这个电路通电时,电容会慢慢充电;断电时,电容会慢慢放电。这个「慢慢」有多慢?取决于RC时间常数τ。

公式很简单:τ = R × C

τ的单位是秒。比如10kΩ电阻配100nF电容,τ = 10k × 100n = 1ms。这意味着电容电压充到63.2%需要1ms,充到95%需要3τ,也就是3ms。

我习惯用一个经验值:充到电源电压的63.2%需要1τ,充到90%需要2.3τ,充到99%需要4.6τ。这些数字你记在心里,调试时很有用。

关键点:电容值变化时,充放电时间会跟着变。触摸传感器就是利用这个原理——手指靠近会改变电容值,从而改变充放电时间。

充放电时间测量:怎么测才准?

测量充放电时间,常用的方法有两种:一种是测充电时间,一种是测放电时间。我个人更推荐测放电时间,为什么?因为放电时电容初始电压是确定的,测量误差小。

具体怎么做?看这个流程:

  1. 先把IO口设为输出,拉高电平,给电容充电到VCC
  2. 然后把IO口设为输入(高阻态),同时启动定时器
  3. 电容通过外部电阻放电,电压慢慢下降
  4. 当电压降到某个阈值(比如0.7V或1.5V)时,IO口检测到低电平,停止定时器
  5. 读定时器值,这就是放电时间

我在项目中遇到过一个问题:IO口的输入阈值电压不是固定的。不同芯片、不同温度下,阈值可能从0.7V漂到1.2V。这会导致测量结果不稳定。后来我改用比较器或者ADC来检测阈值,效果就好多了。

小技巧:如果芯片有施密特触发输入,尽量用它。施密特触发有滞回特性,能有效抑制噪声引起的误触发。

阈值比较法实现:代码怎么写?

阈值比较法的核心思想是:设定一个基准时间T0(没有触摸时的放电时间),然后实时测量放电时间T。如果T比T0大很多,说明有触摸。

为什么T会变大?因为手指靠近时,电容值增加了,放电变慢了。你想想看,电容从10pF变成12pF,放电时间就多了20%。这个变化足够我们检测到。

下面是一个典型的实现代码,我用的是STM32的HAL库:

// 初始化
#define THRESHOLD_TOUCH  50   // 触摸阈值,单位:定时器计数
#define THRESHOLD_RELEASE 30  // 释放阈值,带滞回

uint16_t base_time = 0;       // 基准时间
uint16_t measure_time = 0;    // 实测时间

// 测量一次放电时间
uint16_t measure_discharge_time(void) {
    uint16_t count = 0;
    
    // 1. 充电:IO口输出高
    GPIO_SetPin(GPIO_PIN, GPIO_MODE_OUTPUT_PP);
    GPIO_WritePin(GPIO_PIN, GPIO_PIN_SET);
    delay_us(10);  // 确保充满
    
    // 2. 开始放电:IO口设为输入,同时启动定时器
    GPIO_SetPin(GPIO_PIN, GPIO_MODE_INPUT);
    TIM_Cmd(TIM2, ENABLE);
    
    // 3. 等待电压降到阈值以下
    while(GPIO_ReadPin(GPIO_PIN) == GPIO_PIN_SET) {
        count++;
        if(count > 10000) break;  // 超时保护
    }
    
    // 4. 停止定时器,返回计数值
    TIM_Cmd(TIM2, DISABLE);
    return TIM_GetCounter(TIM2);
}

// 触摸检测主循环
void touch_scan(void) {
    measure_time = measure_discharge_time();
    
    if(measure_time > (base_time + THRESHOLD_TOUCH)) {
        // 检测到触摸
        touch_flag = 1;
    } else if(measure_time < (base_time + THRESHOLD_RELEASE)) {
        // 触摸释放
        touch_flag = 0;
    }
}

注意看,我用了两个阈值:THRESHOLD_TOUCH和THRESHOLD_RELEASE。这就是滞回比较,防止在临界点来回跳变。我曾经吃过这个亏——不加滞回,手指轻轻一碰,输出信号就在0和1之间疯狂抖动。

避坑指南:我曾经在量产时发现,同一批板子,有的灵敏度高,有的灵敏度低。查了半天,发现是PCB走线寄生电容不一致导致的。解决办法是:每块板子出厂前做一次自动校准,把base_time重新测一遍。

RC充放电检测法的知识体系

下面这张图是我画的,把整个方法的逻辑串起来了。你可以看到,从物理原理到软件实现,每一步都有对应的处理手段。

RC充放电检测法知识体系 物理层:RC充放电原理 τ = R × C | 电容变化 → 时间变化 | 手指靠近 → 电容增大 测量层:充放电时间测量 充电法 | 放电法(推荐) | 定时器捕获 | 阈值检测 算法层:阈值比较法 基准时间T0 | 实时测量T | 滞回比较 | 自动校准 工程层:抗干扰与优化 施密特触发 | 软件滤波 | 温度补偿 | 寄生电容校准

实际项目中的参数选择

参数怎么选?我直接给个参考表,这是我做项目时常用的组合:

应用场景 电阻R 电容C 理论τ 测量分辨率
触摸按键(小面积) 10kΩ 10pF(含寄生) 0.1μs 高(需高速定时器)
触摸按键(大面积) 100kΩ 50pF(含寄生) 5μs
接近感应 1MΩ 100pF(含寄生) 100μs 低(但检测距离远)
水位检测 470kΩ 200pF(含寄生) 94μs 中(需防电解)

注意,这里的电容C包含了PCB走线的寄生电容。我习惯在设计时预留一个调试焊盘,方便用示波器实测波形。嗯,说到示波器,调试时一定要看波形——充电曲线是不是平滑的?放电有没有毛刺?这些一眼就能看出来。

经验之谈:如果测量结果跳动很大,先别急着改算法。用示波器看看电源纹波,很多时候是电源噪声耦合到触摸通道上了。加个100nF去耦电容,问题就解决了。

总结一下

RC充放电检测法,说白了就是利用电容充放电的时间变化来感知触摸。它的优点是简单、成本低、不需要专用芯片。缺点是受寄生电容和温度影响较大,需要做校准和补偿。

我个人建议,初学者先用这个方法入门,把原理吃透。等你真正理解了电容检测的本质,再去学更高级的电荷转移法、互电容检测法,会事半功倍。

好了,这一节的内容就到这里。记住:理论要懂,但动手更重要。拿块开发板,焊几个电阻电容,写几行代码,你才能真正掌握它。


专注资料整理