i2c 0.2.22 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- - A backend for accessing the I2C bus (right now only through the i2c-dev
11
+ * A backend for accessing the I2C bus (right now only through the i2c-dev
12
12
  in Linux, other impementations are possible).
13
- - Drivers for I2C enabled ICs.
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
- Bugfixes by
61
- - Pierre Paques
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
@@ -8,8 +8,8 @@
8
8
 
9
9
  require 'i2c/i2c.rb'
10
10
  require 'i2c/backends/i2c-dev.rb'
11
- require 'i2c/drivers/mcp23008.rb'
12
- require 'i2c/drivers/mcp23017.rb'
11
+ require 'i2c/drivers/mcp230xx.rb'
12
+ #require 'i2c/drivers/mcp23017.rb'
13
13
 
14
14
 
15
15
 
@@ -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.2.22
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-04-15 00:00:00.000000000 Z
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/mcp23017.rb
26
- - lib/i2c/drivers/mcp23008.rb
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.24
49
+ rubygems_version: 1.8.23
52
50
  signing_key:
53
51
  specification_version: 3
54
52
  summary: I2C access library (for Linux).
@@ -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
@@ -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
@@ -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
@@ -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