znlgis 博客

GIS开发与技术分享

第6章:按键与触摸传感器

本章介绍按键传感器、电容触摸传感器的工作原理与编程方法,重点讲解信号检测与防抖处理。


6.1 按键传感器原理

6.1.1 轻触开关结构

按键模块采用轻触开关(Tactile Switch):

        ┌─────────┐
        │   帽盖  │
        ├─────────┤
   引脚1─┤  弹片   ├─引脚3
        │         │
   引脚2─┤         ├─引脚4
        └─────────┘
  • 引脚 1 和 3 内部相连
  • 引脚 2 和 4 内部相连
  • 按下时 1-3 与 2-4 导通

6.1.2 电路设计

       VCC
        │
      [R1 4.7K]  上拉电阻
        │
        ├─────── S (信号输出到 GPIO)
        │
      [按键]
        │
       GND

工作逻辑

  • 按键松开时:信号端被上拉电阻拉高 → 高电平
  • 按键按下时:信号端直接接地 → 低电平

6.1.3 编码方式解析

按键是最简单的数字编码设备:

状态 信号值 二进制
松开 1 HIGH
按下 0 LOW

6.2 按键检测代码

6.2.1 基础检测

'''
实验:按键状态检测
接线:按键 S 端连接 GP15
'''
from machine import Pin
import time

button = Pin(15, Pin.IN, Pin.PULL_UP)

while True:
    if button.value() == 0:
        print("按键按下!")
    else:
        print("按键松开")
    time.sleep(0.1)

6.2.2 按键控制 LED

'''
实验:按键控制 LED 开关
'''
from machine import Pin
import time

button = Pin(15, Pin.IN, Pin.PULL_UP)
led = Pin(0, Pin.OUT)

led_state = False

while True:
    if button.value() == 0:  # 按键按下
        time.sleep(0.05)     # 防抖延时
        if button.value() == 0:  # 确认按下
            led_state = not led_state
            led.value(led_state)
            print(f"LED {'开' if led_state else '关'}")
            
            # 等待按键松开
            while button.value() == 0:
                time.sleep(0.01)

6.3 按键防抖

6.3.1 抖动现象

机械按键按下时,触点会产生机械振动,导致信号在短时间内多次跳变:

理想信号:   ────┐          ┌────
                └──────────┘

实际信号:   ────┐ ┌─┐┌─┐   ┌─┐┌────
                └─┘ └┘ └───┘ └┘
                │   抖动   │
                └─────────┘

抖动持续时间通常为 5-20ms。

6.3.2 软件防抖

方法 1:延时消抖

def read_button(button, debounce_ms=50):
    """读取按键状态(带防抖)"""
    if button.value() == 0:
        time.sleep_ms(debounce_ms)
        if button.value() == 0:
            return True
    return False

方法 2:状态机消抖

class DebouncedButton:
    """防抖按键类"""
    
    def __init__(self, pin_num, debounce_ms=50):
        self.pin = Pin(pin_num, Pin.IN, Pin.PULL_UP)
        self.debounce_ms = debounce_ms
        self.last_press = 0
        self.last_state = 1
    
    def is_pressed(self):
        """检测按键是否刚被按下(边沿检测)"""
        current = self.pin.value()
        now = time.ticks_ms()
        
        # 状态变化且超过防抖时间
        if current != self.last_state:
            if time.ticks_diff(now, self.last_press) > self.debounce_ms:
                self.last_press = now
                self.last_state = current
                return current == 0  # 按下时返回 True
        return False
    
    def is_down(self):
        """检测按键是否处于按下状态"""
        return self.pin.value() == 0

# 使用示例
button = DebouncedButton(15)

while True:
    if button.is_pressed():
        print("按键按下!")
    time.sleep(0.01)

6.3.3 中断+定时器防抖

from machine import Pin, Timer

button = Pin(15, Pin.IN, Pin.PULL_UP)
led = Pin(0, Pin.OUT)
debounce_timer = Timer()
pending_event = False

def debounce_callback(timer):
    global pending_event
    if button.value() == 0:
        led.toggle()
        print("确认按键事件")
    pending_event = False

def button_irq(pin):
    global pending_event
    if not pending_event:
        pending_event = True
        debounce_timer.init(mode=Timer.ONE_SHOT, period=50, callback=debounce_callback)

button.irq(trigger=Pin.IRQ_FALLING, handler=button_irq)

6.4 电容触摸传感器

6.4.1 工作原理

电容触摸模块基于 TTP223-BA6 芯片,检测人体电容变化:

            人体
             │
           感应片 ─┬─ TTP223 ─── S 输出
             │     │
           [Cs]   GND

工作原理

  1. 感应片与 GND 形成电容 Cs
  2. 人体触摸时,增加寄生电容
  3. TTP223 检测电容变化,输出信号

6.4.2 输出逻辑

与按键模块相反:

状态 信号值
未触摸 低电平 (0)
触摸 高电平 (1)

注意:上电后约 0.5 秒进行自校准,此期间勿触摸。

6.4.3 触摸检测代码

'''
实验:电容触摸检测
接线:触摸模块 S 端连接 GP3
'''
from machine import Pin
import time

touch = Pin(3, Pin.IN)

while True:
    if touch.value() == 1:
        print("触摸感应!")
    else:
        print("未触摸")
    time.sleep(0.1)

6.4.4 触摸调光灯

'''
实验:触摸调光
短触:开/关
长触:调节亮度
'''
from machine import Pin, PWM
import time

touch = Pin(3, Pin.IN)
led = PWM(Pin(0))
led.freq(1000)

brightness = 0
is_on = False
BRIGHTNESS_STEP = 10

def adjust_brightness():
    global brightness
    if brightness >= 100:
        brightness = 10
    else:
        brightness += BRIGHTNESS_STEP
    led.duty_u16(int(brightness / 100 * 65535))
    print(f"亮度: {brightness}%")

while True:
    if touch.value() == 1:  # 检测到触摸
        press_start = time.ticks_ms()
        
        # 等待触摸释放
        while touch.value() == 1:
            duration = time.ticks_diff(time.ticks_ms(), press_start)
            
            if duration > 500:  # 长按超过 500ms
                adjust_brightness()
                time.sleep(0.2)
        
        duration = time.ticks_diff(time.ticks_ms(), press_start)
        
        if duration < 500:  # 短按
            is_on = not is_on
            if is_on:
                led.duty_u16(int(brightness / 100 * 65535))
                print("灯开")
            else:
                led.duty_u16(0)
                print("灯关")
    
    time.sleep(0.05)

6.5 多按键处理

6.5.1 多按键独立检测

'''
实验:三个按键控制三个 LED
'''
from machine import Pin
import time

# 按键列表
buttons = [
    Pin(15, Pin.IN, Pin.PULL_UP),
    Pin(16, Pin.IN, Pin.PULL_UP),
    Pin(17, Pin.IN, Pin.PULL_UP),
]

# LED 列表
leds = [
    Pin(0, Pin.OUT),
    Pin(1, Pin.OUT),
    Pin(2, Pin.OUT),
]

while True:
    for i, button in enumerate(buttons):
        if button.value() == 0:
            leds[i].toggle()
            while button.value() == 0:
                time.sleep(0.01)
    time.sleep(0.01)

6.5.2 组合按键

'''
实验:组合按键检测
'''
from machine import Pin
import time

btn1 = Pin(15, Pin.IN, Pin.PULL_UP)
btn2 = Pin(16, Pin.IN, Pin.PULL_UP)

def get_key_combo():
    """获取按键组合状态"""
    return (btn1.value() << 1) | btn2.value()

NONE = 0b11
BTN1 = 0b01
BTN2 = 0b10
BOTH = 0b00

while True:
    combo = get_key_combo()
    
    if combo == BTN1:
        print("按键1按下")
    elif combo == BTN2:
        print("按键2按下")
    elif combo == BOTH:
        print("双键同时按下!")
    
    time.sleep(0.1)

6.6 AD 按键模块

6.6.1 原理解析

五路 AD 按键通过电阻分压,用单个 ADC 引脚识别 5 个按键:

    VCC
     │
   [按键1] → 直接到 VCC → 最高电压
     │
   [R2]
     │
   [按键2] → VCC 通过 R2 分压
     │
   [R3]
     │
   [按键3] → 继续分压
     │
   [R4]
     │
   [按键4]
     │
   [R5]
     │
   [按键5] → 最低电压(接近 0)
     │
   [R1]
     │
    GND ← 信号输出点

6.6.2 电压计算

无按键时: ADC ≈ 0
按键1: ADC ≈ VCC (65535)
按键2: ADC ≈ VCC × R1/(R1+R2)
按键3: ADC ≈ VCC × R1/(R1+R2+R3)
按键4: ADC ≈ VCC × R1/(R1+R2+R3+R4)
按键5: ADC ≈ VCC × R1/(R1+R2+R3+R4+R5)

6.6.3 AD 按键检测代码

'''
实验:五路 AD 按键
接线:模块 S 端连接 GP26 (ADC0)
'''
from machine import ADC
import time

ad_key = ADC(26)

# ADC 阈值表
KEY_THRESHOLDS = {
    'NONE': (0, 6000),
    'SW5': (6000, 20000),
    'SW4': (20000, 32000),
    'SW3': (32000, 45000),
    'SW2': (45000, 59000),
    'SW1': (59000, 65536),
}

def get_key():
    """识别按下的按键"""
    value = ad_key.read_u16()
    
    for key, (low, high) in KEY_THRESHOLDS.items():
        if low <= value < high:
            return key, value
    return 'NONE', value

while True:
    key, value = get_key()
    
    if key != 'NONE':
        print(f"按键: {key}, ADC值: {value}")
    
    time.sleep(0.1)

6.6.4 AD 按键菜单系统

'''
实验:AD 按键菜单控制
'''
from machine import ADC, Pin
import time

ad_key = ADC(26)
led = Pin(0, Pin.OUT)

menu_index = 0
menu_items = ['亮度低', '亮度中', '亮度高', '闪烁', '关闭']

def get_key():
    value = ad_key.read_u16()
    if value < 6000:
        return None
    elif value < 20000:
        return 'DOWN'
    elif value < 32000:
        return 'UP'
    elif value < 45000:
        return 'LEFT'
    elif value < 59000:
        return 'RIGHT'
    else:
        return 'OK'

def execute_menu(index):
    if index == 0:
        led.value(1)
    elif index == 1:
        led.value(1)
    elif index == 2:
        led.value(1)
    elif index == 3:
        for _ in range(3):
            led.toggle()
            time.sleep(0.2)
    else:
        led.value(0)

while True:
    key = get_key()
    
    if key == 'UP':
        menu_index = (menu_index - 1) % len(menu_items)
        print(f"→ {menu_items[menu_index]}")
    elif key == 'DOWN':
        menu_index = (menu_index + 1) % len(menu_items)
        print(f"→ {menu_items[menu_index]}")
    elif key == 'OK':
        print(f"执行: {menu_items[menu_index]}")
        execute_menu(menu_index)
    
    if key:
        time.sleep(0.3)  # 防止连续触发
    else:
        time.sleep(0.05)

6.7 本章小结

本章介绍了数字输入传感器的使用:

  1. 按键模块:上拉电阻设计,按下为低电平
  2. 触摸模块:基于 TTP223,触摸为高电平
  3. 防抖处理:软件延时或状态机消抖
  4. AD 按键:用电阻分压实现多按键识别
  5. 编码方式:数字按键是最简单的 1-bit 编码

下一章将学习红外与光电传感器。