我有一个控制旋转机械编码器的类。事物的更新方面(读取状态,推断增量和更新steps
属性)在单独的线程中处理,更新的step
值在get_steps
中传递出去。 button_callback
函数也通过GPIO.add_event_detect
的宽限度在单独的线程中处理。根据{{3}},我使用了两个互斥对象,因为主线程读取或重置属性的时间可能不确定:
第一个是通过按编码器按钮来控制所传递的button_callback
函数的执行-所传递的函数可能会编辑一个全局变量,也可以由主线程的其他元素来作用。我知道button_lock
将需要在主线程中的其他位置部署相关的变量,但是我相当有信心。
第二个方法是控制对属性steps
的访问,特别是停止reset
方法(可能由主线程调用),从而在更新周期中更改step属性。我不确定这里和get_steps
方法中的任何操作都是原子操作,或者是否需要互斥锁。
import RPi.GPIO as GPIO
import math
from threading import Thread, Lock
import time
class Encoder:
enc_A = 27
enc_B = 17
switch = 4
def __init__(self, button_callback=None, button_args=None):
GPIO.setwarnings(True)
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.enc_A, GPIO.IN)
GPIO.setup(self.enc_B, GPIO.IN)
if button_callback is not None:
GPIO.setup(self.switch, GPIO.IN)
self.button_lock = Lock()
def cb(channel):
with self.button_lock:
return button_callback(channel, button_args) if button_args is not None else button_callback(channel)
GPIO.add_event_detect(self.switch, GPIO.FALLING, callback=cb)
self.lock = Lock()
self.r_seq = self.get_rseq()
self.last_delta = 0
self.reset()
self.threadRun = False
self.start()
def get_rseq(self):
a = GPIO.input(self.enc_A)
b = GPIO.input(self.enc_B)
return (a ^ b) | b << 1
def update(self):
while self.threadRun:
r_seq = self.get_rseq()
delta = 0
if r_seq != self.r_seq:
delta = (r_seq - self.r_seq) % 4
if delta == 3:
delta = -1
elif delta == 2:
delta = int(math.copysign(delta, self.last_delta))
self.last_delta = delta
self.r_seq = r_seq
with self.lock:
self.steps += delta
time.sleep(0.002)
GPIO.remove_event_detect(self.switch)
GPIO.cleanup()
def get_steps(self):
with self.lock:
return self.steps
def reset(self):
with self.lock:
self.steps = 0
def stop(self):
self.threadRun = False
def start(self):
self.threadRun = True
Thread(target=self.update).start()
我希望此代码尽可能简单明了,并且使用Python语言编写,其中包括正确使用互斥量。就目前的情况而言,对于我的用法,此代码有效,但是我对寻求良好的实践非常感兴趣,因此在这种情况下,我很乐意听取建议。
注意:编码器的处理基于Python 3 FAQs-作者的贡献。