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

ESP32-C3平台MicroPython _thread引脚读取致LED显示卡顿问题

ESP32-C3 MicroPython多线程下七段LED刷新缓慢问题排查与解决

问题背景

  • 硬件平台:Espressif ESP32-C3
  • 语言:MicroPython
  • 现象:使用_thread模块开启两个线程后,驱动多位七段LED的displaytime()函数运行极慢,单个LED刷新约需1秒;但将detec_btn()中的pin_button.value()改为True后,displaytime()恢复正常。

问题代码

# define a button
pin_button = Pin(6, Pin.IN)
Buttpushflag = 1
btn_value = 0

def detec_btn():
    global  btn_value
    global  gpio_value
    while True: 
        if pin_button.value() == Buttpushflag:
            time.sleep_ms(1000)
            if pin_button.value() == Buttpushflag:
                #print("button push valid")
                btn_value = 1
                gpio_value = "1"
        else:
            btn_value = 0
            #print("button push no valid")
            gpio_value = "0"

# sacn muti 7 segments LED 
def displaytime():
    while True:
        #current_time = time.localtime()
        current_time = ds.get_time()
        #print(current_time)
        time_hour = current_time[3]
        time_min = current_time[4]
        time_sed = current_time[5]
        
        sed_h, sed_l = divid_2num(time_sed)
        display1num(2, sed_l, "0")
        time.sleep(0.001)
        display1num(3, sed_h, "0")
        time.sleep(0.001)
        
        min_h, min_l = divid_2num(time_min)
        display1num(4, min_l, "1")
        time.sleep(0.001)
        display1num(5, min_h, "0")
        time.sleep(0.001)
        #display1num(6, "-", "0")
        
        hour_h, hour_l = divid_2num(time_hour)
        display1num(6, hour_l, "1")
        time.sleep(0.001)
        display1num(7, hour_h, "0")
        time.sleep(0.001)
        

_thread.start_new_thread(displaytime,())
_thread.start_new_thread(detec_btn,())

原因分析

  1. MicroPython线程调度机制限制:MicroPython的_thread模块实现的是协作式多线程,而非抢占式。线程之间需要主动释放CPU资源(比如通过time.sleep()这类触发调度的操作),其他线程才能获得执行机会。
  2. 按钮检测线程的阻塞问题:
    • 当按钮未被按下时,detec_btn()线程会进入else分支,无延迟地循环执行赋值操作,持续占用CPU,完全不给displaytime()线程执行机会。
    • 只有按钮被按下时,线程执行time.sleep_ms(1000)才会触发调度,但这1秒的延迟直接导致LED刷新变慢。
  3. 修改为True后的变化:将pin_button.value()改为True后,detec_btn()会一直进入if分支并执行time.sleep_ms(1000),每次睡眠都会触发调度,displaytime()就能正常获取CPU时间,LED因此恢复正常刷新。

解决办法

针对协作式多线程的特点,需让按钮检测线程主动释放CPU,避免独占资源:

方法1:在按钮检测循环中添加短延迟

修改detec_btn()函数,在循环末尾添加短延迟,主动触发线程调度:

def detec_btn():
    global btn_value
    global gpio_value
    while True: 
        if pin_button.value() == Buttpushflag:
            time.sleep_ms(1000)
            if pin_button.value() == Buttpushflag:
                btn_value = 1
                gpio_value = "1"
        else:
            btn_value = 0
            gpio_value = "0"
        # 添加短延迟,释放CPU给其他线程
        time.sleep_ms(10)

方法2:使用GPIO中断替代轮询

避免轮询占用CPU,改用GPIO外部中断检测按钮按下,无需持续循环,效率更高:

# 定义中断回调函数
def btn_callback(pin):
    global btn_value, gpio_value
    # 防抖处理
    time.sleep_ms(100)
    if pin.value() == Buttpushflag:
        btn_value = 1
        gpio_value = "1"
    else:
        btn_value = 0
        gpio_value = "0"

# 初始化按钮并注册中断
pin_button = Pin(6, Pin.IN)
# 根据按钮接线选择触发方式,示例为按下时触发上升沿
pin_button.irq(trigger=Pin.IRQ_RISING, handler=btn_callback)

# 仅保留displaytime线程
_thread.start_new_thread(displaytime,())

方法3:优化轮询逻辑

若必须使用轮询,可只在按钮状态变化时更新值,同时保留短延迟:

def detec_btn():
    global btn_value, gpio_value
    last_state = pin_button.value()
    while True:
        current_state = pin_button.value()
        if current_state != last_state:
            time.sleep_ms(100)  # 防抖
            current_state = pin_button.value()
            if current_state == Buttpushflag:
                btn_value = 1
                gpio_value = "1"
            else:
                btn_value = 0
                gpio_value = "0"
            last_state = current_state
        time.sleep_ms(10)  # 释放CPU

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 13:10:08