i2c 0.1.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/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: []