如何在循环中实现值切换且不使用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
原代码问题分析
结合提供的完整代码,核心问题如下:
- 无按键消抖:物理按键按下/释放时存在电平抖动,导致状态检测出错
- 阻塞式按键处理:
button_input()函数是死循环等待按键,会完全卡住主循环,使时钟停止更新 - 右侧按键逻辑错误:按住按键时会反复执行状态切换,同时阻塞主循环的时钟更新分支
修改后的代码
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
相关产品推荐
相关产品推荐

