Skip to content

Commit 7d668f2

Browse files
committed
Fix comments for better documentation
1 parent 92eda70 commit 7d668f2

File tree

6 files changed

+330
-179
lines changed

6 files changed

+330
-179
lines changed

i2c_expanders/PCA9554.py

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,25 @@
3636
* PCA9538
3737
* TODO
3838
39+
Note: Some devices have the same command set and register, but different i2c addresses and register
40+
defaults. These devices should work fine with this class, but make sure the addresses are set right
41+
when initializing them.
42+
3943
Heavily based on the code written by Tony DiCola for the MCP230xx library.
4044
4145
* Author(s): Pat Satyshur
4246
"""
4347

4448
# TODO: Fix these imports.
45-
from micropython import (
46-
const,
47-
) # TODO: What does const get me in this situation? Can I remove it?
49+
from micropython import const
4850
from i2c_expanders.i2c_expander import I2c_Expander
4951
from i2c_expanders.helpers import _enable_bit, Capability
5052

5153
__version__ = "0.0.0+auto.0"
5254
__repo__ = "https://github.com/ilikecake/CircuitPython_I2C_Expanders.git"
5355

54-
# TODO: probably don't want this here.
55-
_PCA9554_ADDRESS = const(0x27)
56+
# This is the default address for the PCA9554 with all addr pins grounded.
57+
_PCA9554_DEFAULT_ADDRESS = const(0x20)
5658

5759
_PCA9554_INPUT = const(0x00) # Input register
5860
_PCA9554_OUTPUT = const(0x01) # Output register
@@ -65,10 +67,10 @@ class PCA9554(I2c_Expander):
6567
at the specified I2C address.
6668
"""
6769

68-
def __init__(self, i2c, address=_PCA9554_ADDRESS, reset=True):
70+
def __init__(self, i2c, address=_PCA9554_DEFAULT_ADDRESS, reset=True):
6971
super().__init__(i2c, address)
70-
self.maxpins = 7
71-
self.capability = _enable_bit(0x00, Capability.INVERT_POL)
72+
self._maxpins = 7
73+
self._capability = _enable_bit(0x00, Capability.INVERT_POL)
7274
if reset:
7375
self.reset_to_defaults()
7476

@@ -78,44 +80,51 @@ def reset_to_defaults(self):
7880
7981
:return: Nothing.
8082
"""
81-
# TODO: Should I make some sort of 'register' class to
82-
# handle memory addresses and default states?
8383
# Input port register is read only.
8484
self.gpio = 0xFF
8585
self.ipol = 0x00
8686
self.iodir = 0xFF
8787

8888
@property
8989
def gpio(self):
90-
"""The raw GPIO output register. Each bit represents the
91-
output value of the associated pin (0 = low, 1 = high), assuming that
92-
pin has been configured as an output previously.
90+
"""The raw GPIO port registers. Each bit represents the value of the associated pin
91+
(0 = low, 1 = high). Read this register to get the value of all pins. Write to this
92+
register to set the value of any pins configured as outputs.
93+
Read and written as a 8 bit number.
94+
95+
Register address (read): 0x00
96+
97+
Register address (write): 0x01
9398
"""
9499
return self._read_u8(_PCA9554_INPUT)
95100

96101
@gpio.setter
97102
def gpio(self, val):
98103
self._write_u8(_PCA9554_OUTPUT, val)
99104

100-
@property
101-
def iodir(self):
102-
"""The raw IODIR direction register. Each bit represents
103-
direction of a pin, either 1 for an input or 0 for an output mode.
104-
"""
105-
return self._read_u8(_PCA9554_IODIR)
106-
107-
@iodir.setter
108-
def iodir(self, val):
109-
self._write_u8(_PCA9554_IODIR, val)
110-
111105
@property
112106
def ipol(self):
113-
"""The raw IPOL output register. Each bit represents the
114-
polarity value of the associated pin (0 = normal, 1 = inverted), assuming that
115-
pin has been configured as an input previously.
107+
"""The raw 'polarity inversion' register. Each bit represents the polarity value of the
108+
associated pin (0 = normal, 1 = inverted). This only applies to pins configured as inputs.
109+
Read and written as a 8 bit number.
110+
111+
Register address: 0x02
116112
"""
117113
return self._read_u8(_PCA9554_IPOL)
118114

119115
@ipol.setter
120116
def ipol(self, val):
121117
self._write_u8(_PCA9554_IPOL, val)
118+
119+
@property
120+
def iodir(self):
121+
"""The raw pin configuration register. Each bit represents direction of a pin, either 1
122+
for an input or 0 for an output. Read and written as a 8 bit number.
123+
124+
Register address: 0x03
125+
"""
126+
return self._read_u8(_PCA9554_IODIR)
127+
128+
@iodir.setter
129+
def iodir(self, val):
130+
self._write_u8(_PCA9554_IODIR, val)

i2c_expanders/PCA9555.py

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -76,55 +76,61 @@ class PCA9555(I2c_Expander):
7676

7777
def __init__(self, i2c, address=_PCA9555_ADDRESS, reset=True):
7878
super().__init__(i2c, address)
79-
self.maxpins = 15
80-
self.capability = _enable_bit(0x00, Capability.INVERT_POL)
79+
self._maxpins = 15
80+
self._capability = _enable_bit(0x00, Capability.INVERT_POL)
8181
if reset:
8282
self.reset_to_defaults()
8383

84-
@property
85-
def gpio(self):
86-
"""The raw GPIO output register. Each bit represents the
87-
output value of the associated pin (0 = low, 1 = high), assuming that
88-
pin has been configured as an output previously.
89-
"""
90-
return self._read_u16le(_PCA9555_INPUT0)
91-
92-
@gpio.setter
93-
def gpio(self, val):
94-
self._write_u16le(_PCA9555_OUTPUT0, val)
95-
96-
@property
97-
def iodir(self):
98-
"""The raw IODIR direction register. Each bit represents
99-
direction of a pin, either 1 for an input or 0 for an output mode.
100-
"""
101-
return self._read_u16le(_PCA9555_IODIR0)
102-
103-
@iodir.setter
104-
def iodir(self, val):
105-
self._write_u16le(_PCA9555_IODIR0, val)
106-
10784
def reset_to_defaults(self):
10885
"""Reset all registers to their default state. This is also
10986
done with a power cycle, but it can be called by software here.
11087
11188
:return: Nothing.
11289
"""
113-
# TODO: Should I make some sort of 'register' class to
114-
# handle memory addresses and default states?
115-
# Input port register is read only.
11690
self.gpio = 0xFFFF
11791
self.ipol = 0x0000
11892
self.iodir = 0xFFFF
11993

94+
@property
95+
def gpio(self):
96+
"""The raw GPIO port registers. Each bit represents the value of the associated pin
97+
(0 = low, 1 = high). Read this register to get the value of all pins. Write to this
98+
register to set the value of any pins configured as outputs.
99+
Read and written as a 16 bit number.
100+
101+
Register address (read): 0x00, 0x01
102+
103+
Register address (write): 0x02, 0x03
104+
"""
105+
return self._read_u16le(_PCA9555_INPUT0)
106+
107+
@gpio.setter
108+
def gpio(self, val):
109+
self._write_u16le(_PCA9555_OUTPUT0, val)
110+
120111
@property
121112
def ipol(self):
122-
"""The raw IPOL output register. Each bit represents the
123-
polarity value of the associated pin (0 = normal, 1 = inverted), assuming that
124-
pin has been configured as an input previously.
113+
"""The raw 'polarity inversion' register. Each bit represents the polarity value of the
114+
associated pin (0 = normal, 1 = inverted). This only applies to pins configured as inputs.
115+
Read and written as a 16 bit number.
116+
117+
Register address: 0x04, 0x05
125118
"""
126119
return self._read_u16le(_PCA9555_IPOL0)
127120

128121
@ipol.setter
129122
def ipol(self, val):
130123
self._write_u16le(_PCA9555_IPOL0, val)
124+
125+
@property
126+
def iodir(self):
127+
"""The raw pin configuration register. Each bit represents direction of a pin, either 1
128+
for an input or 0 for an output. Read and written as a 16 bit number.
129+
130+
Register address: 0x06, 0x07
131+
"""
132+
return self._read_u16le(_PCA9555_IODIR0)
133+
134+
@iodir.setter
135+
def iodir(self, val):
136+
self._write_u16le(_PCA9555_IODIR0, val)

i2c_expanders/PCAL9554.py

Lines changed: 86 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,22 @@
3636
* PCAL9538
3737
* TODO
3838
39+
Note: Some devices have the same command set and register, but different i2c addresses and register
40+
defaults. These devices should work fine with this class, but make sure the addresses are set right
41+
when initializing them.
42+
43+
:Note: By default if an (non-latched) interrupt enabled pin changes state, but changes back before
44+
the GPIO state register is read, the interrupt state will be cleared. Setting the interrupt
45+
latch will cause the device to latch on a state change of the input pin. With latching
46+
enabled, on a state change to the pin, the interrupt pin will be asserted and will not
47+
deassert until the input register is read. The value read from the input register will be
48+
the value that caused the interrupt, not nessecarially the current value of the pin. If the
49+
pin changed state, but changed back before the input register was read, the changed state
50+
will be what is returned in the register. The state change back to the original state will
51+
not trigger another interrupt as long as it happens before the input register is read. If
52+
the input register is read before the pin state changes back to the original value, both
53+
state changes will cause an interrupt.
54+
3955
Heavily based on the code written by Tony DiCola for the MCP230xx library.
4056
4157
* Author(s): Pat Satyshur
@@ -80,12 +96,12 @@ def __init__(self, i2c, address=_PCAL9554_ADDRESS, reset=True):
8096
super().__init__(
8197
i2c, address, False
8298
) # This initializes the PCA9554 compatible registers.
83-
self.capability = (
99+
self._capability = (
84100
_enable_bit(0x00, Capability.PULL_UP)
85101
| _enable_bit(0x00, Capability.PULL_DOWN)
86102
| _enable_bit(0x00, Capability.INVERT_POL)
87-
) # TODO: This device does not really have a capability to set drive mode the way
88-
# digitalio is expecting. I should probably not set this here.
103+
)
104+
89105
if reset:
90106
self.reset_to_defaults()
91107

@@ -118,6 +134,26 @@ def clear_int_pin(self, pin):
118134
self._validate_pin(pin)
119135
self.irq_mask = _enable_bit(self.irq_mask, pin)
120136

137+
def get_interrupts(self):
138+
"""Returns a list of pins causing an interruptn along with the value of those pins.
139+
It is possible for multiple pins to be causing an interrupt. Calling this function
140+
clears the interrupt state.
141+
142+
:return: Returns a list of dicts containing items "pin" and "value". If no
143+
interrupts are triggered, this function returns none.
144+
"""
145+
output = []
146+
int_status = self.irq_status
147+
pin_values = self.gpio
148+
149+
for i in range(self.maxpins):
150+
if bool((int_status >> i) & 1):
151+
pin_val = bool(((pin_values >> i) & 1))
152+
output.append({"pin": i, "value": pin_val})
153+
if not output:
154+
return None
155+
return output
156+
121157
def get_int_pins(self):
122158
"""Returns a list of pins causing an interrupt. It is possible for multiple pins
123159
to be causing an interrupt. Calling this function will not clear the interrupt state.
@@ -151,16 +187,6 @@ def clear_int_latch(self, pin):
151187
self._validate_pin(pin)
152188
self.input_latch = _clear_bit(self.input_latch, pin)
153189

154-
"""Interrupt latch behavior
155-
By default (non-latched) if an interrupt enabled pin changes state, but changes back before the GPIO state register is read, the interrupt state
156-
will be cleared. Setting the interrupt latch will cause the device to latch on a state change of the input pin. With latching enabled, on a state
157-
change to the pin, the interrupt pin will be asserted and will not deassert until the input register is read. The value read from the input register
158-
will be the value that caused the interrupt, not nessecarially the current value of the pin. If the pin changed state, but changed back before the
159-
input register was read, the changed state will be what is returned in the register. The state change back to the original state will not trigger
160-
another interrupt as long as it happens before the input register is read. If the input register is read before the pin state changes back to the
161-
original value, both state changes will cause an interrupt.
162-
"""
163-
164190
def get_pupd(self, pin):
165191
"""Checks the state of a pin to see if pull up/down is enabled.
166192
@@ -292,7 +318,11 @@ def reset_to_defaults(self):
292318

293319
@property
294320
def out_drive(self):
295-
"""Output drive strength of pins 0-7."""
321+
"""The raw 'output drive strength' register. Controls the drive strength of the pins.
322+
Read and written as a 16 bit number.
323+
324+
Register address: 0x40, 0x41.
325+
"""
296326
return self._read_u16le(_PCAL9554_OUTPUT_DRIVE_1)
297327

298328
@out_drive.setter
@@ -301,7 +331,12 @@ def out_drive(self, val):
301331

302332
@property
303333
def input_latch(self):
304-
"""Sets latching or non-latching interrupts per pin."""
334+
"""The raw 'input latch' register. Each bit represents the latch configuration for the
335+
matching pin. A zero indicates that the corresponding input pin is not latched. Read and
336+
written as a 8 bit number.
337+
338+
Register address: 0x42.
339+
"""
305340
return self._read_u8(_PCAL9554_INPUT_LATCH)
306341

307342
@input_latch.setter
@@ -310,7 +345,14 @@ def input_latch(self, val):
310345

311346
@property
312347
def pupd_en(self):
313-
"""reads the pull up/down status"""
348+
"""The raw 'pull-up/pull-down enable' register. Each bit represents the enabled state of
349+
the pull up/down resistors for that pin. A one indicates that the pull up/down resistors
350+
are enabled. The selection of pull-up vs pull-down is done with the 'pull-up/pull-down
351+
selection register'. A zero indicates that the pull up/down resistors are disconnected.
352+
Read and written as a 8 bit number.
353+
354+
Register address: 0x43.
355+
"""
314356
return self._read_u8(_PCAL9554_PUPD_EN)
315357

316358
@pupd_en.setter
@@ -319,7 +361,13 @@ def pupd_en(self, val):
319361

320362
@property
321363
def pupd_sel(self):
322-
"""reads the pull up/down status"""
364+
"""The raw 'pull-up/pull-down selection' register. Each bit enables either a pull-up or
365+
pull-down resistor on that corresponding pin. A one selects a pull-up and a zero selects a
366+
pull-down. Internal pull up/down resistors are ~100 KOhm (+/-50 KOhm). Read and written as
367+
a 8 bit number.
368+
369+
Register address: 0x44.
370+
"""
323371
return self._read_u8(_PCAL9554_PUPD_SEL)
324372

325373
@pupd_sel.setter
@@ -328,7 +376,12 @@ def pupd_sel(self, val):
328376

329377
@property
330378
def irq_mask(self):
331-
"""Masks or unmasks pins for generating interrupts."""
379+
"""The raw 'interrupt mask' register. Setting a bit to one will mask interrupts on that
380+
corresponding pin. All interrupts are masked by default. Read and written as a 8 bit
381+
number.
382+
383+
Register address: 0x45.
384+
"""
332385
return self._read_u8(_PCAL9554_IRQ_MASK)
333386

334387
@irq_mask.setter
@@ -337,7 +390,13 @@ def irq_mask(self, val):
337390

338391
@property
339392
def irq_status(self):
340-
"""Indicates which pin caused an interrupt."""
393+
"""The raw 'interrupt status' register. Reading this register will tell the source of an
394+
interrupt. A one read from a bit in this register indicates that the corresponding pin
395+
caused the interrupt. This register is read only. Reading from this register does not clear
396+
the interrupt state. Read and written as a 8 bit number.
397+
398+
Register address: 0x46.
399+
"""
341400
return self._read_u8(_PCAL9554_IRQ_STATUS)
342401

343402
@irq_status.setter
@@ -347,9 +406,15 @@ def irq_status(self, val):
347406

348407
@property
349408
def out_port_config(self):
350-
"""Sets output banks to open drain or push-pull operation."""
409+
"""The raw 'output port configuration' register. Use bit zero of this register to set the
410+
output pins to either open-drain or push-pull operation. Set the bit to zero to configure
411+
the pins as push-pull. Set to one to configure the pins as open-drain. All other bits are
412+
reserved. Read and written as a 8 bit number.
413+
414+
Register address: 0x4F.
415+
"""
351416
return self._read_u8(_PCAL9554_OUTPUT_PORT_CONFIG)
352417

353418
@out_port_config.setter
354419
def out_port_config(self, val):
355-
self._write_u8(_PCAL9554_OUTPUT_PORT_CONFIG, val)
420+
self._write_u8((_PCAL9554_OUTPUT_PORT_CONFIG & 0x01), val)

0 commit comments

Comments
 (0)