Bootstrap

树莓派pico入坑笔记,触摸引脚

这里主要是扒一扒官方是如何实现触摸引脚的功能的,rp2040本身是不支持触摸传感的,那么官方是怎么实现的呢,这件事一直困扰着我,在官方给出的使用中,要求外接一颗至少为1M欧姆的电阻下拉,然后就可以使用触摸引脚了,并没有指定要求引脚的属性,比如必须是ADC引脚。

一、触摸引脚的基本概念

触摸引脚技术是一种通过检测引脚上的电容变化或电压变化来实现触摸功能的技术。它广泛应用于触摸屏、触摸按键等场景。有多种方式实现触摸功能,包括电容触摸和电阻触摸等。

二、电容触摸引脚的实现原理

1. 电容触摸的基本原理

电容触摸是基于电容变化来检测触摸的。当手指靠近触摸引脚时,人体的电容会与引脚形成一个电容耦合,导致引脚上的电容值发生变化。通过检测这种电容变化,可以判断是否发生了触摸操作。

  • 当手指靠近时,电容增大,电荷量也会相应变化,从而导致电压发生变化。
  • 电容增大,导致放电时间发生变化。
2. 电容触摸实现

微控制器可以通过其内部的ADC(模数转换器)和定时器来实现电容触摸功能。具体步骤如下:

  1. 初始化引脚:将触摸引脚配置为输入模式,并连接到ADC通道。
  2. 充电过程:通过定时器控制,对触摸引脚充电到一个固定电压(如VDD)。
  3. 放电检测:断开充电源,让引脚通过内部电阻放电,同时通过ADC采样引脚电压。
  4. 计算电容变化:根据ADC采样值的变化,计算出电容的变化量。如果电容变化超过阈值,则判断为触摸。
3. 优点与缺点
  • 优点:非接触式,灵敏度高,使用寿命长。
  • 缺点:容易受到电磁干扰,需要复杂的算法来消除噪声。

三、官方的实现

地址circuitpython/shared-module/touchio/TouchIn.c at main · adafruit/circuitpython (github.com)

核心代码

static uint16_t get_raw_reading(touchio_touchin_obj_t *self) {
    uint16_t ticks = 0;

    for (uint16_t i = 0; i < N_SAMPLES; i++) {
        // set pad to digital output high for 10us to charge it
        common_hal_digitalio_digitalinout_switch_to_output(self->digitalinout, true, DRIVE_MODE_PUSH_PULL);
        mp_hal_delay_us(10);

        // set pad back to an input and take some samples
        common_hal_digitalio_digitalinout_switch_to_input(self->digitalinout, PULL_NONE);

        while (common_hal_digitalio_digitalinout_get_value(self->digitalinout)) {
            if (ticks >= TIMEOUT_TICKS) {
                return TIMEOUT_TICKS;
            }
            ticks++;
        }
    }
    return ticks;
}

该函数用于获取原始的触摸感应读数。

  1. 循环 N_SAMPLES 次进行采样。
  2. 在每次采样中,将引脚设置为数字输出并拉高 10 微秒以对电容充电。
  3. 然后将引脚设置为高阻抗输入,通过一个忙等待循环来测量电容放电所需的时间(以 ticks 为单位)。
  4. 如果 ticks 超过 TIMEOUT_TICKS,则返回 TIMEOUT_TICKS

四、具体步骤如下:

  1. 初始化变量

    uint16_t ticks = 0;
    
     

    定义一个变量 ticks 用于记录电容放电所需的时间(以时钟周期为单位),初始值为 0。

  2. 多次采样循环

    for (uint16_t i = 0; i < N_SAMPLES; i++) {
    
     

    通过 for 循环进行 N_SAMPLES 次采样,这样做的目的是为了减少噪声的影响,提高读数的准确性。

  3. 对电容充电

    common_hal_digitalio_digitalinout_switch_to_output(self->digitalinout, true, DRIVE_MODE_PUSH_PULL);
    mp_hal_delay_us(10);
    
     
    • common_hal_digitalio_digitalinout_switch_to_output 函数将连接触摸感应垫的引脚设置为数字输出模式,并将其电平拉高。
    • mp_hal_delay_us(10) 函数使程序暂停 10 微秒,确保电容有足够的时间充电。
  4. 电容放电并计数

    common_hal_digitalio_digitalinout_switch_to_input(self->digitalinout, PULL_NONE);
    
    while (common_hal_digitalio_digitalinout_get_value(self->digitalinout)) {
        if (ticks >= TIMEOUT_TICKS) {
            return TIMEOUT_TICKS;
        }
        ticks++;
    }
    
     
    • common_hal_digitalio_digitalinout_switch_to_input 函数将引脚切换为高阻抗输入模式,此时电容开始通过连接到地的电阻放电。
    • while 循环持续检查引脚的电平,只要引脚电平为高,就不断增加 ticks 的值。
    • 如果 ticks 的值超过了 TIMEOUT_TICKS,说明电容放电时间过长,可能存在问题,函数将直接返回 TIMEOUT_TICKS
  5. 返回读数

    return ticks;
    
     

    经过 N_SAMPLES 次采样后,函数返回最终的 ticks 值,该值反映了电容放电所需的总时间,可用于后续判断是否有触摸动作。

优点
  • 简单易懂:代码逻辑清晰,通过简单的数字引脚操作和循环计数实现了电容式触摸感应的基本功能,易于理解和实现。
  • 可配置性:通过调整 N_SAMPLES 和 TIMEOUT_TICKS 等宏定义的值,可以根据实际需求调整采样次数和超时阈值,提高了代码的灵活性。
  • 抗噪声能力:多次采样取平均的方式可以有效减少噪声对读数的影响,提高了触摸感应的准确性。
缺点
  • 精度有限:使用忙等待循环来计数电容放电时间,其精度受限于系统时钟频率和循环开销,可能无法满足对精度要求较高的应用场景。
  • 性能较低:忙等待循环会占用 CPU 资源,导致系统在采样期间无法执行其他任务,影响系统的整体性能。
  • 环境适应性差:该方法对环境变化较为敏感,如温度、湿度等因素可能会影响电容的充电和放电特性,从而导致读数不准确。

总结

实现方法非常巧妙,利用寄生电容充放电以及普通gpio检测电平,只需要外接大电阻下拉就可以低成本实现触摸检测,同时多次采样可以降低误差。

;