Skip to content

Commit 3c5d3af

Browse files
author
KTOWN
committed
Fixed bug with bits 8-15, improved sample code
1 parent 4c46868 commit 3c5d3af

File tree

1 file changed

+155
-175
lines changed

1 file changed

+155
-175
lines changed
Lines changed: 155 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -1,193 +1,170 @@
11
#!/usr/bin/python
22

3-
# Copyright 2012 Daniel Berlin (with some changes by Limor Fried,
4-
# Adafruit Industries)
5-
6-
# Permission is hereby granted, free of charge, to any person obtaining a
7-
# copy of this software and associated documentation files (the "Software"),
8-
# to deal in the Software without restriction, including without limitation
9-
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
10-
# and/or sell copies of the Software, and to permit persons to whom the
11-
# Software is furnished to do so, subject to the following conditions:
12-
13-
# The above copyright notice and this permission notice shall be included
14-
# in all copies or substantial portions of the Software.
3+
# Copyright 2012 Daniel Berlin (with some changes by Adafruit Industries/Limor Fried)
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
6+
# this software and associated documentation files (the "Software"), to deal MCP230XX_GPIO(1, 0xin
7+
# the Software without restriction, including without limitation the rights to
8+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9+
# of the Software, and to permit persons to whom the Software is furnished to do
10+
# so, subject to the following conditions:
11+
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
1514

1615
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1716
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19-
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21-
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22-
# DEALINGS IN THE SOFTWARE.
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
# SOFTWARE.
2322

2423
from Adafruit_I2C import Adafruit_I2C
25-
26-
class Adafruit_MCP230XX(Adafruit_I2C):
27-
28-
INPUT = True
29-
OUTPUT = False
30-
31-
MCP23017_IODIRA = 0x00
32-
MCP23017_IODIRB = 0x01
33-
MCP23017_GPPUA = 0x0C
34-
MCP23017_GPPUB = 0x0D
35-
MCP23017_GPIOA = 0x12
36-
MCP23017_GPIOB = 0x13
37-
MCP23017_OLATA = 0x14
38-
MCP23017_OLATB = 0x15
39-
MCP23008_IODIR = 0x00
40-
MCP23008_GPIO = 0x09
41-
MCP23008_GPPU = 0x06
42-
MCP23008_OLAT = 0x0A
43-
44-
45-
def __init__(self, address, num_gpios=8, busnum=-1, debug=False):
46-
47-
assert 0 < num_gpios < 17, "Number of GPIOs must be between 1 and 16"
48-
49-
self.i2c = Adafruit_I2C(address, busnum, debug)
24+
import smbus
25+
import time
26+
27+
MCP23017_IODIRA = 0x00
28+
MCP23017_IODIRB = 0x01
29+
MCP23017_GPIOA = 0x12
30+
MCP23017_GPIOB = 0x13
31+
MCP23017_GPPUA = 0x0C
32+
MCP23017_GPPUB = 0x0D
33+
MCP23017_OLATA = 0x14
34+
MCP23017_OLATB = 0x15
35+
MCP23008_GPIOA = 0x09
36+
MCP23008_GPPUA = 0x06
37+
MCP23008_OLATA = 0x0A
38+
39+
class Adafruit_MCP230XX(object):
40+
OUTPUT = 0
41+
INPUT = 1
42+
43+
def __init__(self, address, num_gpios):
44+
assert num_gpios >= 0 and num_gpios <= 16, "Number of GPIOs must be between 0 and 16"
45+
self.i2c = Adafruit_I2C(address=address)
46+
self.address = address
5047
self.num_gpios = num_gpios
51-
self.pullups = 0
5248

53-
# Set default pin values -- all inputs with pull-ups disabled.
54-
# Current OLAT (output) value is polled, not set.
49+
# set defaults
5550
if num_gpios <= 8:
56-
self.direction = 0xFF
57-
self.i2c.write8(self.MCP23008_IODIR, self.direction)
58-
self.i2c.write8(self.MCP23008_GPPU , self.pullups)
59-
self.outputvalue = self.i2c.readU8(self.MCP23008_OLAT)
60-
else:
61-
self.direction = 0xFFFF
62-
self.i2c.write16(self.MCP23017_IODIRA, self.direction)
63-
self.i2c.write16(self.MCP23017_GPPUA , self.pullups)
64-
self.outputvalue = self.i2c.readU16(self.MCP23017_OLATA)
65-
66-
67-
# Set single pin to either INPUT or OUTPUT mode
68-
def config(self, pin, mode):
69-
70-
assert 0 <= pin < self.num_gpios, "Pin number %s is invalid, must be between 0 and %s" % (pin, self.num_gpios-1)
51+
self.i2c.write8(MCP23017_IODIRA, 0xFF) # all inputs on port A
52+
self.direction = self.i2c.readU8(MCP23017_IODIRA)
53+
self.i2c.write8(MCP23008_GPPUA, 0x00)
54+
elif num_gpios > 8 and num_gpios <= 16:
55+
self.i2c.write8(MCP23017_IODIRA, 0xFF) # all inputs on port A
56+
self.i2c.write8(MCP23017_IODIRB, 0xFF) # all inputs on port B
57+
self.direction = self.i2c.readU8(MCP23017_IODIRA)
58+
self.direction |= self.i2c.readU8(MCP23017_IODIRB) << 8
59+
self.i2c.write8(MCP23017_GPPUA, 0x00)
60+
self.i2c.write8(MCP23017_GPPUB, 0x00)
61+
62+
def _changebit(self, bitmap, bit, value):
63+
assert value == 1 or value == 0, "Value is %s must be 1 or 0" % value
64+
if value == 0:
65+
return bitmap & ~(1 << bit)
66+
elif value == 1:
67+
return bitmap | (1 << bit)
68+
69+
def _readandchangepin(self, port, pin, value, currvalue = None):
70+
assert pin >= 0 and pin < self.num_gpios, "Pin number %s is invalid, only 0-%s are valid" % (pin, self.num_gpios)
71+
#assert self.direction & (1 << pin) == 0, "Pin %s not set to output" % pin
72+
if not currvalue:
73+
currvalue = self.i2c.readU8(port)
74+
newvalue = self._changebit(currvalue, pin, value)
75+
self.i2c.write8(port, newvalue)
76+
return newvalue
7177

72-
if mode is self.INPUT: self.direction |= (1 << pin)
73-
else: self.direction &= ~(1 << pin)
7478

79+
def pullup(self, pin, value):
7580
if self.num_gpios <= 8:
76-
self.i2c.write8(self.MCP23008_IODIR, self.direction)
77-
elif pin < 8:
78-
# Replace low bits (IODIRA)
79-
self.i2c.write8(self.MCP23017_IODIRA, self.direction & 0xFF)
80-
else:
81-
# Replace high bits (IODIRB)
82-
self.i2c.write8(self.MCP23017_IODIRB, self.direction >> 8)
83-
84-
return self.direction
85-
86-
87-
# Enable pull-up resistor on single input pin
88-
def pullup(self, pin, enable, check=False):
89-
90-
assert 0 <= pin < self.num_gpios, "Pin number %s is invalid, must be between 0 and %s" % (pin, self.num_gpios-1)
91-
if check:
92-
assert (self.direction & (1 << pin)) != 0, "Pin %s not set to input" % pin
93-
94-
if enable: self.pullups |= (1 << pin)
95-
else: self.pullups &= ~(1 << pin)
81+
return self._readandchangepin(MCP23008_GPPUA, pin, value)
82+
if self.num_gpios <= 16:
83+
if (pin < 8):
84+
return self._readandchangepin(MCP23017_GPPUA, pin, value)
85+
else:
86+
return self._readandchangepin(MCP23017_GPPUB, pin-8, value) << 8
9687

88+
# Set pin to either input or output mode
89+
def config(self, pin, mode):
9790
if self.num_gpios <= 8:
98-
self.i2c.write8(self.MCP23008_GPPU, self.pullups)
99-
elif pin < 8:
100-
# Replace low bits (GPPUA)
101-
self.i2c.write8(self.MCP23017_GPPUA, self.pullups & 0xFF)
102-
else:
103-
# Replace high bits (GPPUB)
104-
self.i2c.write8(self.MCP23017_GPPUB, self.pullups >> 8)
105-
106-
return self.pullups
107-
108-
109-
# Read value from single input pin
110-
def input(self, pin, check=True):
91+
self.direction = self._readandchangepin(MCP23017_IODIRA, pin, mode)
92+
if self.num_gpios <= 16:
93+
if (pin < 8):
94+
self.direction = self._readandchangepin(MCP23017_IODIRA, pin, mode)
95+
else:
96+
self.direction |= self._readandchangepin(MCP23017_IODIRB, pin-8, mode) << 8
11197

112-
assert 0 <= pin < self.num_gpios, "Pin number %s is invalid, must be between 0 and %s" % (pin, self.num_gpios-1)
113-
if check:
114-
assert (self.direction & (1 << pin)) != 0, "Pin %s not set to input" % pin
98+
return self.direction
11599

116-
if self.num_gpios <= 8:
117-
value = self.i2c.readU8(self.MCP23008_GPIO)
118-
return (value >> pin) & 1
119-
elif pin < 8:
120-
# Read from low bits (GPIOA)
121-
value = self.i2c.readU8(self.MCP23017_GPIOA)
122-
return (value >> pin) & 1
123-
else:
124-
# Read from high bits (GPIOB)
125-
value = self.i2c.readU8(self.MCP23017_GPIOB)
126-
return (value >> (pin - 8)) & 1
127-
128-
129-
# Write value to single output pin
130100
def output(self, pin, value):
131-
assert 0 <= pin < self.num_gpios, "Pin number %s is invalid, must be between 0 and %s" % (pin, self.num_gpios-1)
132101
# assert self.direction & (1 << pin) == 0, "Pin %s not set to output" % pin
133-
134-
if value: new = self.outputvalue | (1 << pin)
135-
else: new = self.outputvalue & ~(1 << pin)
136-
137-
# Only write if pin value has changed:
138-
if new is not self.outputvalue:
139-
self.outputvalue = new
140-
if self.num_gpios <= 8:
141-
self.i2c.write8(self.MCP23008_OLAT, new)
142-
elif pin < 8:
143-
# Write to low bits (OLATA)
144-
self.i2c.write8(self.MCP23017_OLATA, new & 0xFF)
102+
if self.num_gpios <= 8:
103+
self.outputvalue = self._readandchangepin(MCP23008_GPIOA, pin, value, self.i2c.readU8(MCP23008_OLATA))
104+
if self.num_gpios <= 16:
105+
if (pin < 8):
106+
self.outputvalue = self._readandchangepin(MCP23017_GPIOA, pin, value, self.i2c.readU8(MCP23017_OLATA))
145107
else:
146-
# Write to high bits (OLATB)
147-
self.i2c.write8(self.MCP23017_OLATB, new >> 8)
148-
149-
return new
150-
108+
self.outputvalue = self._readandchangepin(MCP23017_GPIOB, pin-8, value, self.i2c.readU8(MCP23017_OLATB)) << 8
151109

152-
# The following two methods (inputAll and outputAll) neither assert
153-
# inputs nor invoke the base class methods that handle I/O exceptions.
154-
# The underlying smbus calls are invoked directly for expediency, the
155-
# expectation being that any I2C access or address type errors have
156-
# already been identified during initialization.
110+
return self.outputvalue
157111

158-
# Read contiguous value from all input pins
159-
def inputAll(self):
160-
if self.num_gpios <= 8:
161-
return self.i2c.bus.read_byte_data(self.i2c.address,
162-
self.MCP23008_GPIO)
163-
else:
164-
return self.i2c.bus.read_word_data(self.i2c.address,
165-
self.MCP23017_GPIOA)
166-
167-
168-
# Write contiguous value to all output pins
169-
def outputAll(self, value):
170-
self.outputvalue = value
171-
if self.num_gpios <= 8:
172-
self.i2c.bus.write_byte_data(self.i2c.address,
173-
self.MCP23008_OLAT, value)
174-
else:
175-
self.i2c.bus.write_word_data(self.i2c.address,
176-
self.MCP23017_OLATA, value)
177112

113+
self.outputvalue = self._readandchangepin(MCP23017_IODIRA, pin, value, self.outputvalue)
114+
return self.outputvalue
115+
116+
def input(self, pin):
117+
assert pin >= 0 and pin < self.num_gpios, "Pin number %s is invalid, only 0-%s are valid" % (pin, self.num_gpios)
118+
assert self.direction & (1 << pin) != 0, "Pin %s not set to input" % pin
119+
if self.num_gpios <= 8:
120+
value = self.i2c.readU8(MCP23008_GPIOA)
121+
elif self.num_gpios > 8 and self.num_gpios <= 16:
122+
value = self.i2c.readU8(MCP23017_GPIOA)
123+
value |= self.i2c.readU8(MCP23017_GPIOB) << 8
124+
return value & (1 << pin)
125+
126+
def readU8(self):
127+
result = self.i2c.readU8(MCP23008_OLATA)
128+
return(result)
129+
130+
def readS8(self):
131+
result = self.i2c.readU8(MCP23008_OLATA)
132+
if (result > 127): result -= 256
133+
return result
134+
135+
def readU16(self):
136+
assert self.num_gpios >= 16, "16bits required"
137+
lo = self.i2c.readU8(MCP23017_OLATA)
138+
hi = self.i2c.readU8(MCP23017_OLATB)
139+
return((hi << 8) | lo)
140+
141+
def readS16(self):
142+
assert self.num_gpios >= 16, "16bits required"
143+
lo = self.i2c.readU8(MCP23017_OLATA)
144+
hi = self.i2c.readU8(MCP23017_OLATB)
145+
if (hi > 127): hi -= 256
146+
return((hi << 8) | lo)
147+
148+
def write8(self, value):
149+
self.i2c.write8(MCP23008_OLATA, value)
150+
151+
def write16(self, value):
152+
assert self.num_gpios >= 16, "16bits required"
153+
self.i2c.write8(MCP23017_OLATA, value & 0xFF)
154+
self.i2c.write8(MCP23017_OLATB, (value >> 8) & 0xFF)
178155

179156
# RPi.GPIO compatible interface for MCP23017 and MCP23008
180157

181158
class MCP230XX_GPIO(object):
182-
OUT = 0
183-
IN = 1
184-
BCM = 0
159+
OUT = 0
160+
IN = 1
161+
BCM = 0
185162
BOARD = 0
186-
187163
def __init__(self, busnum, address, num_gpios):
188164
self.chip = Adafruit_MCP230XX(busnum, address, num_gpios)
189165
def setmode(self, mode):
190-
pass # do nothing
166+
# do nothing
167+
pass
191168
def setup(self, pin, mode):
192169
self.chip.config(pin, mode)
193170
def input(self, pin):
@@ -196,28 +173,31 @@ def output(self, pin, value):
196173
self.chip.output(pin, value)
197174
def pullup(self, pin, value):
198175
self.chip.pullup(pin, value)
199-
176+
200177

201178
if __name__ == '__main__':
202-
203-
# ****************************************************
179+
# ***************************************************
204180
# Set num_gpios to 8 for MCP23008 or 16 for MCP23017!
205-
# If you have a new Pi you may also need to add: bus=1
206-
# ****************************************************
207-
mcp = Adafruit_MCP230XX(address=0x20, num_gpios=16)
208-
209-
# Set pins 0, 1, 2 as outputs
181+
# ***************************************************
182+
mcp = Adafruit_MCP230XX(address = 0x20, num_gpios = 8) # MCP23008
183+
# mcp = Adafruit_MCP230XX(address = 0x20, num_gpios = 16) # MCP23017
184+
185+
# Set pins 0, 1 and 2 to output (you can set pins 0..15 this way)
210186
mcp.config(0, mcp.OUTPUT)
211187
mcp.config(1, mcp.OUTPUT)
212188
mcp.config(2, mcp.OUTPUT)
213-
189+
214190
# Set pin 3 to input with the pullup resistor enabled
215-
mcp.pullup(3, True)
216-
217-
# Read pin 3 and display the results
218-
print "%d: %x" % (3, mcp.input(3))
191+
mcp.config(3, mcp.INPUT)
192+
mcp.pullup(3, 1)
219193

194+
# Read input pin and display the results
195+
print "Pin 3 = %d" % (mcp.input(3) >> 3)
196+
220197
# Python speed test on output 0 toggling at max speed
221-
while True:
222-
mcp.output(0, 1) # Pin 0 High
223-
mcp.output(0, 0) # Pin 0 Low
198+
print "Starting blinky on pin 0 (CTRL+C to quit)"
199+
while (True):
200+
mcp.output(0, 1) # Pin 0 High
201+
time.sleep(1);
202+
mcp.output(0, 0) # Pin 0 Low
203+
time.sleep(1);

0 commit comments

Comments
 (0)