i2c 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/i2c.rb ADDED
@@ -0,0 +1,14 @@
1
+ # -*- coding: utf-8 -*-
2
+ # I2C gem setup.
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
+ require 'i2c/backends/i2c-dev.rb'
11
+ require 'i2c/drivers/mcp17026.rb'
12
+
13
+
14
+
@@ -0,0 +1,71 @@
1
+ # -*- coding: utf-8 -*-
2
+ # I2C - Linux i2c-dev backend.
3
+ #
4
+ # Copyright (c) 2012 Christoph Anderegg <christoph@christoph-anderegg.ch>
5
+ # Copyright (c) 2008 Jonas Bähr, jonas.baehr@fs.ei.tum.de
6
+ # This file may be distributed under the terms of the GNU General Public
7
+ # License Version 2.
8
+ #
9
+ module I2C
10
+ class Dev
11
+ # see i2c-dev.h
12
+ I2C_SLAVE = 0x0703
13
+
14
+ def self.create(device_path)
15
+ raise Errno::ENOENT, "Device #{device_path} not found." unless File.exists?(device_path)
16
+ @instances ||= Hash.new
17
+ @instances[device_path] = Dev.new(device_path) unless @instances.has_key?(device_path)
18
+ @instances[device_path]
19
+ end
20
+
21
+ # sends every param, begining with +params[0]+
22
+ # If the current param is a Fixnum, it is treated as one byte.
23
+ # If the param is a String, this string will be send byte by byte.
24
+ # You can use Array#pack to create a string from an array
25
+ # For Fixnum there is a convinient function to_short which transforms
26
+ # the number to a string this way: 12345.to_short == [12345].pack("s")
27
+ def write(address, *params)
28
+ data = String.new
29
+ data.force_encoding("US-ASCII")
30
+ params.each do |value|
31
+ data << value
32
+ end
33
+ @device.ioctl(I2C_SLAVE, address)
34
+ @device.syswrite(data)
35
+ end
36
+
37
+ # this sends *params as the write function and then tries to read
38
+ # +size+ bytes. The result is a String which can be treated with
39
+ # String#unpack afterwards
40
+ def read(address, size, *params)
41
+ ret = ""
42
+ write(address, *params)
43
+ ret = @device.sysread(size)
44
+ return ret
45
+ end
46
+
47
+ private
48
+ def initialize(device_path)
49
+ @device = File.new(device_path, 'r+')
50
+ # change the sys* functions of the file object to meet our requirements
51
+ class << @device
52
+ alias :syswrite_orig :syswrite
53
+ def syswrite(var)
54
+ begin
55
+ syswrite_orig var
56
+ rescue Errno::EREMOTEIO
57
+ raise AckError, "No acknowledge received"
58
+ end
59
+ end
60
+ alias :sysread_orig :sysread
61
+ def sysread(var)
62
+ begin
63
+ sysread_orig var
64
+ rescue Errno::EREMOTEIO
65
+ raise AckError, "No acknowledge received"
66
+ end
67
+ end
68
+ end # class
69
+ end # initialize
70
+ end
71
+ end
@@ -0,0 +1,124 @@
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
17
+ OUTPUT = 0
18
+
19
+ # Constants for write()
20
+ HIGH = 1
21
+ LOW = 0
22
+
23
+ module I2C
24
+ module Drivers
25
+ class MCP17026
26
+ # Registers
27
+ IODIRA = 0x00
28
+ IODIRB = 0x01
29
+ GPIOA = 0x12
30
+ GPIOB = 0x13
31
+
32
+ # Creates an instance representing exactly one
33
+ # MCP17026 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..16).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/lib/i2c/i2c.rb ADDED
@@ -0,0 +1,35 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Entry point to the I2C library.
3
+ #
4
+ # Essentially this requires the correct backend
5
+ # driver, right now this means the linux i2c-dev driver.
6
+ # This could be extended to do some system specific
7
+ #
8
+ # Copyright (c) 2012 Christoph Anderegg <christoph@christoph-anderegg.ch>
9
+ # Copyright (c) 2008 Jonas Bähr, jonas.baehr@fs.ei.tum.de
10
+ #
11
+ # This file may be distributed under the terms of the GNU General Public
12
+ # License Version 2.
13
+ #
14
+
15
+ require 'i2c/backends/i2c-dev.rb'
16
+
17
+ module I2C
18
+ # some common error classes
19
+ class AckError < StandardError; end
20
+
21
+ # Returns an instance of the current backend
22
+ # driver.
23
+ #
24
+ # Is there a system agnostic way to do this?
25
+ #
26
+ # +bus_descriptor+ describes the bus to use. This is
27
+ # of course system specific. For the
28
+ # Linux i2c-dev driver this is the
29
+ # device file (e.g. /dev/i2c-0").
30
+ def self.create(bus_descriptor)
31
+ I2C::Dev.create(bus_descriptor)
32
+ end
33
+ end
34
+
35
+
@@ -0,0 +1 @@
1
+ KERNEL=="i2c-[0-9]", GROUP="i2c", MODE="0660"
@@ -0,0 +1,90 @@
1
+ require 'i2c'
2
+ #require 'mock/mock_i2c_io.rb'
3
+
4
+ class MockI2CIO
5
+
6
+ attr_reader :registers
7
+ attr_reader :last_address
8
+
9
+ def initialize
10
+ @registers = Hash.new
11
+ # Initialize according to data sheet
12
+ (0x00..0x01).each do |reg|
13
+ @registers[reg] = 0xFF
14
+ end
15
+ (0x02..0x15).each do |reg|
16
+ @registers[reg] = 0x00
17
+ end
18
+ end
19
+
20
+ def write(address, *params)
21
+ @last_address = address
22
+ if params.count >= 1
23
+ reg_addr = params.shift
24
+ index = 0
25
+ params.each do |p|
26
+ @registers[reg_addr+index] = p
27
+ index += 1
28
+ end
29
+ end
30
+ end
31
+
32
+ def read(address, size, *params)
33
+ @last_address = address
34
+ answer = String.new
35
+ answer.force_encoding("US-ASCII")
36
+ if (size > 0) && (params.count >= 1)
37
+ reg_addr = params.shift
38
+ (0...size).each do |index|
39
+ answer << (@registers[reg_addr+index] & 0xFF)
40
+ end
41
+ end
42
+ answer
43
+ end
44
+ end
45
+
46
+ describe I2C::Drivers::MCP17026, "#mode?" do
47
+ it "initially returns 1 for all pin modes" do
48
+ io = MockI2CIO.new
49
+ mcp17026 = I2C::Drivers::MCP17026.new(io, 0x20)
50
+ (0..15).each do |pin|
51
+ mcp17026.mode?(pin).should eq(1)
52
+ end
53
+ end
54
+ end
55
+
56
+ describe I2C::Drivers::MCP17026, "#mode?" do
57
+ it "returns what has been set through #mode" do
58
+ io = MockI2CIO.new
59
+ mcp17026 = I2C::Drivers::MCP17026.new(io, 0x20)
60
+ (0..500).each do |pin|
61
+ pin = rand(16)
62
+ mode = rand(2)
63
+ mcp17026.mode(pin, mode)
64
+ mcp17026.mode?(pin).should eq(mode)
65
+ end
66
+ end
67
+ end
68
+
69
+ describe I2C::Drivers::MCP17026, "#[]" do
70
+ it "initially returns 0 for all I/O pins" do
71
+ io = MockI2CIO.new
72
+ mcp17026 = I2C::Drivers::MCP17026.new(io, 0x20)
73
+ (0..15).each do |pin|
74
+ mcp17026[pin].should eq(0)
75
+ end
76
+ end
77
+ end
78
+
79
+ describe I2C::Drivers::MCP17026, "#[]" do
80
+ it "returns what has been set through #[]=" do
81
+ io = MockI2CIO.new
82
+ mcp17026 = I2C::Drivers::MCP17026.new(io, 0x20)
83
+ (0..500).each do |pin|
84
+ pin = rand(16)
85
+ value = rand(2)
86
+ mcp17026[pin] = value
87
+ mcp17026[pin].should eq(value)
88
+ end
89
+ end
90
+ end
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: i2c
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Christoph Anderegg
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-03 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Interface to Linux I2C (a.k.a. TWI) implementations.
15
+ email: christoph@christoph-anderegg.ch
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/i2c.rb
21
+ - lib/i2c/i2c.rb
22
+ - lib/i2c/backends/i2c-dev.rb
23
+ - lib/i2c/drivers/mcp17026.rb
24
+ - test//mcp17026_spec.rb
25
+ - rules/88-i2c.rules
26
+ homepage: https://github.com/andec/i2c
27
+ licenses: []
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubyforge_project:
46
+ rubygems_version: 1.8.24
47
+ signing_key:
48
+ specification_version: 3
49
+ summary: I2C access library.
50
+ test_files: []