Skip to content

Commit 197b31c

Browse files
committed
Add named support for the PCAL9538
The PCAL9538 is identical to the PCAL9554 except for different default register values and i2c address.
1 parent 8461980 commit 197b31c

File tree

2 files changed

+118
-1
lines changed

2 files changed

+118
-1
lines changed

i2c_expanders/PCAL9538.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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

i2c_expanders/PCAL9554.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ def reset_to_defaults(self):
305305

306306
self.out_drive = 0xFFFF
307307
self.input_latch = 0x00
308-
self.pupd_en = 0x00 # TODO: 0xFF for PCAL9554, 0x00 for PCAL9538
308+
self.pupd_en = 0xFF
309309
self.pupd_sel = 0xFF
310310
self.irq_mask = 0xFF
311311
self.out_port_config = 0x00

0 commit comments

Comments
 (0)