|
| 1 | +''' |
| 2 | +Adafruit compatible using BaseGPIO class to represent a PCF8574/A IO expander |
| 3 | +Copyright (C) 2015 Sylvan Butler |
| 4 | +
|
| 5 | +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and |
| 6 | +associated documentation files (the "Software"), to deal in the Software without restriction, |
| 7 | +including without limitation the rights to use, copy, modify, merge, publish, distribute, |
| 8 | +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is |
| 9 | +furnished to do so, subject to the following conditions: |
| 10 | +
|
| 11 | +The above copyright notice and this permission notice shall be included in all copies or substantial |
| 12 | +portions of the Software. |
| 13 | +
|
| 14 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT |
| 15 | +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| 16 | +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| 17 | +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| 18 | +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 19 | +''' |
| 20 | + |
| 21 | +import Adafruit_GPIO as GPIO |
| 22 | +import Adafruit_GPIO.I2C as I2C |
| 23 | + |
| 24 | + |
| 25 | + |
| 26 | +IN = GPIO.IN |
| 27 | +OUT = GPIO.OUT |
| 28 | +HIGH = GPIO.HIGH |
| 29 | +LOW = GPIO.LOW |
| 30 | + |
| 31 | + |
| 32 | +class PCF8574(GPIO.BaseGPIO): |
| 33 | + """Class to represent a PCF8574 or PCF8574A GPIO extender. Compatible |
| 34 | + with the Adafruit_GPIO BaseGPIO class so it can be used as a custom GPIO |
| 35 | + class for interacting with device. |
| 36 | + """ |
| 37 | + |
| 38 | + NUM_GPIO = 8 |
| 39 | + |
| 40 | + def __init__(self, address, i2c=None, **kwargs): |
| 41 | + address = int(address) |
| 42 | + self.__name__ = \ |
| 43 | + "PCF8574" if address in range(0x20, 0x28) else \ |
| 44 | + "PCF8574A" if address in range(0x38, 0x40) else \ |
| 45 | + "Bad address for PCF8574(A): 0x%02X not in range [0x20..0x27, 0x38..0x3F]" % address |
| 46 | + if self.__name__[0] != 'P': |
| 47 | + raise ValueError(self.__name__) |
| 48 | + # Create I2C device. |
| 49 | + if i2c is None: |
| 50 | + i2c = I2C |
| 51 | + self._device = i2c.get_i2c_device(address, **kwargs) |
| 52 | + # Buffer register values so they can be changed without reading. |
| 53 | + self.iodir = 0xFF # Default direction to all inputs is in |
| 54 | + self.gpio = 0x00 |
| 55 | + self._write_pins() |
| 56 | + |
| 57 | + |
| 58 | + def _write_pins(self): |
| 59 | + self._device.writeRaw8(self.gpio | self.iodir) |
| 60 | + |
| 61 | + def _read_pins(self): |
| 62 | + return self._device.readRaw8() & self.iodir |
| 63 | + |
| 64 | + def _validate_pin(self, pin): |
| 65 | + # Raise an exception if pin is outside the range of allowed values. |
| 66 | + if pin < 0 or pin >= self.NUM_GPIO: |
| 67 | + raise ValueError('Invalid GPIO value, must be between 0 and {0}.'.format(self.NUM_GPIO)) |
| 68 | + |
| 69 | + def _bit2(self, src, bit, val): |
| 70 | + bit = 1 << bit |
| 71 | + return (src | bit) if val else (src & ~bit) |
| 72 | + |
| 73 | + |
| 74 | + def setup(self, pin, mode): |
| 75 | + self.setup_pins({pin: mode}) |
| 76 | + |
| 77 | + def setup_pins(self, pins): |
| 78 | + if False in [y for x,y in [(self._validate_pin(pin),mode in (IN,OUT)) for pin,mode in pins.iteritems()]]: |
| 79 | + raise ValueError('Invalid MODE, IN or OUT') |
| 80 | + for pin,mode in pins.iteritems(): |
| 81 | + self.iodir = self._bit2(self.iodir, pin, mode) |
| 82 | + self._write_pins() |
| 83 | + |
| 84 | + |
| 85 | + def output(self, pin, value): |
| 86 | + self.output_pins({pin: value}) |
| 87 | + |
| 88 | + def output_pins(self, pins): |
| 89 | + [self._validate_pin(pin) for pin in pins.keys()] |
| 90 | + for pin,value in pins.iteritems(): |
| 91 | + self.gpio = self._bit2(self.gpio, pin, bool(value)) |
| 92 | + self._write_pins() |
| 93 | + |
| 94 | + |
| 95 | + def input(self, pin): |
| 96 | + return self.input_pins([pin])[0] |
| 97 | + |
| 98 | + def input_pins(self, pins): |
| 99 | + [self._validate_pin(pin) for pin in pins] |
| 100 | + inp = self._read_pins() |
| 101 | + return [bool(inp & pin) for pin in pins] |
0 commit comments