Skip to content

Commit 57c0dfd

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.
1 parent f72c4ee commit 57c0dfd

File tree

2 files changed

+156
-13
lines changed

2 files changed

+156
-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: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
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+
16+
from micropython import const
17+
from .mcp230xx import MCP230XX
18+
from .digital_inout import DigitalInOut
19+
20+
__version__ = "0.0.0-auto.0"
21+
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_MCP230xx.git"
22+
23+
# pylint: disable=bad-whitespace
24+
_MCP23016_ADDRESS = const(0x20)
25+
_MCP23016_GPIO0 = const(0x00)
26+
_MCP23016_GPIO1 = const(0x01)
27+
_MCP23016_IPOL0 = const(0x04)
28+
_MCP23016_IPOL1 = const(0x05)
29+
_MCP23016_IODIR0 = const(0x06)
30+
_MCP23016_IODIR1 = const(0x07)
31+
_MCP23016_INTCAP0 = const(0x08)
32+
_MCP23016_INTCAP1 = const(0x09)
33+
_MCP23016_IOCON0 = const(0x0A)
34+
_MCP23016_IOCON1 = const(0x0B)
35+
36+
37+
class MCP23016(MCP230XX):
38+
"""Supports MCP23016 instance on specified I2C bus and optionally
39+
at the specified I2C address.
40+
"""
41+
42+
def __init__(self, i2c, address=_MCP23016_ADDRESS):
43+
super().__init__(i2c, address)
44+
45+
# Reset to all inputs and no inverted polarity.
46+
self.iodir = 0xFFFF
47+
self._write_u16le(_MCP23016_IPOL0, 0x0000)
48+
49+
@property
50+
def gpio(self):
51+
"""The raw GPIO output register. Each bit represents the
52+
output value of the associated pin (0 = low, 1 = high), assuming that
53+
pin has been configured as an output previously.
54+
"""
55+
return self._read_u16le(_MCP23016_GPIO0)
56+
57+
@gpio.setter
58+
def gpio(self, val):
59+
self._write_u16le(_MCP23016_GPIO0, val)
60+
61+
@property
62+
def gpio0(self):
63+
"""The raw GPIO 0 output register. Each bit represents the
64+
output value of the associated pin (0 = low, 1 = high), assuming that
65+
pin has been configured as an output previously.
66+
"""
67+
return self._read_u8(_MCP23016_GPIO0)
68+
69+
@gpio0.setter
70+
def gpio0(self, val):
71+
self._write_u8(_MCP23016_GPIO0, val)
72+
73+
@property
74+
def gpio1(self):
75+
"""The raw GPIO 1 output register. Each bit represents the
76+
output value of the associated pin (0 = low, 1 = high), assuming that
77+
pin has been configured as an output previously.
78+
"""
79+
return self._read_u8(_MCP23016_GPIO1)
80+
81+
@gpio1.setter
82+
def gpio1(self, val):
83+
self._write_u8(_MCP23016_GPIO1, val)
84+
85+
@property
86+
def iodir(self):
87+
"""The raw IODIR direction register. Each bit represents
88+
direction of a pin, either 1 for an input or 0 for an output mode.
89+
"""
90+
return self._read_u16le(_MCP23016_IODIR0)
91+
92+
@iodir.setter
93+
def iodir(self, val):
94+
self._write_u16le(_MCP23016_IODIR0, val)
95+
96+
@property
97+
def iodir0(self):
98+
"""The raw IODIR0 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_u8(_MCP23016_IODIR0)
102+
103+
@iodir0.setter
104+
def iodir0(self, val):
105+
self._write_u8(_MCP23016_IODIR0, val)
106+
107+
@property
108+
def iodir1(self):
109+
"""The raw IODIR0 direction register. Each bit represents
110+
direction of a pin, either 1 for an input or 0 for an output mode.
111+
"""
112+
return self._read_u8(_MCP23016_IODIR1)
113+
114+
@iodir1.setter
115+
def iodir1(self, val):
116+
self._write_u8(_MCP23016_IODIR1, val)
117+
118+
def get_pin(self, pin):
119+
"""Convenience function to create an instance of the DigitalInOut class
120+
pointing at the specified pin of this MCP23017 device.
121+
"""
122+
assert 0 <= pin <= 15
123+
return DigitalInOut(pin, self)
124+
125+
def clear_int0(self):
126+
"""Clears port 0 interrupts."""
127+
self._read_u8(_MCP23016_INTCAP0)
128+
129+
def clear_int1(self):
130+
"""Clears port 1 interrupts."""
131+
self._read_u8(_MCP23016_INTCAP1)

0 commit comments

Comments
 (0)