From a820755f5de005bf4790c62dd327d21e3386676d Mon Sep 17 00:00:00 2001 From: Peter Hinch Date: Mon, 5 Sep 2016 09:14:38 +0100 Subject: [PATCH] Encoder code added --- README.md | 12 ++++++++++ encoders/encoder.py | 28 +++++++++++++++++++++++ encoders/encoder_timed.py | 47 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 encoders/encoder.py create mode 100644 encoders/encoder_timed.py diff --git a/README.md b/README.md index 310f4f3..0c85c68 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,18 @@ the same topic. Measures the round-trip delay. Adapt to suit your server address QOS (quality of service, 0 and 1 are supported). After 100 messages reports maximum and minimum delays. +## Rotary Incremental Encoder + +Classes for handling incremental rotary position encoders. Note that the Pyboard timers can +do this in hardware. These samples cater for cases where that solution can't be used. The +encoder_timed.py sample provides rate information by timing successive edges. In practice this +is likely to need filtering to reduce jitter caused by imperfections in the encoder geometry. + +There are other algorithms but this is the simplest and fastest I've encountered. + +These were written for encoders producing TTL outputs. For switches, adapt the pull definition +to provide a pull up or pull down as required. + # License Any code placed here is released under the MIT License (MIT). diff --git a/encoders/encoder.py b/encoders/encoder.py new file mode 100644 index 0000000..8bca507 --- /dev/null +++ b/encoders/encoder.py @@ -0,0 +1,28 @@ +import pyb + +class Encoder(object): + def __init__(self, pin_x, pin_y, reverse, scale): + self.reverse = reverse + self.scale = scale + self.forward = True + self.pin_x = pin_x + self.pin_y = pin_y + self._pos = 0 + self.x_interrupt = pyb.ExtInt(pin_x, pyb.ExtInt.IRQ_RISING_FALLING, pyb.Pin.PULL_NONE, self.x_callback) + self.y_interrupt = pyb.ExtInt(pin_y, pyb.ExtInt.IRQ_RISING_FALLING, pyb.Pin.PULL_NONE, self.y_callback) + + def x_callback(self, line): + self.forward = self.pin_x.value() ^ self.pin_y.value() ^ self.reverse + self._pos += 1 if self.forward else -1 + + def y_callback(self, line): + self.forward = self.pin_x.value() ^ self.pin_y.value() ^ self.reverse ^ 1 + self._pos += 1 if self.forward else -1 + + @property + def position(self): + return self._pos*self.scale + + def reset(self): + self._pos = 0 + diff --git a/encoders/encoder_timed.py b/encoders/encoder_timed.py new file mode 100644 index 0000000..ce141b0 --- /dev/null +++ b/encoders/encoder_timed.py @@ -0,0 +1,47 @@ +import pyb, utime + +class EncoderTimed(object): + def __init__(self, pin_x, pin_y, reverse, scale): + self.reverse = reverse + self.scale = scale + self.tprev = 0 + self.tlast = 0 + self.forward = True + self.pin_x = pin_x + self.pin_y = pin_y + self._pos = 0 + self.x_interrupt = pyb.ExtInt(pin_x, pyb.ExtInt.IRQ_RISING_FALLING, pyb.Pin.PULL_NONE, self.x_callback) + self.y_interrupt = pyb.ExtInt(pin_y, pyb.ExtInt.IRQ_RISING_FALLING, pyb.Pin.PULL_NONE, self.y_callback) + + def x_callback(self, line): + self.forward = self.pin_x.value() ^ self.pin_y.value() ^ self.reverse + self._pos += 1 if self.forward else -1 + self.tprev = self.tlast + self.tlast = utime.ticks_us() + + def y_callback(self, line): + self.forward = self.pin_x.value() ^ self.pin_y.value() ^ self.reverse ^ 1 + self._pos += 1 if self.forward else -1 + self.tprev = self.tlast + self.tlast = utime.ticks_us() + + @property + def rate(self): # Return rate in edges per second + self.x_interrupt.disable() + self.y_interrupt.disable() + if utime.ticks_diff(self.tlast, utime.ticks_us) > 2000000: # It's stopped + result = 0.0 + else: + result = 1000000.0/(utime.ticks_diff(self.tprev, self.tlast)) + self.x_interrupt.enable() + self.y_interrupt.enable() + result *= self.scale + return result if self.forward else -result + + @property + def position(self): + return self._pos*self.scale + + def reset(self): + self._pos = 0 +