Fix #459 - properly support remote SPI with pigpio

Sorry! Dave's messing around with the pin implementations again.
Hopefully the last time. The pin_factory is now really a factory object
which can be asked to produce individual pins or pin-based interfaces
like SPI (which can be supported properly via pigpio).
This commit is contained in:
Dave Jones
2016-09-27 00:30:57 +01:00
parent 0ca2586e9e
commit ce6217c14f
34 changed files with 2311 additions and 1456 deletions

View File

@@ -15,56 +15,28 @@ try:
except ImportError:
from ..compat import isclose
from . import Pin
from .data import pi_info
from ..exc import PinSetInput, PinPWMUnsupported, PinFixedPull
from ..exc import PinPWMUnsupported, PinSetInput, PinFixedPull
from ..devices import Device
from .pi import PiPin
from .local import LocalPiFactory
PinState = namedtuple('PinState', ('timestamp', 'state'))
class MockPin(Pin):
class MockPin(PiPin):
"""
A mock pin used primarily for testing. This class does *not* support PWM.
"""
_PINS = {}
@classmethod
def clear_pins(cls):
cls._PINS.clear()
@classmethod
def pi_info(cls):
return pi_info('a21041') # Pretend we're a Pi 2B
def __new__(cls, number):
if not (0 <= number < 54):
raise ValueError('invalid pin %d specified (must be 0..53)' % number)
try:
old_pin = cls._PINS[number]
except KeyError:
self = super(MockPin, cls).__new__(cls)
cls._PINS[number] = self
self._number = number
self._function = 'input'
self._state = False
self._pull = 'floating'
self._bounce = None
self._edges = 'both'
self._when_changed = None
self.clear_states()
return self
# Ensure the pin class expected supports PWM (or not)
if issubclass(cls, MockPWMPin) != isinstance(old_pin, MockPWMPin):
raise ValueError('pin %d is already in use as a %s' % (number, old_pin.__class__.__name__))
return old_pin
def __repr__(self):
return 'MOCK%d' % self._number
@property
def number(self):
return self._number
def __init__(self, factory, number):
super(MockPin, self).__init__(factory, number)
self._function = 'input'
self._state = False
self._pull = 'floating'
self._bounce = None
self._edges = 'both'
self._when_changed = None
self.clear_states()
def close(self):
self.when_changed = None
@@ -186,8 +158,8 @@ class MockChargingPin(MockPin):
(as if attached to, e.g. a typical circuit using an LDR and a capacitor
to time the charging rate).
"""
def __init__(self, number):
super(MockChargingPin, self).__init__()
def __init__(self, factory, number):
super(MockChargingPin, self).__init__(factory, number)
self.charge_time = 0.01 # dark charging time
self._charge_stop = Event()
self._charge_thread = None
@@ -225,8 +197,8 @@ class MockTriggerPin(MockPin):
corresponding pin instance. When this pin is driven high it will trigger
the echo pin to drive high for the echo time.
"""
def __init__(self, number):
super(MockTriggerPin, self).__init__()
def __init__(self, factory, number):
super(MockTriggerPin, self).__init__(factory, number)
self.echo_pin = None
self.echo_time = 0.04 # longest echo time
self._echo_thread = None
@@ -250,8 +222,8 @@ class MockPWMPin(MockPin):
"""
This derivative of :class:`MockPin` adds PWM support.
"""
def __init__(self, number):
super(MockPWMPin, self).__init__()
def __init__(self, factory, number):
super(MockPWMPin, self).__init__(factory, number)
self._frequency = None
def close(self):
@@ -283,8 +255,8 @@ class MockSPIClockPin(MockPin):
rather, construct a :class:`MockSPIDevice` with various pin numbers, and
this class will be used for the clock pin.
"""
def __init__(self, number):
super(MockSPIClockPin, self).__init__()
def __init__(self, factory, number):
super(MockSPIClockPin, self).__init__(factory, number)
if not hasattr(self, 'spi_devices'):
self.spi_devices = []
@@ -301,8 +273,8 @@ class MockSPISelectPin(MockPin):
tests; rather, construct a :class:`MockSPIDevice` with various pin numbers,
and this class will be used for the select pin.
"""
def __init__(self, number):
super(MockSPISelectPin, self).__init__()
def __init__(self, factory, number):
super(MockSPISelectPin, self).__init__(factory, number)
if not hasattr(self, 'spi_device'):
self.spi_device = None
@@ -314,13 +286,13 @@ class MockSPISelectPin(MockPin):
class MockSPIDevice(object):
def __init__(
self, clock_pin, mosi_pin, miso_pin, select_pin=None,
self, clock_pin, mosi_pin=None, miso_pin=None, select_pin=None,
clock_polarity=False, clock_phase=False, lsb_first=False,
bits_per_word=8, select_high=False):
self.clock_pin = MockSPIClockPin(clock_pin)
self.mosi_pin = None if mosi_pin is None else MockPin(mosi_pin)
self.miso_pin = None if miso_pin is None else MockPin(miso_pin)
self.select_pin = None if select_pin is None else MockSPISelectPin(select_pin)
self.clock_pin = Device._pin_factory.pin(clock_pin, pin_class=MockSPIClockPin)
self.mosi_pin = None if mosi_pin is None else Device._pin_factory.pin(mosi_pin)
self.miso_pin = None if miso_pin is None else Device._pin_factory.pin(miso_pin)
self.select_pin = None if select_pin is None else Device._pin_factory.pin(select_pin, pin_class=MockSPISelectPin)
self.clock_polarity = clock_polarity
self.clock_phase = clock_phase
self.lsb_first = lsb_first
@@ -413,3 +385,34 @@ class MockSPIDevice(object):
bits = reversed(bits)
self.tx_buf.extend(bits)
class MockFactory(LocalPiFactory):
def __init__(self, revision='a21041', pin_class=MockPin):
super(MockFactory, self).__init__()
self._revision = revision
self.pin_class = pin_class
def _get_address(self):
return ('mock',)
def _get_revision(self):
return self._revision
def reset(self):
self.pins.clear()
def pin(self, spec, pin_class=None):
if pin_class is None:
pin_class = self.pin_class
n = self._to_gpio(spec)
try:
pin = self.pins[n]
except KeyError:
pin = pin_class(self, n)
self.pins[n] = pin
else:
# Ensure the pin class expected supports PWM (or not)
if issubclass(pin_class, MockPWMPin) != isinstance(pin, MockPWMPin):
raise ValueError('pin %d is already in use as a %s' % (n, pin.__class__.__name__))
return pin