14、TSP防水方案:防水算法、湿手触控、水下触控

说到触摸屏的防水,我估计不少工程师都头疼过。手机掉水里、下雨天接电话、洗完手滑屏幕……这些场景太常见了。但电容屏天生怕水,水是导体,一滴水珠就能让整块屏幕乱跳。今天我们就来聊聊,怎么让TSP在湿手甚至水下还能正常工作。

14.1 为什么水会影响触摸屏?

先搞清楚原理。电容屏检测的是手指和屏幕之间的电容变化。水也是导体,它会在屏幕上形成一层导电层,产生额外的寄生电容。说白了,水珠就像一堆假手指,让控制器分不清哪个是真手指。

我遇到过最夸张的情况:一杯水洒在样机上,屏幕直接像发了疯一样,图标乱跳,完全没法用。后来一查数据,水珠产生的电容信号比手指还强。嗯,这就是问题的根源。

14.2 防水算法的核心思路

防水算法不是单一技术,而是一套组合拳。我把它拆成三个层面:

  • 信号预处理:先识别哪些区域被水覆盖,把水信号滤掉或削弱。
  • 手指检测优化:在滤水后的信号中,重新计算手指位置。
  • 状态机切换:根据环境(干手、湿手、水下)动态调整算法参数。

说白了,就是让控制器学会“看水”和“忽略水”。

核心原则:水信号通常是大面积、低频率、慢变化的。手指信号是小面积、高频率、快变化的。利用这个差异,就能把两者分开。

14.3 湿手触控的实现

湿手触控是最常见的需求。比如你刚洗完手,或者下雨天用手机。这时候屏幕上有水膜,但水不多。

我个人的习惯做法是:

  1. 提高驱动电压:让信号更强,穿透水膜。
  2. 降低检测阈值:因为水会衰减手指信号,阈值太高就检测不到了。
  3. 启用“水模式”滤波器:专门滤除大面积的水信号。

这里有个坑。我曾经把阈值降得太低,结果干手时反而容易误触。后来加了一个动态阈值调整机制:根据当前检测到的噪声水平,自动调整阈值。干手时阈值高,湿手时阈值低。

小技巧:湿手触控的检测率,很大程度上取决于屏幕表面的疏水涂层。涂层好的屏幕,水珠会滚落,不容易形成连续水膜。硬件和算法要配合好。

14.4 水下触控的挑战

水下触控比湿手难得多。为什么?因为整个屏幕都泡在水里,水是连续的导体,寄生电容非常大。而且水的介电常数(约80)远高于空气(约1),手指的电容信号会被严重衰减。

我记得有一次做水下触控测试,把手机沉到1米深的水池里。结果屏幕完全没反应,手指按上去像按在石头上。后来分析发现,水把驱动信号都短路了,控制器根本收不到有效信号。

解决方案主要有几种:

  • 提高驱动频率:高频信号在水中的衰减更小。
  • 差分检测:同时检测两个相邻电极的信号差,水信号是共模的,可以被差分放大器抑制掉。
  • 专用水下模式:切换成高灵敏度、高驱动电压的模式,同时禁用一些不必要的功能(比如手势识别)。

注意:水下触控目前还做不到和干手一样灵敏。大多数方案只能支持“点按”和“滑动”,不支持多点触控或精细操作。这是物理限制,算法再强也突破不了。

14.5 防水状态机

一个好的防水方案,必须有一个状态机来管理不同场景。我画了一张图,方便你理解:

干手模式 湿手模式 水下模式 恢复模式 检测到水膜 检测到浸水 水膜消失 离开水面 水膜变深 水位下降 异常恢复 恢复完成

状态机的工作逻辑是这样的:

  • 干手模式:默认模式,标准参数。
  • 湿手模式:检测到屏幕上有水膜(通过噪声水平判断),自动切换。
  • 水下模式:检测到屏幕完全浸入水中(信号衰减严重),切换成高灵敏度模式。
  • 恢复模式:当从水下或湿手回到干手时,先进入恢复模式,逐步调整参数,避免误触。

为什么要加恢复模式?我吃过亏。有一次从水下拿出来,直接切回干手模式,结果屏幕上残留的水珠被当成手指,屏幕又乱跳了。加一个恢复模式,等水珠完全蒸发或擦干,再切回去,就稳了。

14.6 代码示例:防水检测逻辑

下面是一段简化的防水检测代码,展示如何判断当前环境:

// 防水状态机示例
typedef enum {
    MODE_DRY,
    MODE_WET,
    MODE_UNDERWATER,
    MODE_RECOVERY
} WaterMode_t;

WaterMode_t currentMode = MODE_DRY;

void WaterDetection_Task(void) {
    // 读取原始信号
    uint32_t noiseLevel = GetNoiseLevel();
    uint32_t signalLevel = GetSignalLevel();
    uint32_t waterCoverage = GetWaterCoverage();
    
    switch (currentMode) {
        case MODE_DRY:
            if (waterCoverage > 30) {
                // 水覆盖面积超过30%,进入湿手模式
                currentMode = MODE_WET;
                SetWetModeParams();
            }
            if (signalLevel < 50 && noiseLevel > 200) {
                // 信号弱且噪声大,可能在水下
                currentMode = MODE_UNDERWATER;
                SetUnderwaterModeParams();
            }
            break;
            
        case MODE_WET:
            if (waterCoverage < 5) {
                // 水基本消失,进入恢复模式
                currentMode = MODE_RECOVERY;
                SetRecoveryModeParams();
            }
            if (signalLevel < 30) {
                // 信号进一步衰减,可能进入水下
                currentMode = MODE_UNDERWATER;
                SetUnderwaterModeParams();
            }
            break;
            
        case MODE_UNDERWATER:
            if (signalLevel > 100 && noiseLevel < 100) {
                // 信号恢复,离开水面
                currentMode = MODE_RECOVERY;
                SetRecoveryModeParams();
            }
            break;
            
        case MODE_RECOVERY:
            // 等待5秒,确认水已消失
            static uint32_t recoveryTimer = 0;
            recoveryTimer++;
            if (recoveryTimer > 500) {  // 假设每10ms调用一次
                recoveryTimer = 0;
                currentMode = MODE_DRY;
                SetDryModeParams();
            }
            break;
    }
}

这段代码很简化,但核心逻辑都在了。实际产品中,判断条件会更复杂,比如要结合多个传感器的数据,还要考虑温度、湿度等环境因素。

14.7 避坑指南

最后,分享几个我踩过的坑:

  • 不要只依赖算法:硬件设计也很重要。屏幕的密封、疏水涂层、电极布局,都会影响防水效果。算法和硬件要一起优化。
  • 注意充电时的防水:手机充电时,USB口可能有水,容易造成短路。防水算法要能识别充电状态,必要时禁用触控。
  • 水下触控的功耗:水下模式需要更高的驱动电压和频率,功耗会明显增加。记得在离开水下后及时切回普通模式。
  • 测试要全面:不要只在实验室测。我试过在海水、游泳池、自来水三种环境下测试,结果差异很大。海水的导电性更强,算法参数要重新调。

一句话总结:防水方案的核心,就是让控制器学会“看水”和“忽略水”。算法是灵魂,硬件是骨架,两者缺一不可。


公众号:蓝海资料掘金营,微信deep3321