diff options
Diffstat (limited to 'Adafruit_Python_GPIO/Adafruit_GPIO/GPIO.py')
-rw-r--r-- | Adafruit_Python_GPIO/Adafruit_GPIO/GPIO.py | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/Adafruit_Python_GPIO/Adafruit_GPIO/GPIO.py b/Adafruit_Python_GPIO/Adafruit_GPIO/GPIO.py new file mode 100644 index 0000000..08e99c6 --- /dev/null +++ b/Adafruit_Python_GPIO/Adafruit_GPIO/GPIO.py @@ -0,0 +1,426 @@ +# Copyright (c) 2014 Adafruit Industries +# Author: Tony DiCola +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import Adafruit_GPIO.Platform as Platform + + +OUT = 0 +IN = 1 +HIGH = True +LOW = False + +RISING = 1 +FALLING = 2 +BOTH = 3 + +PUD_OFF = 0 +PUD_DOWN = 1 +PUD_UP = 2 + +class BaseGPIO(object): + """Base class for implementing simple digital IO for a platform. + Implementors are expected to subclass from this and provide an implementation + of the setup, output, and input functions.""" + + def setup(self, pin, mode, pull_up_down=PUD_OFF): + """Set the input or output mode for a specified pin. Mode should be + either OUT or IN.""" + raise NotImplementedError + + def output(self, pin, value): + """Set the specified pin the provided high/low value. Value should be + either HIGH/LOW or a boolean (true = high).""" + raise NotImplementedError + + def input(self, pin): + """Read the specified pin and return HIGH/true if the pin is pulled high, + or LOW/false if pulled low.""" + raise NotImplementedError + + def set_high(self, pin): + """Set the specified pin HIGH.""" + self.output(pin, HIGH) + + def set_low(self, pin): + """Set the specified pin LOW.""" + self.output(pin, LOW) + + def is_high(self, pin): + """Return true if the specified pin is pulled high.""" + return self.input(pin) == HIGH + + def is_low(self, pin): + """Return true if the specified pin is pulled low.""" + return self.input(pin) == LOW + + +# Basic implementation of multiple pin methods just loops through pins and +# processes each one individually. This is not optimal, but derived classes can +# provide a more optimal implementation that deals with groups of pins +# simultaneously. +# See MCP230xx or PCF8574 classes for examples of optimized implementations. + + def output_pins(self, pins): + """Set multiple pins high or low at once. Pins should be a dict of pin + name to pin value (HIGH/True for 1, LOW/False for 0). All provided pins + will be set to the given values. + """ + # General implementation just loops through pins and writes them out + # manually. This is not optimized, but subclasses can choose to implement + # a more optimal batch output implementation. See the MCP230xx class for + # example of optimized implementation. + for pin, value in iter(pins.items()): + self.output(pin, value) + + def setup_pins(self, pins): + """Setup multiple pins as inputs or outputs at once. Pins should be a + dict of pin name to pin type (IN or OUT). + """ + # General implementation that can be optimized by derived classes. + for pin, value in iter(pins.items()): + self.setup(pin, value) + + def input_pins(self, pins): + """Read multiple pins specified in the given list and return list of pin values + GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low. + """ + # General implementation that can be optimized by derived classes. + return [self.input(pin) for pin in pins] + + + def add_event_detect(self, pin, edge): + """Enable edge detection events for a particular GPIO channel. Pin + should be type IN. Edge must be RISING, FALLING or BOTH. + """ + raise NotImplementedError + + def remove_event_detect(self, pin): + """Remove edge detection for a particular GPIO channel. Pin should be + type IN. + """ + raise NotImplementedError + + def add_event_callback(self, pin, callback): + """Add a callback for an event already defined using add_event_detect(). + Pin should be type IN. + """ + raise NotImplementedError + + def event_detected(self, pin): + """Returns True if an edge has occured on a given GPIO. You need to + enable edge detection using add_event_detect() first. Pin should be + type IN. + """ + raise NotImplementedError + + def wait_for_edge(self, pin, edge): + """Wait for an edge. Pin should be type IN. Edge must be RISING, + FALLING or BOTH.""" + raise NotImplementedError + + def cleanup(self, pin=None): + """Clean up GPIO event detection for specific pin, or all pins if none + is specified. + """ + raise NotImplementedError + + +# helper functions useful to derived classes + + def _validate_pin(self, pin): + # Raise an exception if pin is outside the range of allowed values. + if pin < 0 or pin >= self.NUM_GPIO: + raise ValueError('Invalid GPIO value, must be between 0 and {0}.'.format(self.NUM_GPIO)) + + def _bit2(self, src, bit, val): + bit = 1 << bit + return (src | bit) if val else (src & ~bit) + + +class RPiGPIOAdapter(BaseGPIO): + """GPIO implementation for the Raspberry Pi using the RPi.GPIO library.""" + + def __init__(self, rpi_gpio, mode=None): + self.rpi_gpio = rpi_gpio + # Suppress warnings about GPIO in use. + rpi_gpio.setwarnings(False) + # Setup board pin mode. + if mode == rpi_gpio.BOARD or mode == rpi_gpio.BCM: + rpi_gpio.setmode(mode) + elif mode is not None: + raise ValueError('Unexpected value for mode. Must be BOARD or BCM.') + else: + # Default to BCM numbering if not told otherwise. + rpi_gpio.setmode(rpi_gpio.BCM) + # Define mapping of Adafruit GPIO library constants to RPi.GPIO constants. + self._dir_mapping = { OUT: rpi_gpio.OUT, + IN: rpi_gpio.IN } + self._pud_mapping = { PUD_OFF: rpi_gpio.PUD_OFF, + PUD_DOWN: rpi_gpio.PUD_DOWN, + PUD_UP: rpi_gpio.PUD_UP } + self._edge_mapping = { RISING: rpi_gpio.RISING, + FALLING: rpi_gpio.FALLING, + BOTH: rpi_gpio.BOTH } + + def setup(self, pin, mode, pull_up_down=PUD_OFF): + """Set the input or output mode for a specified pin. Mode should be + either OUTPUT or INPUT. + """ + self.rpi_gpio.setup(pin, self._dir_mapping[mode], + pull_up_down=self._pud_mapping[pull_up_down]) + + def output(self, pin, value): + """Set the specified pin the provided high/low value. Value should be + either HIGH/LOW or a boolean (true = high). + """ + self.rpi_gpio.output(pin, value) + + def input(self, pin): + """Read the specified pin and return HIGH/true if the pin is pulled high, + or LOW/false if pulled low. + """ + return self.rpi_gpio.input(pin) + + def input_pins(self, pins): + """Read multiple pins specified in the given list and return list of pin values + GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low. + """ + # maybe rpi has a mass read... it would be more efficient to use it if it exists + return [self.rpi_gpio.input(pin) for pin in pins] + + def add_event_detect(self, pin, edge, callback=None, bouncetime=-1): + """Enable edge detection events for a particular GPIO channel. Pin + should be type IN. Edge must be RISING, FALLING or BOTH. Callback is a + function for the event. Bouncetime is switch bounce timeout in ms for + callback + """ + kwargs = {} + if callback: + kwargs['callback']=callback + if bouncetime > 0: + kwargs['bouncetime']=bouncetime + self.rpi_gpio.add_event_detect(pin, self._edge_mapping[edge], **kwargs) + + def remove_event_detect(self, pin): + """Remove edge detection for a particular GPIO channel. Pin should be + type IN. + """ + self.rpi_gpio.remove_event_detect(pin) + + def add_event_callback(self, pin, callback): + """Add a callback for an event already defined using add_event_detect(). + Pin should be type IN. + """ + self.rpi_gpio.add_event_callback(pin, callback) + + def event_detected(self, pin): + """Returns True if an edge has occured on a given GPIO. You need to + enable edge detection using add_event_detect() first. Pin should be + type IN. + """ + return self.rpi_gpio.event_detected(pin) + + def wait_for_edge(self, pin, edge): + """Wait for an edge. Pin should be type IN. Edge must be RISING, + FALLING or BOTH. + """ + self.rpi_gpio.wait_for_edge(pin, self._edge_mapping[edge]) + + def cleanup(self, pin=None): + """Clean up GPIO event detection for specific pin, or all pins if none + is specified. + """ + if pin is None: + self.rpi_gpio.cleanup() + else: + self.rpi_gpio.cleanup(pin) + +class AdafruitBBIOAdapter(BaseGPIO): + """GPIO implementation for the Beaglebone Black using the Adafruit_BBIO + library. + """ + + def __init__(self, bbio_gpio): + self.bbio_gpio = bbio_gpio + # Define mapping of Adafruit GPIO library constants to RPi.GPIO constants. + self._dir_mapping = { OUT: bbio_gpio.OUT, + IN: bbio_gpio.IN } + self._pud_mapping = { PUD_OFF: bbio_gpio.PUD_OFF, + PUD_DOWN: bbio_gpio.PUD_DOWN, + PUD_UP: bbio_gpio.PUD_UP } + self._edge_mapping = { RISING: bbio_gpio.RISING, + FALLING: bbio_gpio.FALLING, + BOTH: bbio_gpio.BOTH } + + def setup(self, pin, mode, pull_up_down=PUD_OFF): + """Set the input or output mode for a specified pin. Mode should be + either OUTPUT or INPUT. + """ + self.bbio_gpio.setup(pin, self._dir_mapping[mode], + pull_up_down=self._pud_mapping[pull_up_down]) + + def output(self, pin, value): + """Set the specified pin the provided high/low value. Value should be + either HIGH/LOW or a boolean (true = high). + """ + self.bbio_gpio.output(pin, value) + + def input(self, pin): + """Read the specified pin and return HIGH/true if the pin is pulled high, + or LOW/false if pulled low. + """ + return self.bbio_gpio.input(pin) + + def input_pins(self, pins): + """Read multiple pins specified in the given list and return list of pin values + GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low. + """ + # maybe bbb has a mass read... it would be more efficient to use it if it exists + return [self.bbio_gpio.input(pin) for pin in pins] + + def add_event_detect(self, pin, edge, callback=None, bouncetime=-1): + """Enable edge detection events for a particular GPIO channel. Pin + should be type IN. Edge must be RISING, FALLING or BOTH. Callback is a + function for the event. Bouncetime is switch bounce timeout in ms for + callback + """ + kwargs = {} + if callback: + kwargs['callback']=callback + if bouncetime > 0: + kwargs['bouncetime']=bouncetime + self.bbio_gpio.add_event_detect(pin, self._edge_mapping[edge], **kwargs) + + def remove_event_detect(self, pin): + """Remove edge detection for a particular GPIO channel. Pin should be + type IN. + """ + self.bbio_gpio.remove_event_detect(pin) + + def add_event_callback(self, pin, callback, bouncetime=-1): + """Add a callback for an event already defined using add_event_detect(). + Pin should be type IN. Bouncetime is switch bounce timeout in ms for + callback + """ + kwargs = {} + if bouncetime > 0: + kwargs['bouncetime']=bouncetime + self.bbio_gpio.add_event_callback(pin, callback, **kwargs) + + def event_detected(self, pin): + """Returns True if an edge has occured on a given GPIO. You need to + enable edge detection using add_event_detect() first. Pin should be + type IN. + """ + return self.bbio_gpio.event_detected(pin) + + def wait_for_edge(self, pin, edge): + """Wait for an edge. Pin should be type IN. Edge must be RISING, + FALLING or BOTH. + """ + self.bbio_gpio.wait_for_edge(pin, self._edge_mapping[edge]) + + def cleanup(self, pin=None): + """Clean up GPIO event detection for specific pin, or all pins if none + is specified. + """ + if pin is None: + self.bbio_gpio.cleanup() + else: + self.bbio_gpio.cleanup(pin) + +class AdafruitMinnowAdapter(BaseGPIO): + """GPIO implementation for the Minnowboard + MAX using the mraa library""" + + def __init__(self,mraa_gpio): + self.mraa_gpio = mraa_gpio + # Define mapping of Adafruit GPIO library constants to mraa constants + self._dir_mapping = { OUT: self.mraa_gpio.DIR_OUT, + IN: self.mraa_gpio.DIR_IN } + self._pud_mapping = { PUD_OFF: self.mraa_gpio.MODE_STRONG, + PUD_UP: self.mraa_gpio.MODE_HIZ, + PUD_DOWN: self.mraa_gpio.MODE_PULLDOWN } + self._edge_mapping = { RISING: self.mraa_gpio.EDGE_RISING, + FALLING: self.mraa_gpio.EDGE_FALLING, + BOTH: self.mraa_gpio.EDGE_BOTH } + + def setup(self,pin,mode): + """Set the input or output mode for a specified pin. Mode should be + either DIR_IN or DIR_OUT. + """ + self.mraa_gpio.Gpio.dir(self.mraa_gpio.Gpio(pin),self._dir_mapping[mode]) + + def output(self,pin,value): + """Set the specified pin the provided high/low value. Value should be + either 1 (ON or HIGH), or 0 (OFF or LOW) or a boolean. + """ + self.mraa_gpio.Gpio.write(self.mraa_gpio.Gpio(pin), value) + + def input(self,pin): + """Read the specified pin and return HIGH/true if the pin is pulled high, + or LOW/false if pulled low. + """ + return self.mraa_gpio.Gpio.read(self.mraa_gpio.Gpio(pin)) + + def add_event_detect(self, pin, edge, callback=None, bouncetime=-1): + """Enable edge detection events for a particular GPIO channel. Pin + should be type IN. Edge must be RISING, FALLING or BOTH. Callback is a + function for the event. Bouncetime is switch bounce timeout in ms for + callback + """ + kwargs = {} + if callback: + kwargs['callback']=callback + if bouncetime > 0: + kwargs['bouncetime']=bouncetime + self.mraa_gpio.Gpio.isr(self.mraa_gpio.Gpio(pin), self._edge_mapping[edge], **kwargs) + + def remove_event_detect(self, pin): + """Remove edge detection for a particular GPIO channel. Pin should be + type IN. + """ + self.mraa_gpio.Gpio.isrExit(self.mraa_gpio.Gpio(pin)) + + def wait_for_edge(self, pin, edge): + """Wait for an edge. Pin should be type IN. Edge must be RISING, + FALLING or BOTH. + """ + self.bbio_gpio.wait_for_edge(self.mraa_gpio.Gpio(pin), self._edge_mapping[edge]) + +def get_platform_gpio(**keywords): + """Attempt to return a GPIO instance for the platform which the code is being + executed on. Currently supports only the Raspberry Pi using the RPi.GPIO + library and Beaglebone Black using the Adafruit_BBIO library. Will throw an + exception if a GPIO instance can't be created for the current platform. The + returned GPIO object is an instance of BaseGPIO. + """ + plat = Platform.platform_detect() + if plat == Platform.RASPBERRY_PI: + import RPi.GPIO + return RPiGPIOAdapter(RPi.GPIO, **keywords) + elif plat == Platform.BEAGLEBONE_BLACK: + import Adafruit_BBIO.GPIO + return AdafruitBBIOAdapter(Adafruit_BBIO.GPIO, **keywords) + elif plat == Platform.MINNOWBOARD: + import mraa + return AdafruitMinnowAdapter(mraa, **keywords) + elif plat == Platform.UNKNOWN: + raise RuntimeError('Could not determine platform.') |