树莓派3B GPIO引脚线程编程问题:双编码器同步读取异常
树莓派3B GPIO线程读取编码器的问题解决与注意事项
代码核心错误分析
你的代码里有两个直接导致第二个线程无法执行的问题:
- Thread的target参数传递错误:你写的
target = encoder_right(t1)是直接调用了函数,而非传递函数对象。如果encoder_right是死循环(编码器读取通常需要持续运行),主线程会卡在执行这个函数的步骤,根本到不了创建Thread2的环节。 - 线程等待逻辑错误:最后一行重复调用了
Thread1.join(),应该改为Thread2.join(),否则主线程只会等待第一个线程结束,不会处理第二个线程。
树莓派3B GPIO使用线程的注意事项
- GPIO初始化(模式设置、引脚输入输出配置、上拉下拉设置)必须在主线程完成,禁止在子线程中重复配置,否则会引发资源冲突。
- 多线程共享的变量(比如编码器计数)必须用
threading.Lock保护,避免多个线程同时读写导致计数混乱。 - 如果用GPIO中断触发编码器计数,RPi.GPIO的中断回调本身会在独立线程运行,此时要注意和自定义线程的资源竞争,同样需要用锁保护共享数据。
- Daemon线程会在主线程结束时被强制终止,如果需要编码器线程持续运行,要么让主线程保持存活,要么不要设置
daemon=True,同时正确调用join()等待所有子线程结束。 - 高频读取编码器脉冲时,要给线程加短延时(比如
time.sleep(0.001)),避免过度占用CPU资源。
修正后的完整代码示例
from threading import Thread, Lock import RPi.GPIO as GPIO import time from datetime import datetime # 共享计数变量,用锁保护避免多线程冲突 right_count = 0 left_count = 0 count_lock = Lock() # 编码器引脚定义(根据实际接线修改) RIGHT_ENCODER_A = 17 RIGHT_ENCODER_B = 27 LEFT_ENCODER_A = 22 LEFT_ENCODER_B = 23 def encoder_right(start_time): global right_count # 增量编码器AB相计数逻辑示例,需根据硬件调整 last_a_state = GPIO.input(RIGHT_ENCODER_A) while True: current_a_state = GPIO.input(RIGHT_ENCODER_A) current_b_state = GPIO.input(RIGHT_ENCODER_B) # 当A相电平变化时,根据B相判断转向并计数 if current_a_state != last_a_state: with count_lock: if current_b_state != current_a_state: right_count += 1 else: right_count -= 1 last_a_state = current_a_state time.sleep(0.001) def encoder_left(start_time): global left_count last_a_state = GPIO.input(LEFT_ENCODER_A) while True: current_a_state = GPIO.input(LEFT_ENCODER_A) current_b_state = GPIO.input(LEFT_ENCODER_B) if current_a_state != last_a_state: with count_lock: if current_b_state != current_a_state: left_count += 1 else: left_count -= 1 last_a_state = current_a_state time.sleep(0.001) # 主线程初始化GPIO GPIO.setmode(GPIO.BCM) # 设置编码器引脚为输入,带上拉电阻(根据编码器类型选择上拉/下拉) GPIO.setup(RIGHT_ENCODER_A, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(RIGHT_ENCODER_B, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(LEFT_ENCODER_A, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(LEFT_ENCODER_B, GPIO.IN, pull_up_down=GPIO.PUD_UP) t1 = datetime.now() # 正确创建线程:target传函数对象,args传参数元组 Thread1 = Thread(target=encoder_right, args=(t1,), daemon=True) Thread2 = Thread(target=encoder_left, args=(t1,), daemon=True) # 启动线程 Thread1.start() Thread2.start() # 主线程保持运行,实时打印计数 try: while True: with count_lock: print(f"右轮计数: {right_count}, 左轮计数: {left_count}") time.sleep(1) except KeyboardInterrupt: # 捕获Ctrl+C,清理GPIO资源 GPIO.cleanup() print("程序已退出")
内容的提问来源于stack exchange,提问作者DEVAPRASAD S
相关产品推荐
相关产品推荐

