i2c 0.2.22 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +7 -4
- data/lib/i2c.rb +2 -2
- data/lib/i2c/backends/i2c-dev.rb +10 -1
- data/lib/i2c/drivers/mcp230xx.rb +253 -0
- data/test/mcp230xx_spec.rb +239 -0
- metadata +5 -7
- data/lib/i2c/drivers/mcp23008.rb +0 -98
- data/lib/i2c/drivers/mcp23017.rb +0 -124
- data/test/mcp23008_spec.rb +0 -86
- data/test/mcp23017_spec.rb +0 -88
data/README.rdoc
CHANGED
@@ -8,9 +8,9 @@ Raspberry Pi, but should work with any linux i2c-dev I2C-hardware.
|
|
8
8
|
== Structure
|
9
9
|
|
10
10
|
The library is split into two parts:
|
11
|
-
|
11
|
+
* A backend for accessing the I2C bus (right now only through the i2c-dev
|
12
12
|
in Linux, other impementations are possible).
|
13
|
-
|
13
|
+
* Drivers for I2C enabled ICs.
|
14
14
|
|
15
15
|
== Installation
|
16
16
|
|
@@ -57,8 +57,11 @@ Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21919e.pdf
|
|
57
57
|
The low-level IO (mainly in i2c-dev.rb) was extracted from Ruby-I2C
|
58
58
|
(http://rubyforge.org/projects/i2c/) by Jonas Bähr <jonas.baehr@fs.ei.tum.de>
|
59
59
|
|
60
|
-
|
61
|
-
|
60
|
+
=== Contributions
|
61
|
+
* [Gaëtan Duchaussois](https://github.com/gaetronik) I2C::Dev.read_byte.
|
62
|
+
|
63
|
+
=== Bugfixes
|
64
|
+
* Pierre Paques
|
62
65
|
|
63
66
|
== Copyright / Licence
|
64
67
|
|
data/lib/i2c.rb
CHANGED
data/lib/i2c/backends/i2c-dev.rb
CHANGED
@@ -43,7 +43,16 @@ module I2C
|
|
43
43
|
ret = @device.sysread(size)
|
44
44
|
return ret
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
|
+
# Read a byte from the current address. Return a one char String which
|
48
|
+
# can be treated with String#unpack
|
49
|
+
def read_byte(address)
|
50
|
+
ret=""
|
51
|
+
@device.ioctl(I2C_SLAVE,address)
|
52
|
+
ret=@device.sysread(1)
|
53
|
+
return ret
|
54
|
+
end
|
55
|
+
|
47
56
|
private
|
48
57
|
def initialize(device_path)
|
49
58
|
@device = File.new(device_path, 'r+')
|
@@ -0,0 +1,253 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# I2C IO-Expander drivers.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2012 Christoph Anderegg <christoph@christoph-anderegg.ch>
|
5
|
+
# This file may be distributed under the terms of the GNU General Public
|
6
|
+
# License Version 2.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'i2c/i2c.rb'
|
10
|
+
|
11
|
+
# Constants for mode()
|
12
|
+
INPUT = 1 unless defined? INPUT
|
13
|
+
OUTPUT = 0 unless defined? OUTPUT
|
14
|
+
|
15
|
+
# Constants for write()
|
16
|
+
HIGH = 1 unless defined? HIGH
|
17
|
+
LOW = 0 unless defined? LOW
|
18
|
+
|
19
|
+
module I2C
|
20
|
+
module Drivers
|
21
|
+
# Driver class for the Microchip MPC230xx IO-Expanders.
|
22
|
+
#
|
23
|
+
# The interface is mostly compatible to the interface
|
24
|
+
# of the WiringPi gem. PWM is not supported though.
|
25
|
+
# On the other hand a more rubyesque interface is also
|
26
|
+
# provided.
|
27
|
+
module MCP230xx
|
28
|
+
# Creates an instance representing exactly one
|
29
|
+
# MCP230xx on one I2C-bus.
|
30
|
+
#
|
31
|
+
# @todo This implementation currently assumes
|
32
|
+
# that all registers of the same type are
|
33
|
+
# on a continuos address range. Implement
|
34
|
+
# a (less efficient) case for other
|
35
|
+
# situations.
|
36
|
+
#
|
37
|
+
# @param device [IO, String] I2C-device file
|
38
|
+
# (usually /dev/i2c-0). Or an intantiated
|
39
|
+
# io class that supports the necessary
|
40
|
+
# operations (#read, #write and #ioctl).
|
41
|
+
# @param address [Integer] Device address on the bus.
|
42
|
+
def initialize(device, address)
|
43
|
+
if device.kind_of?(String)
|
44
|
+
@device = ::I2C.create(device)
|
45
|
+
else
|
46
|
+
[ :read, :write ].each do |m|
|
47
|
+
raise IncompatibleDeviceException,
|
48
|
+
"Missing #{m} method in device object." unless device.respond_to?(m)
|
49
|
+
end
|
50
|
+
@device = device
|
51
|
+
end
|
52
|
+
@address = address
|
53
|
+
|
54
|
+
@iodir = Array.new
|
55
|
+
max_port_no.times { @iodir << 0xFF } # Direction is input initially
|
56
|
+
@device.write(@address, port_count, iodir[0], @iodir)
|
57
|
+
|
58
|
+
@data = Array.new
|
59
|
+
max_port_no.times { @data << 0xFF }
|
60
|
+
@unpack_string = "C" * port_count
|
61
|
+
@data = @device.read(@address, port_count, gpio[0]).unpack(@unpack_string)
|
62
|
+
@dir = @device.read(@address, port_count, iodir[0]).unpack(@unpack_string)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Reads the mode of a IO-pin.
|
66
|
+
# @param pin [Integer] Pin number to check.
|
67
|
+
# @return [Integer] Pin mode. Either INPUT or OUTPUT.
|
68
|
+
def mode?(pin)
|
69
|
+
check_pin(pin) # Raises if the pin is not valid
|
70
|
+
@dir = @device.read(@address, port_count, iodir[0]).unpack(@unpack_string)
|
71
|
+
index = port_for_pin(pin)
|
72
|
+
|
73
|
+
(@dir[index[0]] >> index[1]) & 0x01
|
74
|
+
end
|
75
|
+
|
76
|
+
# Sets the mode of a IO-pin.
|
77
|
+
# @param pin [Integer] Pin number to set.
|
78
|
+
# @param pin_mode [Integer] Pin mode. Either INPUT or OUTPUT.
|
79
|
+
def mode(pin, pin_mode)
|
80
|
+
check_pin(pin) # Raises if the pin is not valid
|
81
|
+
raise ArgumentError, 'invalid value' unless [0,1].include?(pin_mode)
|
82
|
+
index = port_for_pin(pin)
|
83
|
+
|
84
|
+
@dir[index[0]] = set_bit_value(@dir[index[0]], index[1], pin_mode)
|
85
|
+
@device.write(@address, iodir[0], *@dir)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Sets a IO-pin value.
|
89
|
+
# @param pin [Integer] Pin number to set.
|
90
|
+
# @param value [Integer] Pin value. Either HIGH or LOW.
|
91
|
+
def []=(pin, value)
|
92
|
+
check_pin(pin) # Raises if the pin is not valid
|
93
|
+
raise ArgumentError, 'invalid value' unless [0,1].include?(value)
|
94
|
+
index = port_for_pin(pin)
|
95
|
+
@data[index[0]] = set_bit_value(@data[index[0]], index[1], value)
|
96
|
+
@device.write(@address, gpio[0], *@data)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Alias for a WiringPi compatible naming.
|
100
|
+
alias :write :[]=
|
101
|
+
|
102
|
+
# Reads a IO-pin value.
|
103
|
+
# @param pin [Integer] Pin number to set.
|
104
|
+
# @return [Integer] Pin value. Either HIGH or LOW.
|
105
|
+
def [](pin)
|
106
|
+
check_pin(pin) # Raises if the pin is not valid
|
107
|
+
index = port_for_pin(pin)
|
108
|
+
@data = @device.read(@address, port_count, gpio[0]).unpack(@unpack_string)
|
109
|
+
index = port_for_pin(pin)
|
110
|
+
|
111
|
+
(@data[index[0]] >> index[1]) & 0x01
|
112
|
+
end
|
113
|
+
|
114
|
+
# Alias for a WiringPi compatible naming.
|
115
|
+
alias_method :read, :[]
|
116
|
+
|
117
|
+
# private
|
118
|
+
# Checks a pin number for validity.
|
119
|
+
# Raises an exception if not valid. Returns nil otherwise.
|
120
|
+
# @param pin [Integer] IO pin number.
|
121
|
+
# @return nil Raises an exception in all other cases.
|
122
|
+
def check_pin(pin)
|
123
|
+
raise ArgumentError, "Pin not 0-#{max_pin_no}" unless (0..max_pin_no).include?(pin)
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
|
127
|
+
# Checks a port number for validity.
|
128
|
+
# Raises an exception if not valid. Returns nil otherwise.
|
129
|
+
# @param no [Integer] IO port number.
|
130
|
+
# @return nil Raises an exception in all other cases.
|
131
|
+
def check_port(no)
|
132
|
+
raise ArgumentError, "Only Ports 0-#{max_port_no} available." unless (0..max_port_no).include?(no)
|
133
|
+
nil
|
134
|
+
end
|
135
|
+
|
136
|
+
# Returns a port no, index in port pair for a pin number.
|
137
|
+
#
|
138
|
+
# E.g. Pin 14 is the is bit 7 of port 1. So the method returns [1,7].
|
139
|
+
#
|
140
|
+
# @param pin [Integer] Pin number, begining at 0.
|
141
|
+
# @return [Array<Integer, Integer>] Port number and index in the port
|
142
|
+
# of the passed continuos pin number.
|
143
|
+
def port_for_pin(pin)
|
144
|
+
check_pin(pin)
|
145
|
+
[pin / 8, pin % 8]
|
146
|
+
end
|
147
|
+
|
148
|
+
# Sets a bit in a byte to a defied state not touching the other bits.
|
149
|
+
#
|
150
|
+
# @param byte [Integer] Byte to manipulate (LSB).
|
151
|
+
# @param bit [Integer] Bit number to manipulate.
|
152
|
+
# @param value [Integer] 1 or 0; the new value of the bit.
|
153
|
+
# @return [Integer] The new byte
|
154
|
+
def set_bit_value(byte, bit, value)
|
155
|
+
[byte, bit, value].each do |p|
|
156
|
+
raise ArgumentError, "Arguments must be Integer" unless p.kind_of? Integer
|
157
|
+
end
|
158
|
+
raise ArgumentError, "Only bits 0..7 are available." unless bit < 8
|
159
|
+
raise ArgumentError, "Value needs to be 0 or 1." unless (value == 0) or (value == 1)
|
160
|
+
mask = 0x00
|
161
|
+
mask = (0x01 << bit)
|
162
|
+
case value
|
163
|
+
when 0
|
164
|
+
byte = (byte & ((~mask) & 0xFF)) & 0xFF
|
165
|
+
when 1
|
166
|
+
byte = (byte | mask) & 0xFF
|
167
|
+
else
|
168
|
+
raise ArgumentError, "Bit not 0-7."
|
169
|
+
end
|
170
|
+
byte
|
171
|
+
end
|
172
|
+
|
173
|
+
# @!method iodir(no)
|
174
|
+
# Returns the address of the IO direction register
|
175
|
+
# for an IO port.
|
176
|
+
#
|
177
|
+
# @param no [Integer] Port number, begining at 0.
|
178
|
+
# @return Address of the IO direction register
|
179
|
+
# corresponding to passed IO port number.
|
180
|
+
|
181
|
+
# @!method gpio(no)
|
182
|
+
# Returns the address of the GPIO register
|
183
|
+
# for an IO port.
|
184
|
+
#
|
185
|
+
# @param no [Integer] Port number, begining at 0.
|
186
|
+
# @return [Integer] Address of the GPIO register
|
187
|
+
# corresponding to passed IO port number.
|
188
|
+
|
189
|
+
# @!method pin_count
|
190
|
+
# Returns the number of pins in the io expander.
|
191
|
+
#
|
192
|
+
# @return [Integer] Number of pins.
|
193
|
+
|
194
|
+
# @!method max_pin_no
|
195
|
+
# Returns the highest pin index. Usually #pin_count - 1.
|
196
|
+
#
|
197
|
+
# @return [Integer] Highest pin index.
|
198
|
+
|
199
|
+
# @!method port_count
|
200
|
+
# Returns the number of ports in the io expander.
|
201
|
+
#
|
202
|
+
# @return [Integer] Number of ports.
|
203
|
+
|
204
|
+
# @!method max_port_no
|
205
|
+
# Returns the highest port index. Usually #port_count - 1.
|
206
|
+
#
|
207
|
+
# @return [Integer] Highest port index.
|
208
|
+
end
|
209
|
+
|
210
|
+
# Defines a class for a chip implementation.
|
211
|
+
#
|
212
|
+
# @param name Class name
|
213
|
+
def self.define_mcp230xx_chip(name, parameters)
|
214
|
+
raise ArgumentError, "Expecting options hash." unless parameters.kind_of? Hash
|
215
|
+
[ [ :pin_count, Integer ],
|
216
|
+
[ :port_count, Integer ],
|
217
|
+
[ :iodir, Array ],
|
218
|
+
[ :gpio, Array ] ].each do |expected_key|
|
219
|
+
raise ArgumentError, "Missing option #{expected_key[0]}" unless
|
220
|
+
parameters.has_key? expected_key[0]
|
221
|
+
raise ArgumentError, "Option #{expected_key[0]} expected to be a #{expected_key[1]}" unless
|
222
|
+
parameters[expected_key[0]].kind_of? expected_key[1]
|
223
|
+
end
|
224
|
+
chip_class = self.const_set(name.to_sym, Class.new)
|
225
|
+
chip_class.instance_eval do
|
226
|
+
include MCP230xx
|
227
|
+
parameters.each do |method_name, return_value|
|
228
|
+
#puts "Defining #{name}##{method_name.to_sym}"
|
229
|
+
define_method method_name.to_sym do
|
230
|
+
return_value
|
231
|
+
end
|
232
|
+
end
|
233
|
+
define_method :max_pin_no do
|
234
|
+
parameters[:pin_count] - 1
|
235
|
+
end
|
236
|
+
define_method :max_port_no do
|
237
|
+
parameters[:port_count] - 1
|
238
|
+
end
|
239
|
+
end
|
240
|
+
chip_class
|
241
|
+
end
|
242
|
+
define_mcp230xx_chip :MCP23008,
|
243
|
+
:pin_count => 8,
|
244
|
+
:port_count => 1,
|
245
|
+
:iodir => [ 0x00 ],
|
246
|
+
:gpio => [ 0x09 ]
|
247
|
+
define_mcp230xx_chip :MCP23017,
|
248
|
+
:pin_count => 16,
|
249
|
+
:port_count => 2,
|
250
|
+
:iodir => [ 0x00, 0x01 ],
|
251
|
+
:gpio => [ 0x12, 0x13 ]
|
252
|
+
end
|
253
|
+
end
|
@@ -0,0 +1,239 @@
|
|
1
|
+
require 'i2c'
|
2
|
+
|
3
|
+
class MockI2CIO
|
4
|
+
attr_reader :registers
|
5
|
+
attr_reader :last_address
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@registers = Hash.new
|
9
|
+
# Initialize according to data sheet
|
10
|
+
(0x00..0x01).each do |reg|
|
11
|
+
@registers[reg] = 0xFF
|
12
|
+
end
|
13
|
+
(0x02..0x13).each do |reg|
|
14
|
+
@registers[reg] = 0x00
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def write(address, *params)
|
19
|
+
@last_address = address
|
20
|
+
if params.count >= 1
|
21
|
+
reg_addr = params.shift
|
22
|
+
index = 0
|
23
|
+
params.each do |p|
|
24
|
+
@registers[reg_addr+index] = p
|
25
|
+
index += 1
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def read(address, size, *params)
|
31
|
+
@last_address = address
|
32
|
+
answer = String.new
|
33
|
+
answer.force_encoding("US-ASCII")
|
34
|
+
if (size > 0) && (params.count >= 1)
|
35
|
+
reg_addr = params.shift
|
36
|
+
size.times do |index|
|
37
|
+
answer << (@registers[reg_addr+index] & 0xFF)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
answer
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe I2C::Drivers::MCP23017, "#port_count" do
|
45
|
+
it "returns the number of ports." do
|
46
|
+
io = MockI2CIO.new
|
47
|
+
mcp = I2C::Drivers::MCP23017.new(io, 0x20)
|
48
|
+
mcp.port_count.should eq(2)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe I2C::Drivers::MCP23017, "#max_port_no" do
|
53
|
+
it "returns the highest port number." do
|
54
|
+
io = MockI2CIO.new
|
55
|
+
mcp = I2C::Drivers::MCP23017.new(io, 0x20)
|
56
|
+
mcp.max_port_no.should eq(1)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe I2C::Drivers::MCP23017, "#pin_count" do
|
61
|
+
it "returns the number of pins." do
|
62
|
+
io = MockI2CIO.new
|
63
|
+
mcp = I2C::Drivers::MCP23017.new(io, 0x20)
|
64
|
+
mcp.pin_count.should eq(16)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe I2C::Drivers::MCP23017, "#max_pin_no" do
|
69
|
+
it "returns the highest pin number." do
|
70
|
+
io = MockI2CIO.new
|
71
|
+
mcp = I2C::Drivers::MCP23017.new(io, 0x20)
|
72
|
+
mcp.max_pin_no.should eq(15)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe I2C::Drivers::MCP23017, "#check_pin" do
|
77
|
+
it "raises an exception for out of range pin indices." do
|
78
|
+
io = MockI2CIO.new
|
79
|
+
mcp = I2C::Drivers::MCP23017.new(io, 0x20)
|
80
|
+
[-1000, -1, 16, 1000].each do |pin|
|
81
|
+
expect { mcp.check_pin(pin) }.to raise_error
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it "returns nil for valid indices." do
|
86
|
+
io = MockI2CIO.new
|
87
|
+
mcp = I2C::Drivers::MCP23017.new(io, 0x20)
|
88
|
+
16.times do |pin|
|
89
|
+
mcp.check_pin(pin).should eq nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe I2C::Drivers::MCP23017, "#check_port" do
|
95
|
+
it "raises an exception for out of range port indices." do
|
96
|
+
io = MockI2CIO.new
|
97
|
+
mcp = I2C::Drivers::MCP23017.new(io, 0x20)
|
98
|
+
[-1000, -1, 2, 1000].each do |port|
|
99
|
+
expect { mcp.check_port(port) }.to raise_error
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it "returns nil for valid indices." do
|
104
|
+
io = MockI2CIO.new
|
105
|
+
mcp = I2C::Drivers::MCP23017.new(io, 0x20)
|
106
|
+
2.times do |port|
|
107
|
+
mcp.check_port(port).should eq nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe I2C::Drivers::MCP23017, "#port_for_pin" do
|
113
|
+
it "returns correctly split pin and port numbers for 16bit IO expanders." do
|
114
|
+
io = MockI2CIO.new
|
115
|
+
mcp = I2C::Drivers::MCP23017.new(io, 0x20)
|
116
|
+
expected =
|
117
|
+
[[0, 0],
|
118
|
+
[0, 1],
|
119
|
+
[0, 2],
|
120
|
+
[0, 3],
|
121
|
+
[0, 4],
|
122
|
+
[0, 5],
|
123
|
+
[0, 6],
|
124
|
+
[0, 7],
|
125
|
+
[1, 0],
|
126
|
+
[1, 1],
|
127
|
+
[1, 2],
|
128
|
+
[1, 3],
|
129
|
+
[1, 4],
|
130
|
+
[1, 5],
|
131
|
+
[1, 6],
|
132
|
+
[1, 7]]
|
133
|
+
expected.each_index do |pin|
|
134
|
+
mcp.port_for_pin(pin).should eq(expected[pin])
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
it "raises an exception for out of range arguments." do
|
139
|
+
io = MockI2CIO.new
|
140
|
+
mcp = I2C::Drivers::MCP23017.new(io, 0x20)
|
141
|
+
[1000, 16, -1, -1000].each do |pin|
|
142
|
+
expect { mcp.port_for_pin(pin) }.to raise_error
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe I2C::Drivers::MCP23017, "#set_bit_value" do
|
148
|
+
it "raises on non-integer arguments for the first argument" do
|
149
|
+
io = MockI2CIO.new
|
150
|
+
mcp = I2C::Drivers::MCP23017.new(io, 0x20)
|
151
|
+
expect { mcp.set_bit_value("Hello", 0, 0) }.to raise_error
|
152
|
+
end
|
153
|
+
it "raises on non-integer arguments for the second argument" do
|
154
|
+
io = MockI2CIO.new
|
155
|
+
mcp = I2C::Drivers::MCP23017.new(io, 0x20)
|
156
|
+
expect { mcp.set_bit_value(0, "Hello", 0) }.to raise_error
|
157
|
+
end
|
158
|
+
it "raises on arguments other than 0 and 1 for the third argument" do
|
159
|
+
io = MockI2CIO.new
|
160
|
+
mcp = I2C::Drivers::MCP23017.new(io, 0x20)
|
161
|
+
[1000, 2, -1, -1000].each do |value|
|
162
|
+
expect { mcp.set_bit_value(0, 0, value) }.to raise_error
|
163
|
+
end
|
164
|
+
end
|
165
|
+
it "sets the correct bits in a byte" do
|
166
|
+
io = MockI2CIO.new
|
167
|
+
mcp = I2C::Drivers::MCP23017.new(io, 0x20)
|
168
|
+
# Original, bit no, new value, result
|
169
|
+
[
|
170
|
+
[ 0b11111111, 0, 0, 0b11111110 ],
|
171
|
+
[ 0b00000000, 0, 1, 0b00000001 ],
|
172
|
+
[ 0b11111111, 7, 0, 0b01111111 ],
|
173
|
+
[ 0b00000000, 7, 1, 0b10000000 ],
|
174
|
+
[ 0b11111111, 0, 1, 0b11111111 ],
|
175
|
+
[ 0b00000000, 0, 0, 0b00000000 ]
|
176
|
+
].each do |test|
|
177
|
+
mcp.set_bit_value(test[0], test[1], test[2]).should eq test[3]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
describe I2C::Drivers::MCP23017, "#mode" do
|
183
|
+
it "raises an exception for out of range pin indices." do
|
184
|
+
io = MockI2CIO.new
|
185
|
+
mcp = I2C::Drivers::MCP23017.new(io, 0x20)
|
186
|
+
[-1000, -1, 16, 1000].each do |pin|
|
187
|
+
expect { mcp.mode(pin, HIGH) }.to raise_error
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe I2C::Drivers::MCP23017, "#mode?" do
|
193
|
+
it "raises an exception for out of range pin indices." do
|
194
|
+
io = MockI2CIO.new
|
195
|
+
mcp = I2C::Drivers::MCP23017.new(io, 0x20)
|
196
|
+
[-1000, -1, 16, 1000].each do |pin|
|
197
|
+
expect { mcp.mode?(pin) }.to raise_error
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
it "initially returns 1 for all pin modes" do
|
202
|
+
io = MockI2CIO.new
|
203
|
+
mcp23017 = I2C::Drivers::MCP23017.new(io, 0x20)
|
204
|
+
(0..15).each do |pin|
|
205
|
+
mcp23017.mode?(pin).should eq(1)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
it "returns what has been set through #mode" do
|
210
|
+
io = MockI2CIO.new
|
211
|
+
mcp23017 = I2C::Drivers::MCP23017.new(io, 0x20)
|
212
|
+
(0..500).each do |pin|
|
213
|
+
pin = rand(16)
|
214
|
+
mode = rand(2)
|
215
|
+
mcp23017.mode(pin, mode)
|
216
|
+
mcp23017.mode?(pin).should eq(mode)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
describe I2C::Drivers::MCP23017, "#[]" do
|
222
|
+
it "initially returns 0 for all I/O pins" do
|
223
|
+
io = MockI2CIO.new
|
224
|
+
mcp23017 = I2C::Drivers::MCP23017.new(io, 0x20)
|
225
|
+
(0..15).each do |pin|
|
226
|
+
mcp23017[pin].should eq(0)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
it "returns what has been set through #[]=" do
|
230
|
+
io = MockI2CIO.new
|
231
|
+
mcp23017 = I2C::Drivers::MCP23017.new(io, 0x20)
|
232
|
+
(0..500).each do |pin|
|
233
|
+
pin = rand(16)
|
234
|
+
value = rand(2)
|
235
|
+
mcp23017[pin] = value
|
236
|
+
mcp23017[pin].should eq(value)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: i2c
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-15 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Interface to I2C (aka TWI) implementations. Also provides abstractions
|
15
15
|
for some I2c-devices. Created with the Raspberry Pi in mind.
|
@@ -22,10 +22,8 @@ files:
|
|
22
22
|
- lib/i2c.rb
|
23
23
|
- lib/i2c/i2c.rb
|
24
24
|
- lib/i2c/backends/i2c-dev.rb
|
25
|
-
- lib/i2c/drivers/
|
26
|
-
-
|
27
|
-
- test//mcp23017_spec.rb
|
28
|
-
- test//mcp23008_spec.rb
|
25
|
+
- lib/i2c/drivers/mcp230xx.rb
|
26
|
+
- test//mcp230xx_spec.rb
|
29
27
|
- rules/88-i2c.rules
|
30
28
|
- README.rdoc
|
31
29
|
homepage: https://github.com/andec/i2c
|
@@ -48,7 +46,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
48
46
|
version: '0'
|
49
47
|
requirements: []
|
50
48
|
rubyforge_project:
|
51
|
-
rubygems_version: 1.8.
|
49
|
+
rubygems_version: 1.8.23
|
52
50
|
signing_key:
|
53
51
|
specification_version: 3
|
54
52
|
summary: I2C access library (for Linux).
|
data/lib/i2c/drivers/mcp23008.rb
DELETED
@@ -1,98 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
# I2C IO-Expander driver
|
3
|
-
# for the MCP23008 8-bit IO-Expander.
|
4
|
-
#
|
5
|
-
# The interface is compatible to the interface
|
6
|
-
# of the WiringPi gem. PWM is not supported though.
|
7
|
-
#
|
8
|
-
# Copyright (c) 2012 Christoph Anderegg <christoph@christoph-anderegg.ch>
|
9
|
-
# This file may be distributed under the terms of the GNU General Public
|
10
|
-
# License Version 2.
|
11
|
-
#
|
12
|
-
|
13
|
-
require 'i2c/i2c.rb'
|
14
|
-
|
15
|
-
# Constants for mode()
|
16
|
-
INPUT = 1 unless defined?(INPUT)
|
17
|
-
OUTPUT = 0 unless defined?(OUTPUT)
|
18
|
-
|
19
|
-
# Constants for write()
|
20
|
-
HIGH = 1 unless defined?(HIGH)
|
21
|
-
LOW = 0 unless defined?(LOW)
|
22
|
-
|
23
|
-
module I2C
|
24
|
-
module Drivers
|
25
|
-
class MCP23008
|
26
|
-
# Registers
|
27
|
-
IODIR = 0x00
|
28
|
-
GPIO = 0x09
|
29
|
-
|
30
|
-
# Creates an instance representing exactly one
|
31
|
-
# MCP23008 on one I2C-bus.
|
32
|
-
#
|
33
|
-
# device: I2C-device file (usually /dev/i2c-0).
|
34
|
-
# Or an intantiated io class that supports
|
35
|
-
# the necessary operations (#read, #write
|
36
|
-
# and #ioctl).
|
37
|
-
# address: Device address on the bus.
|
38
|
-
def initialize(device, address)
|
39
|
-
if device.kind_of?(String)
|
40
|
-
@device = ::I2C.create(device)
|
41
|
-
else
|
42
|
-
[ :read, :write ].each do |m|
|
43
|
-
raise IncompatibleDeviceException,
|
44
|
-
"Missing #{m} method in device object." unless device.respond_to?(m)
|
45
|
-
end
|
46
|
-
@device = device
|
47
|
-
end
|
48
|
-
@address = address
|
49
|
-
|
50
|
-
@dir = 0xFF # Direction is input initially
|
51
|
-
@device.write(@address, IODIR, @dir)
|
52
|
-
@data = @device.read(@address, 1, GPIO).unpack("C")[0]
|
53
|
-
end
|
54
|
-
|
55
|
-
def mode?(pin)
|
56
|
-
@dir = @device.read(@address, 1, IODIR).unpack("C")[0]
|
57
|
-
return (@dir >> pin) & 0x01
|
58
|
-
end
|
59
|
-
|
60
|
-
def mode(pin, pin_mode)
|
61
|
-
raise ArgumentError, "Pin not 0-7" unless (0..7).include?(pin)
|
62
|
-
raise ArgumentError, 'invalid value' unless [0,1].include?(pin_mode)
|
63
|
-
@dir = set_bit_value(@dir, pin, pin_mode)
|
64
|
-
@device.write(@address, IODIR, @dir)
|
65
|
-
end
|
66
|
-
|
67
|
-
def []=(pin, value)
|
68
|
-
raise ArgumentError, "Pin not 0-7" unless (0..7).include?(pin)
|
69
|
-
raise ArgumentError, 'invalid value' unless [0,1].include?(value)
|
70
|
-
@data = set_bit_value(@data, pin, value)
|
71
|
-
@device.write(@address, GPIO, @data)
|
72
|
-
end
|
73
|
-
alias :write :[]=
|
74
|
-
|
75
|
-
def [](pin)
|
76
|
-
raise ArgumentError, "Pin not 0-7." unless (0..7).include?(pin)
|
77
|
-
@data = @device.read(@address, 1, GPIO).unpack("C")[0]
|
78
|
-
return (@data >> pin) & 0x01
|
79
|
-
end
|
80
|
-
alias :read :[]
|
81
|
-
|
82
|
-
private
|
83
|
-
def set_bit_value(byte, bit, value)
|
84
|
-
mask = 0x00
|
85
|
-
mask = (0x01 << bit)
|
86
|
-
case value
|
87
|
-
when 0
|
88
|
-
byte = (byte & ((~mask) & 0xFF)) & 0xFF
|
89
|
-
when 1
|
90
|
-
byte = (byte | mask) & 0xFF
|
91
|
-
else
|
92
|
-
raise ArgumentError, "Bit not 0-7."
|
93
|
-
end
|
94
|
-
byte
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
data/lib/i2c/drivers/mcp23017.rb
DELETED
@@ -1,124 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
# I2C IO-Expander driver
|
3
|
-
# for the MCP23017 16-bit IO-Expander.
|
4
|
-
#
|
5
|
-
# The interface is compatible to the interface
|
6
|
-
# of the WiringPi gem. PWM is not supported though.
|
7
|
-
#
|
8
|
-
# Copyright (c) 2012 Christoph Anderegg <christoph@christoph-anderegg.ch>
|
9
|
-
# This file may be distributed under the terms of the GNU General Public
|
10
|
-
# License Version 2.
|
11
|
-
#
|
12
|
-
|
13
|
-
require 'i2c/i2c.rb'
|
14
|
-
|
15
|
-
# Constants for mode()
|
16
|
-
INPUT = 1 unless defined?(INPUT)
|
17
|
-
OUTPUT = 0 unless defined?(OUTPUT)
|
18
|
-
|
19
|
-
# Constants for write()
|
20
|
-
HIGH = 1 unless defined?(HIGH)
|
21
|
-
LOW = 0 unless defined?(LOW)
|
22
|
-
|
23
|
-
module I2C
|
24
|
-
module Drivers
|
25
|
-
class MCP23017
|
26
|
-
# Registers
|
27
|
-
IODIRA = 0x00
|
28
|
-
IODIRB = 0x01
|
29
|
-
GPIOA = 0x12
|
30
|
-
GPIOB = 0x13
|
31
|
-
|
32
|
-
# Creates an instance representing exactly one
|
33
|
-
# MCP23017 on one I2C-bus.
|
34
|
-
#
|
35
|
-
# device: I2C-device file (usually /dev/i2c-0).
|
36
|
-
# Or an intantiated io class that supports
|
37
|
-
# the necessary operations (#read, #write
|
38
|
-
# and #ioctl).
|
39
|
-
# address: Device address on the bus.
|
40
|
-
def initialize(device, address)
|
41
|
-
if device.kind_of?(String)
|
42
|
-
@device = ::I2C.create(device)
|
43
|
-
else
|
44
|
-
[ :read, :write ].each do |m|
|
45
|
-
raise IncompatibleDeviceException,
|
46
|
-
"Missing #{m} method in device object." unless device.respond_to?(m)
|
47
|
-
end
|
48
|
-
@device = device
|
49
|
-
end
|
50
|
-
@address = address
|
51
|
-
|
52
|
-
@dir_a = 0xFF # Direction is input initially
|
53
|
-
@dir_b = 0xFF # Direction is input initially
|
54
|
-
@device.write(@address, IODIRA, @dir_a, @dir_b)
|
55
|
-
|
56
|
-
@data_a = 0xFF # Initial data
|
57
|
-
@data_b = 0xFF # Initial data
|
58
|
-
@data_a, @data_b = @device.read(@address, 2, GPIOA).unpack("CC")
|
59
|
-
end
|
60
|
-
|
61
|
-
def mode?(pin)
|
62
|
-
@dir_a, @dir_b = @device.read(@address, 2, IODIRA).unpack("CC")
|
63
|
-
dir = @dir_a
|
64
|
-
if 8 <= pin
|
65
|
-
dir = @dir_b
|
66
|
-
pin -= 8
|
67
|
-
end
|
68
|
-
return (dir >> pin) & 0x01
|
69
|
-
end
|
70
|
-
|
71
|
-
def mode(pin, pin_mode)
|
72
|
-
raise ArgumentError, "Pin not 0-15" unless (0..15).include?(pin)
|
73
|
-
raise ArgumentError, 'invalid value' unless [0,1].include?(pin_mode)
|
74
|
-
if 8 <= pin
|
75
|
-
@dir_b = set_bit_value(@dir_b, (pin-8), pin_mode)
|
76
|
-
else
|
77
|
-
@dir_a = set_bit_value(@dir_a, pin, pin_mode)
|
78
|
-
end
|
79
|
-
@device.write(@address, IODIRA, @dir_a, @dir_b)
|
80
|
-
end
|
81
|
-
|
82
|
-
def []=(pin, value)
|
83
|
-
raise ArgumentError, "Pin not 0-15" unless (0..15).include?(pin)
|
84
|
-
raise ArgumentError, 'invalid value' unless [0,1].include?(value)
|
85
|
-
if 8 <= pin
|
86
|
-
@data_b = set_bit_value(@data_b, (pin-8), value)
|
87
|
-
else
|
88
|
-
@data_a = set_bit_value(@data_a, pin, value)
|
89
|
-
end
|
90
|
-
@device.write(@address, GPIOA, @data_a, @data_b)
|
91
|
-
end
|
92
|
-
alias :write :[]=
|
93
|
-
|
94
|
-
def [](pin)
|
95
|
-
raise ArgumentError, "Pin not 0-15." unless (0..15).include?(pin)
|
96
|
-
@data_a, @data_b = @device.read(@address, 2, GPIOA).unpack("CC")
|
97
|
-
data = @data_a
|
98
|
-
if 8 <= pin
|
99
|
-
data = @data_b;
|
100
|
-
pin -= 8
|
101
|
-
end
|
102
|
-
return (data >> pin) & 0x01
|
103
|
-
end
|
104
|
-
alias :read :[]
|
105
|
-
|
106
|
-
private
|
107
|
-
def set_bit_value(byte, bit, value)
|
108
|
-
mask = 0x00
|
109
|
-
mask = (0x01 << bit)
|
110
|
-
case value
|
111
|
-
when 0
|
112
|
-
byte = (byte & ((~mask) & 0xFF)) & 0xFF
|
113
|
-
when 1
|
114
|
-
byte = (byte | mask) & 0xFF
|
115
|
-
else
|
116
|
-
raise ArgumentError, "Bit not 0-7."
|
117
|
-
end
|
118
|
-
# puts "Byte: (0x#{"%X" % byte}) 0b#{"%B" % byte}; " +
|
119
|
-
# "Mask: 0b#{"%B" % mask}; Bit: #{bit}; Value: #{value}"
|
120
|
-
byte
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
data/test/mcp23008_spec.rb
DELETED
@@ -1,86 +0,0 @@
|
|
1
|
-
require 'i2c'
|
2
|
-
|
3
|
-
class MockI2CIO
|
4
|
-
attr_reader :registers
|
5
|
-
attr_reader :last_address
|
6
|
-
|
7
|
-
def initialize
|
8
|
-
@registers = Hash.new
|
9
|
-
# Initialize according to data sheet
|
10
|
-
@registers[0x00] = 0xFF
|
11
|
-
(0x01..0x0A).each do |reg|
|
12
|
-
@registers[reg] = 0x00
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def write(address, *params)
|
17
|
-
@last_address = address
|
18
|
-
if params.count >= 1
|
19
|
-
reg_addr = params.shift
|
20
|
-
index = 0
|
21
|
-
params.each do |p|
|
22
|
-
@registers[reg_addr+index] = p
|
23
|
-
index += 1
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def read(address, size, *params)
|
29
|
-
@last_address = address
|
30
|
-
answer = String.new
|
31
|
-
answer.force_encoding("US-ASCII")
|
32
|
-
if (size > 0) && (params.count >= 1)
|
33
|
-
reg_addr = params.shift
|
34
|
-
(0...size).each do |index|
|
35
|
-
answer << (@registers[reg_addr+index] & 0xFF)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
answer
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
describe I2C::Drivers::MCP23008, "#mode?" do
|
43
|
-
it "initially returns 1 for all pin modes" do
|
44
|
-
io = MockI2CIO.new
|
45
|
-
mcp23008 = I2C::Drivers::MCP23008.new(io, 0x20)
|
46
|
-
(0..7).each do |pin|
|
47
|
-
mcp23008.mode?(pin).should eq(1)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
describe I2C::Drivers::MCP23008, "#mode?" do
|
53
|
-
it "returns what has been set through #mode" do
|
54
|
-
io = MockI2CIO.new
|
55
|
-
mcp23008 = I2C::Drivers::MCP23008.new(io, 0x20)
|
56
|
-
(0..500).each do |pin|
|
57
|
-
pin = rand(8)
|
58
|
-
mode = rand(2)
|
59
|
-
mcp23008.mode(pin, mode)
|
60
|
-
mcp23008.mode?(pin).should eq(mode)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
describe I2C::Drivers::MCP23008, "#[]" do
|
66
|
-
it "initially returns 0 for all I/O pins" do
|
67
|
-
io = MockI2CIO.new
|
68
|
-
mcp23008 = I2C::Drivers::MCP23008.new(io, 0x20)
|
69
|
-
(0..7).each do |pin|
|
70
|
-
mcp23008[pin].should eq(0)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
describe I2C::Drivers::MCP23008, "#[]" do
|
76
|
-
it "returns what has been set through #[]=" do
|
77
|
-
io = MockI2CIO.new
|
78
|
-
mcp23008 = I2C::Drivers::MCP23008.new(io, 0x20)
|
79
|
-
(0..500).each do |pin|
|
80
|
-
pin = rand(8)
|
81
|
-
value = rand(2)
|
82
|
-
mcp23008[pin] = value
|
83
|
-
mcp23008[pin].should eq(value)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
data/test/mcp23017_spec.rb
DELETED
@@ -1,88 +0,0 @@
|
|
1
|
-
require 'i2c'
|
2
|
-
|
3
|
-
class MockI2CIO
|
4
|
-
attr_reader :registers
|
5
|
-
attr_reader :last_address
|
6
|
-
|
7
|
-
def initialize
|
8
|
-
@registers = Hash.new
|
9
|
-
# Initialize according to data sheet
|
10
|
-
(0x00..0x01).each do |reg|
|
11
|
-
@registers[reg] = 0xFF
|
12
|
-
end
|
13
|
-
(0x02..0x15).each do |reg|
|
14
|
-
@registers[reg] = 0x00
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def write(address, *params)
|
19
|
-
@last_address = address
|
20
|
-
if params.count >= 1
|
21
|
-
reg_addr = params.shift
|
22
|
-
index = 0
|
23
|
-
params.each do |p|
|
24
|
-
@registers[reg_addr+index] = p
|
25
|
-
index += 1
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def read(address, size, *params)
|
31
|
-
@last_address = address
|
32
|
-
answer = String.new
|
33
|
-
answer.force_encoding("US-ASCII")
|
34
|
-
if (size > 0) && (params.count >= 1)
|
35
|
-
reg_addr = params.shift
|
36
|
-
(0...size).each do |index|
|
37
|
-
answer << (@registers[reg_addr+index] & 0xFF)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
answer
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
describe I2C::Drivers::MCP23017, "#mode?" do
|
45
|
-
it "initially returns 1 for all pin modes" do
|
46
|
-
io = MockI2CIO.new
|
47
|
-
mcp23017 = I2C::Drivers::MCP23017.new(io, 0x20)
|
48
|
-
(0..15).each do |pin|
|
49
|
-
mcp23017.mode?(pin).should eq(1)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
describe I2C::Drivers::MCP23017, "#mode?" do
|
55
|
-
it "returns what has been set through #mode" do
|
56
|
-
io = MockI2CIO.new
|
57
|
-
mcp23017 = I2C::Drivers::MCP23017.new(io, 0x20)
|
58
|
-
(0..500).each do |pin|
|
59
|
-
pin = rand(16)
|
60
|
-
mode = rand(2)
|
61
|
-
mcp23017.mode(pin, mode)
|
62
|
-
mcp23017.mode?(pin).should eq(mode)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
describe I2C::Drivers::MCP23017, "#[]" do
|
68
|
-
it "initially returns 0 for all I/O pins" do
|
69
|
-
io = MockI2CIO.new
|
70
|
-
mcp23017 = I2C::Drivers::MCP23017.new(io, 0x20)
|
71
|
-
(0..15).each do |pin|
|
72
|
-
mcp23017[pin].should eq(0)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
describe I2C::Drivers::MCP23017, "#[]" do
|
78
|
-
it "returns what has been set through #[]=" do
|
79
|
-
io = MockI2CIO.new
|
80
|
-
mcp23017 = I2C::Drivers::MCP23017.new(io, 0x20)
|
81
|
-
(0..500).each do |pin|
|
82
|
-
pin = rand(16)
|
83
|
-
value = rand(2)
|
84
|
-
mcp23017[pin] = value
|
85
|
-
mcp23017[pin].should eq(value)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|