mirror of
				https://github.com/KevinMidboe/python-gpiozero.git
				synced 2025-10-29 17:50:37 +00:00 
			
		
		
		
	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.
		
			
				
	
	
		
			205 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			205 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from __future__ import (
 | |
|     unicode_literals,
 | |
|     absolute_import,
 | |
|     print_function,
 | |
|     division,
 | |
|     )
 | |
| str = type('')
 | |
| 
 | |
| 
 | |
| from threading import Lock
 | |
| 
 | |
| import RPIO
 | |
| import RPIO.PWM
 | |
| 
 | |
| from . import Pin, PINS_CLEANUP
 | |
| from .exc import (
 | |
|     PinInvalidFunction,
 | |
|     PinSetInput,
 | |
|     PinFixedPull,
 | |
|     )
 | |
| 
 | |
| 
 | |
| class RPIOPin(Pin):
 | |
|     """
 | |
|     Uses the `RPIO`_ library to interface to the Pi's GPIO pins. This is
 | |
|     the default pin implementation if the RPi.GPIO library is not installed,
 | |
|     but RPIO is. Supports all features including PWM (hardware via DMA).
 | |
| 
 | |
|     .. note::
 | |
| 
 | |
|         Please note that at the time of writing, RPIO is only compatible with
 | |
|         Pi 1's; the Raspberry Pi 2 Model B is *not* supported. Also note that
 | |
|         root access is required so scripts must typically be run with ``sudo``.
 | |
| 
 | |
|     .. _RPIO: https://pythonhosted.org/RPIO/
 | |
|     """
 | |
| 
 | |
|     _PINS = {}
 | |
| 
 | |
|     GPIO_FUNCTIONS = {
 | |
|         'input':   RPIO.IN,
 | |
|         'output':  RPIO.OUT,
 | |
|         'alt0':    RPIO.ALT0,
 | |
|         }
 | |
| 
 | |
|     GPIO_PULL_UPS = {
 | |
|         'up':       RPIO.PUD_UP,
 | |
|         'down':     RPIO.PUD_DOWN,
 | |
|         'floating': RPIO.PUD_OFF,
 | |
|         }
 | |
| 
 | |
|     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()}
 | |
| 
 | |
|     def __new__(cls, number):
 | |
|         if not cls._PINS:
 | |
|             RPIO.setmode(RPIO.BCM)
 | |
|             RPIO.setwarnings(False)
 | |
|             RPIO.wait_for_interrupts(threaded=True)
 | |
|             RPIO.PWM.setup()
 | |
|             RPIO.PWM.init_channel(0, 10000)
 | |
|             PINS_CLEANUP.append(RPIO.PWM.cleanup)
 | |
|             PINS_CLEANUP.append(RPIO.stop_waiting_for_interrupts)
 | |
|             PINS_CLEANUP.append(RPIO.cleanup)
 | |
|         try:
 | |
|             return cls._PINS[number]
 | |
|         except KeyError:
 | |
|             self = super(RPIOPin, cls).__new__(cls)
 | |
|             cls._PINS[number] = self
 | |
|             self._number = number
 | |
|             self._pull = 'up' if number in (2, 3) else 'floating'
 | |
|             self._pwm = False
 | |
|             self._duty_cycle = None
 | |
|             self._bounce = None
 | |
|             self._when_changed = None
 | |
|             self._edges = 'both'
 | |
|             RPIO.setup(self._number, RPIO.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
 | |
|         RPIO.setup(self._number, RPIO.IN, RPIO.PUD_OFF)
 | |
| 
 | |
|     def _get_function(self):
 | |
|         return self.GPIO_FUNCTION_NAMES[RPIO.gpio_function(self._number)]
 | |
| 
 | |
|     def _set_function(self, value):
 | |
|         if value != 'input':
 | |
|             self._pull = 'floating'
 | |
|         try:
 | |
|             RPIO.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 RPIO.input(self._number)
 | |
| 
 | |
|     def _set_state(self, value):
 | |
|         if not 0 <= value <= 1:
 | |
|             raise PinInvalidValue('invalid state "%s" for pin %r' % (value, self))
 | |
|         if self._pwm:
 | |
|             RPIO.PWM.clear_channel_gpio(0, self._number)
 | |
|             if value == 0:
 | |
|                 RPIO.output(self._number, False)
 | |
|             elif value == 1:
 | |
|                 RPIO.output(self._number, True)
 | |
|             else:
 | |
|                 RPIO.PWM.add_channel_pulse(0, self._number, start=0, width=int(1000 * value))
 | |
|             self._duty_cycle = value
 | |
|         else:
 | |
|             try:
 | |
|                 RPIO.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:
 | |
|             RPIO.setup(self._number, RPIO.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 100
 | |
| 
 | |
|     def _set_frequency(self, value):
 | |
|         if value is not None and value != 100:
 | |
|             raise PinPWMError(
 | |
|                 'RPIOPin implementation is currently limited to '
 | |
|                 '100Hz sub-cycles')
 | |
|         if not self._pwm and value is not None:
 | |
|             self._pwm = True
 | |
|             # Dirty hack to get RPIO's PWM support to setup, but do nothing,
 | |
|             # for a given GPIO pin
 | |
|             RPIO.PWM.add_channel_pulse(0, self._number, start=0, width=0)
 | |
|             RPIO.PWM.clear_channel_gpio(0, self._number)
 | |
|         elif self._pwm and value is None:
 | |
|             RPIO.PWM.clear_channel_gpio(0, self._number)
 | |
|             self._pwm = False
 | |
| 
 | |
|     def _get_bounce(self):
 | |
|         return None if self._bounce is None else (self._bounce / 1000)
 | |
| 
 | |
|     def _set_bounce(self, value):
 | |
|         f = self.when_changed
 | |
|         self.when_changed = None
 | |
|         try:
 | |
|             self._bounce = None if value is None else (value * 1000)
 | |
|         finally:
 | |
|             self.when_changed = f
 | |
| 
 | |
|     def _get_edges(self):
 | |
|         return self._edges
 | |
| 
 | |
|     def _set_edges(self, value):
 | |
|         f = self.when_changed
 | |
|         self.when_changed = None
 | |
|         try:
 | |
|             self._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
 | |
|             RPIO.add_interrupt_callback(
 | |
|                 self._number,
 | |
|                 lambda channel, value: self._when_changed(),
 | |
|                 self._edges, self.GPIO_PULL_UPS[self._pull], self._bounce)
 | |
|         elif self._when_changed is not None and value is None:
 | |
|             try:
 | |
|                 RPIO.del_interrupt_callback(self._number)
 | |
|             except KeyError:
 | |
|                 # Ignore this exception which occurs during shutdown; this
 | |
|                 # simply means RPIO's built-in cleanup has already run and
 | |
|                 # removed the handler
 | |
|                 pass
 | |
|             self._when_changed = None
 | |
|         else:
 | |
|             self._when_changed = value
 | |
| 
 |