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.
@@ -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