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
2423from 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
181158class 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
201178if __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