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

树莓派Pico多线程故障:LED与旋转编码器LCD菜单无法协同运行

树莓派Pico多线程与旋转编码器冲突问题排查与解决

问题现象

代码运行初期LED可正常闪烁,但转动旋转编码器后,LED停止工作且编码器无输出。LED、旋转编码器及LCD模块单独运行均正常,问题集中在多线程实现逻辑上。

核心原因分析

  1. 多线程与中断的调度冲突:原代码使用_thread模块在第二核心运行LED闪烁线程,线程内的utime.sleep()会占用核心调度资源。而旋转编码器依赖GPIO中断更新计数器,中断触发时可能与线程的睡眠调度产生冲突,导致LED线程被意外挂起,同时编码器的中断处理也受干扰。
  2. 计数器读取的竞争条件:主线程直接读取编码器的Enc_Counter值,而该值是在中断服务函数中更新的。如果读取操作与中断更新同时发生,可能读到不完整的数值,导致后续逻辑异常,进而卡住整个程序。

解决方案

1. 用硬件Timer替代线程实现LED闪烁

硬件Timer不依赖CPU线程调度,定时触发更稳定,避免与中断或其他线程冲突。

2. 对编码器计数器读取添加中断保护

在主线程读取计数器时,临时禁用全局中断,确保读取到完整的计数器值,读取完成后恢复中断。

修改后的完整代码示例

import machine
import utime
from rotary_encoder_classes import *

clock_pin = machine.Pin(25, machine.Pin.OUT)
frequency = 1

# 用硬件Timer实现LED闪烁,替代多线程
def clock_callback(timer):
    clock_pin.toggle()

# 初始化Timer:频率设置为2*目标频率,实现亮灭各占一半周期
timer = machine.Timer()
timer.init(freq=frequency * 2, mode=machine.Timer.PERIODIC, callback=clock_callback)

# 主菜单
pointer = 0
main_menu_options = ["> Clock Frequency", "> IC Tester", "> Voltmeter"]

# LCD初始化
from pico_i2c_lcd import I2cLcd
i2c = machine.I2C(0, sda=machine.Pin(0), scl=machine.Pin(1), freq=400000)
I2C_ADDR = i2c.scan()[0]
lcd = I2cLcd(i2c, I2C_ADDR, 2, 16)
lcd.putstr(main_menu_options[pointer])

# 旋转编码器变量初始化
last_Enc_Counter_1 = 0
Enc_Counter_1 = 0
Last_Qtr_Cntr_1 = 0
Qtr_Cntr_1 = 0
error_1 = 0

last_Enc_Counter_2 = 0
Enc_Counter_2 = 0
Last_Qtr_Cntr_2 = 0
Qtr_Cntr_2 = 0
error_2 = 0

# 初始化编码器
Enc_1 = R_Encoder(15, 14)
Enc_1.DisplayPins()
Enc_1.Reset_Counter()

Enc_2 = R_Encoder(12, 11)
Enc_2.DisplayPins()
Enc_2.Reset_Counter()

# 编码器开关初始化
Enc_1_SW = machine.Pin(13, machine.Pin.IN, machine.Pin.PULL_UP)
Enc_1_SW_State = "UP"

# 主线程循环
while True:
    utime.sleep(.01)
    
    # 读取编码器1计数器:添加中断保护
    irq_state = machine.disable_irq()
    Qtr_Cntr_1 = round(Enc_1.Enc_Counter / 4)
    machine.enable_irq(irq_state)
    
    if Qtr_Cntr_1 != Last_Qtr_Cntr_1:
        print(Qtr_Cntr_1)
        last_Enc_Counter_1 = Enc_1.Enc_Counter
        Last_Qtr_Cntr_1 = Qtr_Cntr_1
        
    # 开关状态检测
    if (Enc_1_SW.value() == True) and (Enc_1_SW_State == "DOWN"):
        Enc_1_SW_State = "UP"
        print("Switch is UP")
    elif (Enc_1_SW.value() == False) and (Enc_1_SW_State == "UP"):
        Enc_1_SW_State = "DOWN"
        print("Switch is DOWN")
        
    # 读取编码器2计数器:添加中断保护
    irq_state = machine.disable_irq()
    Qtr_Cntr_2 = round(Enc_2.Enc_Counter / 4)
    machine.enable_irq(irq_state)
    
    if Qtr_Cntr_2 != Last_Qtr_Cntr_2:
        print(Qtr_Cntr_1, Qtr_Cntr_2)
        last_Enc_Counter_2 = Enc_2.Enc_Counter
        Last_Qtr_Cntr_2 = Qtr_Cntr_2

额外检查项

  • 确认R_Encoder类的中断服务函数仅执行计数器加减操作,无print()或其他耗时逻辑,避免中断阻塞系统。
  • 若问题仍存在,可尝试降低I2C总线频率(如从400000改为100000),排除LCD与编码器的总线干扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 06:32:01