Skip to content

Commit 0d3ee70

Browse files
committed
Add support for the old, deprecated MCP23016 expander, too.
The main difference with the '17 is that it lacks advanced interrupt control features, and pull-up configuration. This includes changes to DigitalInOut to raise ValueError when trying to set pull-up configuration as well as pull-down, on the MCP23016 only. Note that for compatibility with the already existing '17 module, and for an eventual '18 module, the ports are called A and B, rather than 0 and 1.
1 parent fc934c1 commit 0d3ee70

File tree

2 files changed

+163
-13
lines changed

2 files changed

+163
-13
lines changed

adafruit_mcp230xx/digital_inout.py

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,13 @@ def _clear_bit(val, bit):
3232

3333
class DigitalInOut:
3434
"""Digital input/output of the MCP230xx. The interface is exactly the
35-
same as the digitalio.DigitalInOut class (however the MCP230xx does not
36-
support pull-down resistors and an exception will be thrown
37-
attempting to set one).
35+
same as the digitalio.DigitalInOut class, however:
36+
37+
* MCP230xx family does not support pull-down resistors;
38+
* MCP23016 does not support pull-up resistors.
39+
40+
Exceptions will be thrown when attempting to set unsupported pull
41+
configurations.
3842
"""
3943

4044
def __init__(self, pin_number, mcp230xx):
@@ -105,17 +109,25 @@ def pull(self):
105109
value of digitalio.Pull.UP will enable a pull-up resistor, and None will
106110
disable it. Pull-down resistors are NOT supported!
107111
"""
108-
if _get_bit(self._mcp.gppu, self._pin):
109-
return digitalio.Pull.UP
112+
try:
113+
if _get_bit(self._mcp.gppu, self._pin):
114+
return digitalio.Pull.UP
115+
except AttributeError:
116+
# MCP23016 doesn't have a `gppu` register.
117+
raise ValueError("Pull-up/pull-down resistors not supported.")
110118
return None
111119

112120
@pull.setter
113121
def pull(self, val):
114-
if val is None:
115-
self._mcp.gppu = _clear_bit(self._mcp.gppu, self._pin)
116-
elif val == digitalio.Pull.UP:
117-
self._mcp.gppu = _enable_bit(self._mcp.gppu, self._pin)
118-
elif val == digitalio.Pull.DOWN:
119-
raise ValueError("Pull-down resistors are not supported!")
120-
else:
121-
raise ValueError("Expected UP, DOWN, or None for pull state!")
122+
try:
123+
if val is None:
124+
self._mcp.gppu = _clear_bit(self._mcp.gppu, self._pin)
125+
elif val == digitalio.Pull.UP:
126+
self._mcp.gppu = _enable_bit(self._mcp.gppu, self._pin)
127+
elif val == digitalio.Pull.DOWN:
128+
raise ValueError("Pull-down resistors are not supported!")
129+
else:
130+
raise ValueError("Expected UP, DOWN, or None for pull state!")
131+
except AttributeError:
132+
# MCP23016 doesn't have a `gppu` register.
133+
raise ValueError("Pull-up/pull-down resistors not supported.")

adafruit_mcp230xx/mcp23016.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# SPDX-FileCopyrightText: 2017 Tony DiCola for Adafruit Industries
2+
# SPDX-FileCopyrightText: 2019 Carter Nelson
3+
# SPDX-FileCopyrightText: 2020 Facebook Inc.
4+
#
5+
# SPDX-License-Identifier: MIT
6+
7+
"""
8+
`mcp23016`
9+
====================================================
10+
11+
CircuitPython module for the MCP23016 I2C I/O extenders.
12+
13+
* Author(s): Diego Elio Pettenò (based on MCP23017.py)
14+
15+
Notes
16+
-----
17+
18+
While the datasheet refers to the two 8-bit ports as port 0 and 1,
19+
for API compatibility with more recent expanders, these are exposed as
20+
ports A and B.
21+
"""
22+
23+
from micropython import const
24+
from .mcp230xx import MCP230XX
25+
from .digital_inout import DigitalInOut
26+
27+
__version__ = "0.0.0-auto.0"
28+
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_MCP230xx.git"
29+
30+
# pylint: disable=bad-whitespace
31+
_MCP23016_ADDRESS = const(0x20)
32+
_MCP23016_GPIO0 = const(0x00)
33+
_MCP23016_GPIO1 = const(0x01)
34+
_MCP23016_IPOL0 = const(0x04)
35+
_MCP23016_IPOL1 = const(0x05)
36+
_MCP23016_IODIR0 = const(0x06)
37+
_MCP23016_IODIR1 = const(0x07)
38+
_MCP23016_INTCAP0 = const(0x08)
39+
_MCP23016_INTCAP1 = const(0x09)
40+
_MCP23016_IOCON0 = const(0x0A)
41+
_MCP23016_IOCON1 = const(0x0B)
42+
43+
44+
class MCP23016(MCP230XX):
45+
"""Supports MCP23016 instance on specified I2C bus and optionally
46+
at the specified I2C address.
47+
"""
48+
49+
def __init__(self, i2c, address=_MCP23016_ADDRESS):
50+
super().__init__(i2c, address)
51+
52+
# Reset to all inputs and no inverted polarity.
53+
self.iodir = 0xFFFF
54+
self._write_u16le(_MCP23016_IPOL0, 0x0000)
55+
56+
@property
57+
def gpio(self):
58+
"""The raw GPIO output register. Each bit represents the
59+
output value of the associated pin (0 = low, 1 = high), assuming that
60+
pin has been configured as an output previously.
61+
"""
62+
return self._read_u16le(_MCP23016_GPIO0)
63+
64+
@gpio.setter
65+
def gpio(self, val):
66+
self._write_u16le(_MCP23016_GPIO0, val)
67+
68+
@property
69+
def gpioa(self):
70+
"""The raw GPIO 0 output register. Each bit represents the
71+
output value of the associated pin (0 = low, 1 = high), assuming that
72+
pin has been configured as an output previously.
73+
"""
74+
return self._read_u8(_MCP23016_GPIO0)
75+
76+
@gpioa.setter
77+
def gpioa(self, val):
78+
self._write_u8(_MCP23016_GPIO0, val)
79+
80+
@property
81+
def gpiob(self):
82+
"""The raw GPIO 1 output register. Each bit represents the
83+
output value of the associated pin (0 = low, 1 = high), assuming that
84+
pin has been configured as an output previously.
85+
"""
86+
return self._read_u8(_MCP23016_GPIO1)
87+
88+
@gpiob.setter
89+
def gpiob(self, val):
90+
self._write_u8(_MCP23016_GPIO1, val)
91+
92+
@property
93+
def iodir(self):
94+
"""The raw IODIR direction register. Each bit represents
95+
direction of a pin, either 1 for an input or 0 for an output mode.
96+
"""
97+
return self._read_u16le(_MCP23016_IODIR0)
98+
99+
@iodir.setter
100+
def iodir(self, val):
101+
self._write_u16le(_MCP23016_IODIR0, val)
102+
103+
@property
104+
def iodira(self):
105+
"""The raw IODIR0 direction register. Each bit represents
106+
direction of a pin, either 1 for an input or 0 for an output mode.
107+
"""
108+
return self._read_u8(_MCP23016_IODIR0)
109+
110+
@iodira.setter
111+
def iodira(self, val):
112+
self._write_u8(_MCP23016_IODIR0, val)
113+
114+
@property
115+
def iodirb(self):
116+
"""The raw IODIR0 direction register. Each bit represents
117+
direction of a pin, either 1 for an input or 0 for an output mode.
118+
"""
119+
return self._read_u8(_MCP23016_IODIR1)
120+
121+
@iodirb.setter
122+
def iodirb(self, val):
123+
self._write_u8(_MCP23016_IODIR1, val)
124+
125+
def get_pin(self, pin):
126+
"""Convenience function to create an instance of the DigitalInOut class
127+
pointing at the specified pin of this MCP23016 device.
128+
"""
129+
assert 0 <= pin <= 15
130+
return DigitalInOut(pin, self)
131+
132+
def clear_inta(self):
133+
"""Clears port 0 interrupts."""
134+
self._read_u8(_MCP23016_INTCAP0)
135+
136+
def clear_intb(self):
137+
"""Clears port 1 interrupts."""
138+
self._read_u8(_MCP23016_INTCAP1)

0 commit comments

Comments
 (0)