树莓派Pico多线程故障:LED与旋转编码器LCD菜单无法协同运行
树莓派Pico多线程与旋转编码器冲突问题排查与解决
问题现象
代码运行初期LED可正常闪烁,但转动旋转编码器后,LED停止工作且编码器无输出。LED、旋转编码器及LCD模块单独运行均正常,问题集中在多线程实现逻辑上。
核心原因分析
- 多线程与中断的调度冲突:原代码使用
_thread模块在第二核心运行LED闪烁线程,线程内的utime.sleep()会占用核心调度资源。而旋转编码器依赖GPIO中断更新计数器,中断触发时可能与线程的睡眠调度产生冲突,导致LED线程被意外挂起,同时编码器的中断处理也受干扰。 - 计数器读取的竞争条件:主线程直接读取编码器的
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
相关产品推荐
相关产品推荐

