You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在循环中实现值切换且不使用time.sleep()?

问题与解决方案

问题描述

需要在程序循环中实现一个可切换的值,且不能使用time.sleep()(会导致程序完全停滞,无法响应按键操作)。尝试两种方法均存在问题:

  • 直接检测按键按下:值切换速度过快,每帧都会触发
    while True:
        if button.is_pressed():
            # do task
    
  • 状态对比触发:需要按下再释放才能再次触发,且偶尔按键无响应,按住按键时程序会停滞,严重影响数字时钟的正常运行
    while True:
        if button.value:
            cur_state_right = button.value
            if cur_state_right != prev_state_right:
                # do task
            prev_state_right = cur_state_right
    

原代码问题分析

结合提供的完整代码,核心问题如下:

  1. 无按键消抖:物理按键按下/释放时存在电平抖动,导致状态检测出错
  2. 阻塞式按键处理:button_input()函数是死循环等待按键,会完全卡住主循环,使时钟停止更新
  3. 右侧按键逻辑错误:按住按键时会反复执行状态切换,同时阻塞主循环的时钟更新分支

修改后的代码

import board
import busio
from adafruit_ht16k33 import segments
import adafruit_gps
from adafruit_datetime import timedelta, datetime
import time
import digitalio
import microcontroller

# 初始化按键
btn_right = digitalio.DigitalInOut(board.GP5)
btn_right.switch_to_input(pull=digitalio.Pull.UP)

btn_confirm = digitalio.DigitalInOut(board.GP9)
btn_confirm.switch_to_input(pull=digitalio.Pull.UP)

btn_left = digitalio.DigitalInOut(board.GP13)
btn_left.switch_to_input(pull=digitalio.Pull.UP)

# 初始化外设
i2c = busio.I2C(board.GP27, board.GP26)
display = segments.Seg7x4(i2c)
gps = adafruit_gps.GPS_GtopI2C(i2c)

display.fill(1)
time.sleep(1)
display.fill(0)

# GPS配置
gps.send_command(b"PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0")  # 开启时间和日期数据
gps.send_command(b"PMTK220,1000")  # 1Hz更新频率
gps.send_command(b"PMTK285,2,100") # 激活PPS

# 文件操作函数
def load(filename):
    f = open(filename + ".txt", "r")
    output = f.read()
    f.close()
    return output
    
def save(filename, data):
    f = open(filename + ".txt", "w")
    f.write(data)
    f.close()

# 非阻塞按键检测(带消抖)
def check_button(btn, prev_state, last_debounce_time, debounce_delay=0.05):
    current_state = btn.value
    current_time = time.monotonic()
    
    # 消抖:状态变化后等待稳定
    if current_state != prev_state:
        last_debounce_time = current_time
    
    # 状态稳定后确认有效变化
    if (current_time - last_debounce_time) > debounce_delay:
        if current_state != prev_state:
            prev_state = current_state
            return prev_state, last_debounce_time, True  # 返回状态变化
    return prev_state, last_debounce_time, False

# 初始化按键状态和消抖时间
prev_left = btn_left.value
prev_confirm = btn_confirm.value
prev_right = btn_right.value
last_left_debounce = time.monotonic()
last_confirm_debounce = time.monotonic()
last_right_debounce = time.monotonic()

# DST设置(初始化阶段逻辑)
def savings_check():
    dst = 0
    while True:
        display.fill(0)
        display.print(dst)
        cur_left = btn_left.value
        cur_confirm = btn_confirm.value
        cur_right = btn_right.value
        
        if not cur_confirm:
            time.sleep(0.2) # 短时间消抖避免误触
            while not btn_confirm.value:
                pass # 等待按键释放
            break
        elif not cur_right:
            time.sleep(0.2)
            while not btn_right.value:
                pass
            dst += 1
        elif not cur_left:
            time.sleep(0.2)
            while not btn_left.value:
                pass
            dst -= 1
        time.sleep(0.1) # 避免循环过快
    return dst

dst = savings_check()

# 加载GPS时间
gps.update()
while True:
    try:
        time.sleep(1)
        gps.update()
        timeset = datetime(
            gps.timestamp_utc.tm_year,
            gps.timestamp_utc.tm_mon,
            gps.timestamp_utc.tm_mday,
            gps.timestamp_utc.tm_hour,
            gps.timestamp_utc.tm_min,
            gps.timestamp_utc.tm_sec,
        )
        break
    except Exception as e:
        display.print("LOAD")
        print(e)
        print("Time load fail. Retrying. Satellites:", gps.satellites)
print("Load success!")

if dst != 0:
    timeset = timeset + timedelta(hours=dst)

timeset = timeset + timedelta(seconds=15)

last_print = time.monotonic()
pps_state = True

# 主循环(非阻塞)
while True:
    current = time.monotonic()
    
    # 检测所有按键状态(带消抖)
    prev_left, last_left_debounce, left_changed = check_button(btn_left, prev_left, last_left_debounce)
    prev_confirm, last_confirm_debounce, confirm_changed = check_button(btn_confirm, prev_confirm, last_confirm_debounce)
    prev_right, last_right_debounce, right_changed = check_button(btn_right, prev_right, last_right_debounce)
    
    # 处理时钟更新(无按键操作时)
    if prev_left and prev_confirm and prev_right:
        if current - last_print >= 1.0:
            last_print = current
            timeset = timeset + timedelta(seconds=1)
            display.print(f"{timeset.hour:02d}{timeset.minute:02d}")
            print(timeset, "With", gps.satellites, "Satellites")
            display.colon = (timeset.second % 2 == 0)
    # 处理左按键(显示日期)
    elif not prev_left:
        display.colon = False
        display.print(f"{timeset.day:02d}{timeset.month:02d}")
    # 处理确认按键(显示年份)
    elif not prev_confirm:
        display.colon = False
        display.print(timeset.year)
    # 处理右按键(切换PPS状态,仅在按键按下边缘触发)
    elif not prev_right:
        if right_changed: # 仅在状态变化时触发一次
            pps_state = not pps_state
            if pps_state:
                gps.send_command(b"PMTK285,2,100")
                print("PPS on")
            else:
                gps.send_command(b"PMTK285,0,100")
                print("PPS off")
    # 三键同时按下重启
    elif not prev_left and not prev_confirm and not prev_right:
        microcontroller.reset()

关键修改点

  • 新增check_button()函数,实现非阻塞式按键消抖,避免电平抖动导致的误触发
  • 主循环中同时检测所有按键状态,保证时钟更新逻辑不会被阻塞
  • 右侧按键仅在状态变化边缘触发一次切换,按住按键时不会重复执行
  • 修改savings_check()函数的按键处理,加入简单消抖,避免初始化阶段误触

内容的提问来源于stack exchange,提问作者Commodore 64

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 02:14:56