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 +14 -0
- data/lib/i2c/backends/i2c-dev.rb +71 -0
- data/lib/i2c/drivers/mcp17026.rb +124 -0
- data/lib/i2c/i2c.rb +35 -0
- data/rules/88-i2c.rules +1 -0
- data/test/mcp17026_spec.rb +90 -0
- metadata +50 -0
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
|
+
|
data/rules/88-i2c.rules
ADDED
@@ -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: []
|