|
| 1 | +# SPDX-FileCopyrightText: 2023 Pat Satyshur |
| 2 | +# SPDX-FileCopyrightText: 2017 Tony DiCola for Adafruit Industries |
| 3 | +# SPDX-FileCopyrightText: 2019 Carter Nelson |
| 4 | +# |
| 5 | +# SPDX-License-Identifier: MIT |
| 6 | + |
| 7 | +# pylint: disable=too-many-public-methods, duplicate-code |
| 8 | +# Note: there is a bit of duplicated code between this and the other PCAL parts. This code is |
| 9 | +# duplicated for these two expanders, but may not be if other expanders are added to this |
| 10 | +# library. I therefore want to keep is separate in these two classes. The line above |
| 11 | +# disables the pylint check for this. |
| 12 | + |
| 13 | +""" |
| 14 | +`PCAL9538` |
| 15 | +==================================================== |
| 16 | +
|
| 17 | +CircuitPython module for the PCAL9538 I2C I/O extenders. |
| 18 | +The PCAL9554 is a 8 pin IO Expander. It is software compatible with the PCA9554, but has a |
| 19 | +bunch of added functions. |
| 20 | +
|
| 21 | +Added features of these expanders include: |
| 22 | +
|
| 23 | +* Built in pull up and pull down resistors. |
| 24 | +* Per pin selectable drive strength. |
| 25 | +* Maskable interrupt pins |
| 26 | +* Latching interrupt option |
| 27 | +* Per bank push-pull/open drain pin setup. |
| 28 | +
|
| 29 | +Required library files (.py or their .mpy equivalent): |
| 30 | +
|
| 31 | +* PCAL9538.py |
| 32 | +* PCAL9554.py |
| 33 | +* PCA9554.py |
| 34 | +* i2c_expander.py |
| 35 | +* digital_inout.py |
| 36 | +* helpers.py |
| 37 | +
|
| 38 | +Compatible Devices |
| 39 | +
|
| 40 | +* PCAL9554 |
| 41 | +* PCAL9538 |
| 42 | +
|
| 43 | +These are devices I have specifically tested and know work. There appear to be a lot more devices |
| 44 | +with similar naming schemes that use the same register map. These should also be compatible, but |
| 45 | +make sure you check the i2c address and default register state. |
| 46 | +
|
| 47 | +:Note: By default if an (non-latched) interrupt enabled pin changes state, but changes back before |
| 48 | + the GPIO state register is read, the interrupt state will be cleared. Setting the interrupt |
| 49 | + latch will cause the device to latch on a state change of the input pin. With latching |
| 50 | + enabled, on a state change to the pin, the interrupt pin will be asserted and will not |
| 51 | + deassert until the input register is read. The value read from the input register will be |
| 52 | + the value that caused the interrupt, not nessecarially the current value of the pin. If the |
| 53 | + pin changed state, but changed back before the input register was read, the changed state |
| 54 | + will be what is returned in the register. The state change back to the original state will |
| 55 | + not trigger another interrupt as long as it happens before the input register is read. If |
| 56 | + the input register is read before the pin state changes back to the original value, both |
| 57 | + state changes will cause an interrupt. |
| 58 | +
|
| 59 | +Heavily based on the code written by Tony DiCola for the MCP230xx library. |
| 60 | +
|
| 61 | +* Author(s): Pat Satyshur |
| 62 | +""" |
| 63 | + |
| 64 | +# TODO: Disable unused imports here. After I finish this code, turn that off and see if they |
| 65 | +# are still unused. |
| 66 | +# pylint: disable=unused-import |
| 67 | +from micropython import const |
| 68 | +import digitalio |
| 69 | + |
| 70 | +from i2c_expanders.PCAL9554 import PCAL9554 |
| 71 | +from i2c_expanders.helpers import Capability, _get_bit, _enable_bit, _clear_bit |
| 72 | + |
| 73 | + |
| 74 | +__version__ = "0.0.0+auto.0" |
| 75 | +__repo__ = "https://github.com/ilikecake/CircuitPython_I2C_Expanders.git" |
| 76 | + |
| 77 | +# This is the default address for the PCA9538 with all addr pins grounded. |
| 78 | +_PCAL9538_DEFAULT_ADDRESS = const(0x70) |
| 79 | + |
| 80 | + |
| 81 | +class PCAL9538(PCAL9554): |
| 82 | + """The class for the PCAL9538 expander. Instantiate one of these for each expander on the bus. |
| 83 | + Make sure you get the address right. |
| 84 | + """ |
| 85 | + |
| 86 | + def __init__(self, i2c, address=_PCAL9538_DEFAULT_ADDRESS, reset=True): |
| 87 | + super().__init__( |
| 88 | + i2c, address, False |
| 89 | + ) # This initializes the PCA9554 compatible registers. |
| 90 | + self._capability = ( |
| 91 | + _enable_bit(0x00, Capability.PULL_UP) |
| 92 | + | _enable_bit(0x00, Capability.PULL_DOWN) |
| 93 | + | _enable_bit(0x00, Capability.INVERT_POL) |
| 94 | + ) |
| 95 | + |
| 96 | + if reset: |
| 97 | + self.reset_to_defaults() |
| 98 | + |
| 99 | + def reset_to_defaults(self): |
| 100 | + """Reset all registers to their default state. This is also |
| 101 | + done with a power cycle, but it can be called by software here. |
| 102 | +
|
| 103 | + :return: Nothing. |
| 104 | + """ |
| 105 | + # TODO: Should I make some sort of 'register' class to handle |
| 106 | + # memory addresses and default states? |
| 107 | + # Input port and interrupt status registers are read only. |
| 108 | + self.gpio = 0xFF |
| 109 | + self.ipol = 0x00 |
| 110 | + self.iodir = 0xFF |
| 111 | + |
| 112 | + self.out_drive = 0xFFFF |
| 113 | + self.input_latch = 0x00 |
| 114 | + self.pupd_en = 0x00 |
| 115 | + self.pupd_sel = 0xFF |
| 116 | + self.irq_mask = 0xFF |
| 117 | + self.out_port_config = 0x00 |
0 commit comments