1
1
#!/usr/bin/python
2
2
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.
15
14
16
15
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
16
# 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.
23
22
24
23
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
50
47
self .num_gpios = num_gpios
51
- self .pullups = 0
52
48
53
- # Set default pin values -- all inputs with pull-ups disabled.
54
- # Current OLAT (output) value is polled, not set.
49
+ # set defaults
55
50
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
71
77
72
- if mode is self .INPUT : self .direction |= (1 << pin )
73
- else : self .direction &= ~ (1 << pin )
74
78
79
+ def pullup (self , pin , value ):
75
80
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
96
87
88
+ # Set pin to either input or output mode
89
+ def config (self , pin , mode ):
97
90
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
111
97
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
115
99
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
130
100
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 )
132
101
# 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 ))
145
107
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
151
109
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
157
111
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 )
177
112
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 )
178
155
179
156
# RPi.GPIO compatible interface for MCP23017 and MCP23008
180
157
181
158
class MCP230XX_GPIO (object ):
182
- OUT = 0
183
- IN = 1
184
- BCM = 0
159
+ OUT = 0
160
+ IN = 1
161
+ BCM = 0
185
162
BOARD = 0
186
-
187
163
def __init__ (self , busnum , address , num_gpios ):
188
164
self .chip = Adafruit_MCP230XX (busnum , address , num_gpios )
189
165
def setmode (self , mode ):
190
- pass # do nothing
166
+ # do nothing
167
+ pass
191
168
def setup (self , pin , mode ):
192
169
self .chip .config (pin , mode )
193
170
def input (self , pin ):
@@ -196,28 +173,31 @@ def output(self, pin, value):
196
173
self .chip .output (pin , value )
197
174
def pullup (self , pin , value ):
198
175
self .chip .pullup (pin , value )
199
-
176
+
200
177
201
178
if __name__ == '__main__' :
202
-
203
- # ****************************************************
179
+ # ***************************************************
204
180
# 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)
210
186
mcp .config (0 , mcp .OUTPUT )
211
187
mcp .config (1 , mcp .OUTPUT )
212
188
mcp .config (2 , mcp .OUTPUT )
213
-
189
+
214
190
# 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 )
219
193
194
+ # Read input pin and display the results
195
+ print "Pin 3 = %d" % (mcp .input (3 ) >> 3 )
196
+
220
197
# 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