diff --git a/Adafruit_GPIO/FT232H.py b/Adafruit_GPIO/FT232H.py index a0b0f6d..3453db0 100644 --- a/Adafruit_GPIO/FT232H.py +++ b/Adafruit_GPIO/FT232H.py @@ -380,10 +380,15 @@ def output_pins(self, pins, write=True): def input(self, pin): """Read the specified pin and return HIGH/true if the pin is pulled high, or LOW/false if pulled low.""" - if pin < 0 or pin > 15: + return self.input_pins([pin])[0] + + 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.""" + if [pin for pin in pins if pin < 0 or pin > 15]: raise ValueError('Pin must be between 0 and 15 (inclusive).') - pins = self.mpsse_read_gpio() - return ((pins >> pin) & 0x0001) == 1 + _pins = self.mpsse_read_gpio() + return [((_pins >> pin) & 0x0001) == 1 for pin in pins] class SPI(object): diff --git a/Adafruit_GPIO/GPIO.py b/Adafruit_GPIO/GPIO.py index 6555f6a..08e99c6 100644 --- a/Adafruit_GPIO/GPIO.py +++ b/Adafruit_GPIO/GPIO.py @@ -71,6 +71,13 @@ 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 @@ -87,10 +94,18 @@ 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 improved by subclasses. + # 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. @@ -127,6 +142,19 @@ def cleanup(self, pin=None): """ 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.""" @@ -171,6 +199,13 @@ def input(self, pin): """ 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 @@ -254,6 +289,13 @@ def input(self, pin): """ 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 diff --git a/Adafruit_GPIO/MCP230xx.py b/Adafruit_GPIO/MCP230xx.py old mode 100755 new mode 100644 index f8fd3d7..8496ccd --- a/Adafruit_GPIO/MCP230xx.py +++ b/Adafruit_GPIO/MCP230xx.py @@ -50,10 +50,6 @@ def __init__(self, address, i2c=None, **kwargs): self.write_iodir() self.write_gppu() - 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 setup(self, pin, value): """Set the input or output mode for a specified pin. Mode should be @@ -69,24 +65,19 @@ def setup(self, pin, value): raise ValueError('Unexpected value. Must be GPIO.IN or GPIO.OUT.') self.write_iodir() + def output(self, pin, value): """Set the specified pin the provided high/low value. Value should be - either GPIO.HIGH/GPIO.LOW or a boolean (True = high). + either GPIO.HIGH/GPIO.LOW or a boolean (True = HIGH). """ - self._validate_pin(pin) - # Set bit on or off. - if value: - self.gpio[int(pin/8)] |= 1 << (int(pin%8)) - else: - self.gpio[int(pin/8)] &= ~(1 << (int(pin%8))) - # Write GPIO state. - self.write_gpio() + self.output_pins({pin: value}) 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. """ + [self._validate_pin(pin) for pin in pins.keys()] # Set each changed pin's bit. for pin, value in iter(pins.items()): if value: @@ -96,15 +87,23 @@ def output_pins(self, pins): # Write GPIO state. self.write_gpio() + def input(self, pin): """Read the specified pin and return GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low. """ - self._validate_pin(pin) + return self.input_pins([pin])[0] + + 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. + """ + [self._validate_pin(pin) for pin in pins] # Get GPIO state. gpio = self._device.readList(self.GPIO, self.gpio_bytes) # Return True if pin's bit is set. - return (gpio[int(pin/8)] & 1 << (int(pin%8))) > 0 + return [(gpio[int(pin/8)] & 1 << (int(pin%8))) > 0 for pin in pins] + def pullup(self, pin, enabled): """Turn on the pull-up resistor for the specified pin if enabled is True, diff --git a/Adafruit_GPIO/PCF8574.py b/Adafruit_GPIO/PCF8574.py new file mode 100644 index 0000000..fe58e52 --- /dev/null +++ b/Adafruit_GPIO/PCF8574.py @@ -0,0 +1,94 @@ +''' +Adafruit compatible using BaseGPIO class to represent a PCF8574/A IO expander +Copyright (C) 2015 Sylvan Butler + +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 as GPIO +import Adafruit_GPIO.I2C as I2C + + + +IN = GPIO.IN +OUT = GPIO.OUT +HIGH = GPIO.HIGH +LOW = GPIO.LOW + + +class PCF8574(GPIO.BaseGPIO): + """Class to represent a PCF8574 or PCF8574A GPIO extender. Compatible + with the Adafruit_GPIO BaseGPIO class so it can be used as a custom GPIO + class for interacting with device. + """ + + NUM_GPIO = 8 + + def __init__(self, address=0x27, busnum=None, i2c=None, **kwargs): + address = int(address) + self.__name__ = \ + "PCF8574" if address in range(0x20, 0x28) else \ + "PCF8574A" if address in range(0x38, 0x40) else \ + "Bad address for PCF8574(A): 0x%02X not in range [0x20..0x27, 0x38..0x3F]" % address + if self.__name__[0] != 'P': + raise ValueError(self.__name__) + # Create I2C device. + i2c = i2c or I2C + busnum = busnum or i2c.get_default_bus() + self._device = i2c.get_i2c_device(address, busnum, **kwargs) + # Buffer register values so they can be changed without reading. + self.iodir = 0xFF # Default direction to all inputs is in + self.gpio = 0x00 + self._write_pins() + + + def _write_pins(self): + self._device.writeRaw8(self.gpio | self.iodir) + + def _read_pins(self): + return self._device.readRaw8() & self.iodir + + + def setup(self, pin, mode): + self.setup_pins({pin: mode}) + + def setup_pins(self, pins): + if False in [y for x,y in [(self._validate_pin(pin),mode in (IN,OUT)) for pin,mode in pins.iteritems()]]: + raise ValueError('Invalid MODE, IN or OUT') + for pin,mode in pins.iteritems(): + self.iodir = self._bit2(self.iodir, pin, mode) + self._write_pins() + + + def output(self, pin, value): + self.output_pins({pin: value}) + + def output_pins(self, pins): + [self._validate_pin(pin) for pin in pins.keys()] + for pin,value in pins.iteritems(): + self.gpio = self._bit2(self.gpio, pin, bool(value)) + self._write_pins() + + + def input(self, pin): + return self.input_pins([pin])[0] + + def input_pins(self, pins): + [self._validate_pin(pin) for pin in pins] + inp = self._read_pins() + return [bool(inp & (1<