mirror of
https://github.com/KevinMidboe/python-gpiozero.git
synced 2025-10-29 17:50:37 +00:00
Refactor low level implementation
This commit is a fairly major piece of work that abstracts all pin operations (function, state, edge detection, PWM, etc.) into a base "Pin" class which is then used by input/output/composite devices to perform all required configuration. The idea is to pave the way for I2C based IO extenders which can present additional GPIO ports with similar capabilities to the Pi's "native" GPIO ports. As a bonus it also abstracts away the reliance on the RPi.GPIO library to allow alternative pin implementations (e.g. using RPIO to take advantage of DMA based PWM), or even pure Python implementations.
This commit is contained in:
205
gpiozero/pins/rpigpio.py
Normal file
205
gpiozero/pins/rpigpio.py
Normal file
@@ -0,0 +1,205 @@
|
||||
from __future__ import (
|
||||
unicode_literals,
|
||||
absolute_import,
|
||||
print_function,
|
||||
division,
|
||||
)
|
||||
str = type('')
|
||||
|
||||
from RPi import GPIO
|
||||
|
||||
from . import Pin
|
||||
from .exc import (
|
||||
PinInvalidFunction,
|
||||
PinSetInput,
|
||||
PinFixedPull,
|
||||
)
|
||||
|
||||
|
||||
class RPiGPIOPin(Pin):
|
||||
"""
|
||||
Uses the `RPi.GPIO`_ library to interface to the Pi's GPIO pins. This is
|
||||
the default pin implementation if the RPi.GPIO library is installed.
|
||||
Supports all features including PWM (via software).
|
||||
|
||||
.. _RPi.GPIO: https://pypi.python.org/pypi/RPi.GPIO
|
||||
"""
|
||||
|
||||
_PINS = {}
|
||||
|
||||
GPIO_FUNCTIONS = {
|
||||
'input': GPIO.IN,
|
||||
'output': GPIO.OUT,
|
||||
'i2c': GPIO.I2C,
|
||||
'spi': GPIO.SPI,
|
||||
'pwm': GPIO.PWM,
|
||||
'serial': GPIO.SERIAL,
|
||||
'unknown': GPIO.UNKNOWN,
|
||||
}
|
||||
|
||||
GPIO_PULL_UPS = {
|
||||
'up': GPIO.PUD_UP,
|
||||
'down': GPIO.PUD_DOWN,
|
||||
'floating': GPIO.PUD_OFF,
|
||||
}
|
||||
|
||||
GPIO_EDGES = {
|
||||
'both': GPIO.BOTH,
|
||||
'rising': GPIO.RISING,
|
||||
'falling': GPIO.FALLING,
|
||||
}
|
||||
|
||||
GPIO_FUNCTION_NAMES = {v: k for (k, v) in GPIO_FUNCTIONS.items()}
|
||||
GPIO_PULL_UP_NAMES = {v: k for (k, v) in GPIO_PULL_UPS.items()}
|
||||
GPIO_EDGES_NAMES = {v: k for (k, v) in GPIO_EDGES.items()}
|
||||
|
||||
def __new__(cls, number):
|
||||
if not cls._PINS:
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
GPIO.setwarnings(False)
|
||||
try:
|
||||
return cls._PINS[number]
|
||||
except KeyError:
|
||||
self = super(RPiGPIOPin, cls).__new__(cls)
|
||||
cls._PINS[number] = self
|
||||
self._number = number
|
||||
self._pull = 'up' if number in (2, 3) else 'floating'
|
||||
self._pwm = None
|
||||
self._frequency = None
|
||||
self._duty_cycle = None
|
||||
self._bounce = -666
|
||||
self._when_changed = None
|
||||
self._edges = GPIO.BOTH
|
||||
GPIO.setup(self._number, GPIO.IN, self.GPIO_PULL_UPS[self._pull])
|
||||
return self
|
||||
|
||||
def __repr__(self):
|
||||
return "GPIO%d" % self._number
|
||||
|
||||
@property
|
||||
def number(self):
|
||||
return self._number
|
||||
|
||||
def close(self):
|
||||
self.frequency = None
|
||||
self.when_changed = None
|
||||
GPIO.cleanup(self._number)
|
||||
|
||||
def output_with_state(self, state):
|
||||
self._pull = 'floating'
|
||||
GPIO.setup(self._number, GPIO.OUT, initial=state)
|
||||
|
||||
def input_with_pull(self, pull):
|
||||
if pull != 'up' and self._number in (2, 3):
|
||||
raise PinFixedPull('%r has a physical pull-up resistor' % self)
|
||||
try:
|
||||
GPIO.setup(self._number, GPIO.IN, self.GPIO_PULL_UPS[pull])
|
||||
self._pull = pull
|
||||
except KeyError:
|
||||
raise PinInvalidPull('invalid pull "%s" for pin %r' % (pull, self))
|
||||
|
||||
def _get_function(self):
|
||||
return self.GPIO_FUNCTION_NAMES[GPIO.gpio_function(self._number)]
|
||||
|
||||
def _set_function(self, value):
|
||||
if value != 'input':
|
||||
self._pull = 'floating'
|
||||
try:
|
||||
GPIO.setup(self._number, self.GPIO_FUNCTIONS[value], self.GPIO_PULL_UPS[self._pull])
|
||||
except KeyError:
|
||||
raise PinInvalidFunction('invalid function "%s" for pin %r' % (value, self))
|
||||
|
||||
def _get_state(self):
|
||||
if self._pwm:
|
||||
return self._duty_cycle
|
||||
else:
|
||||
return GPIO.input(self._number)
|
||||
|
||||
def _set_state(self, value):
|
||||
if self._pwm:
|
||||
try:
|
||||
self._pwm.ChangeDutyCycle(value * 100)
|
||||
except ValueError:
|
||||
raise PinInvalidValue('invalid state "%s" for pin %r' % (value, self))
|
||||
self._duty_cycle = value
|
||||
else:
|
||||
try:
|
||||
GPIO.output(self._number, value)
|
||||
except ValueError:
|
||||
raise PinInvalidState('invalid state "%s" for pin %r' % (value, self))
|
||||
except RuntimeError:
|
||||
raise PinSetInput('cannot set state of pin %r' % self)
|
||||
|
||||
def _get_pull(self):
|
||||
return self._pull
|
||||
|
||||
def _set_pull(self, value):
|
||||
if self.function != 'input':
|
||||
raise PinFixedPull('cannot set pull on non-input pin %r' % self)
|
||||
if value != 'up' and self._number in (2, 3):
|
||||
raise PinFixedPull('%r has a physical pull-up resistor' % self)
|
||||
try:
|
||||
GPIO.setup(self._number, GPIO.IN, self.GPIO_PULL_UPS[value])
|
||||
self._pull = value
|
||||
except KeyError:
|
||||
raise PinInvalidPull('invalid pull "%s" for pin %r' % (value, self))
|
||||
|
||||
def _get_frequency(self):
|
||||
return self._frequency
|
||||
|
||||
def _set_frequency(self, value):
|
||||
if self._frequency is None and value is not None:
|
||||
try:
|
||||
self._pwm = GPIO.PWM(self._number, value)
|
||||
except RuntimeError:
|
||||
raise PinPWMFixedValue('cannot start PWM on pin %r' % self)
|
||||
self._pwm.start(0)
|
||||
self._duty_cycle = 0
|
||||
self._frequency = value
|
||||
elif self._frequency is not None and value is not None:
|
||||
self._pwm.ChangeFrequency(value)
|
||||
self._frequency = value
|
||||
elif self._frequency is not None and value is None:
|
||||
self._pwm.stop()
|
||||
self._pwm = None
|
||||
self._duty_cycle = None
|
||||
self._frequency = None
|
||||
|
||||
def _get_bounce(self):
|
||||
return None if self._bounce == -666 else (self._bounce / 1000)
|
||||
|
||||
def _set_bounce(self, value):
|
||||
f = self.when_changed
|
||||
self.when_changed = None
|
||||
try:
|
||||
self._bounce = -666 if value is None else (value * 1000)
|
||||
finally:
|
||||
self.when_changed = f
|
||||
|
||||
def _get_edges(self):
|
||||
return self.GPIO_EDGES_NAMES[self._edges]
|
||||
|
||||
def _set_edges(self, value):
|
||||
f = self.when_changed
|
||||
self.when_changed = None
|
||||
try:
|
||||
self._edges = self.GPIO_EDGES[value]
|
||||
finally:
|
||||
self.when_changed = f
|
||||
|
||||
def _get_when_changed(self):
|
||||
return self._when_changed
|
||||
|
||||
def _set_when_changed(self, value):
|
||||
if self._when_changed is None and value is not None:
|
||||
self._when_changed = value
|
||||
GPIO.add_event_detect(
|
||||
self._number, self._edges,
|
||||
callback=lambda channel: self._when_changed(),
|
||||
bouncetime=self._bounce)
|
||||
elif self._when_changed is not None and value is None:
|
||||
GPIO.remove_event_detect(self._number)
|
||||
self._when_changed = None
|
||||
else:
|
||||
self._when_changed = value
|
||||
|
||||
Reference in New Issue
Block a user