i2c-devices 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/ChangeLog +4 -0
- data/README.md +130 -0
- data/Rakefile +22 -0
- data/i2c-devices.gemspec +22 -0
- data/lib/i2c.rb +28 -0
- data/lib/i2c/device/acm1602ni.rb +18 -0
- data/lib/i2c/device/adt7410.rb +146 -0
- data/lib/i2c/device/aqm0802.rb +58 -0
- data/lib/i2c/device/hd44780.rb +137 -0
- data/lib/i2c/device/mpl115a2.rb +39 -0
- data/lib/i2c/driver/gpio.rb +198 -0
- data/lib/i2c/driver/i2c-dev.rb +53 -0
- data/lib/i2c/mocki2cdevice.rb +67 -0
- data/spec/device/adt7410_spec.rb +87 -0
- data/spec/device/hd44780_spec.rb +155 -0
- data/spec/driver/gpio_spec.rb +514 -0
- data/spec/i2cdevice_spec.rb +89 -0
- data/xt/acm1602ni.rb +22 -0
- data/xt/driver-gpio.rb +34 -0
- data/xt/i2cdetect.rb +69 -0
- data/xt/mpl115a2.rb +12 -0
- metadata +97 -0
@@ -0,0 +1,137 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require "i2c"
|
4
|
+
|
5
|
+
# I2C interface with HD44780 compatible commands
|
6
|
+
class HD44780 < I2CDevice
|
7
|
+
MAP = Hash[
|
8
|
+
[
|
9
|
+
"。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚".split(//).map {|c|
|
10
|
+
c.force_encoding(Encoding::BINARY)
|
11
|
+
},
|
12
|
+
(0b10100001..0b11011111).map {|c|
|
13
|
+
c.chr
|
14
|
+
}
|
15
|
+
].transpose
|
16
|
+
]
|
17
|
+
|
18
|
+
def initialize(args={})
|
19
|
+
super
|
20
|
+
@lines = []
|
21
|
+
initialize_lcd
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize_lcd
|
25
|
+
function_set(1, 1, 0)
|
26
|
+
sleep 4.1e-3
|
27
|
+
function_set(1, 1, 0)
|
28
|
+
sleep 100e-6
|
29
|
+
function_set(1, 1, 0)
|
30
|
+
function_set(1, 1, 0)
|
31
|
+
display_on_off_control(1, 0, 0)
|
32
|
+
clear
|
33
|
+
end
|
34
|
+
|
35
|
+
def put_line(line, str, force=false)
|
36
|
+
str.force_encoding(Encoding::BINARY)
|
37
|
+
str.gsub!(/#{MAP.keys.join('|')}/, MAP)
|
38
|
+
|
39
|
+
str = "%- 16s" % str
|
40
|
+
|
41
|
+
if force || str != @lines[line]
|
42
|
+
# set ddram address
|
43
|
+
set_ddram_address(0x40 * line)
|
44
|
+
sleep 60e-6
|
45
|
+
i2cset(*str.unpack("C*").map {|i| [0x80, i] }.flatten)
|
46
|
+
sleep 60e-6
|
47
|
+
end
|
48
|
+
@lines[line] = str
|
49
|
+
end
|
50
|
+
|
51
|
+
# Usage:
|
52
|
+
# lcd.define_character(0, [
|
53
|
+
# 0,1,1,1,0,
|
54
|
+
# 1,0,0,0,1,
|
55
|
+
# 1,1,0,1,1,
|
56
|
+
# 1,0,1,0,1,
|
57
|
+
# 1,1,0,1,1,
|
58
|
+
# 1,0,0,0,1,
|
59
|
+
# 1,0,0,0,1,
|
60
|
+
# 0,1,1,1,0,
|
61
|
+
# ])
|
62
|
+
def define_character(n, array)
|
63
|
+
raise "n < 8" unless n < 8
|
64
|
+
raise "array size must be 40 (5x8)" unless array.size == 40
|
65
|
+
|
66
|
+
array = array.each_slice(5).map {|i|
|
67
|
+
i.inject {|r,i| (r << 1) + i }
|
68
|
+
}
|
69
|
+
set_cgram_address(8 * n)
|
70
|
+
sleep 60e-6
|
71
|
+
i2cset(*array.map {|i| [0x80, i] }.flatten)
|
72
|
+
sleep 60e-6
|
73
|
+
end
|
74
|
+
|
75
|
+
def clear
|
76
|
+
@lines.clear
|
77
|
+
clear_display
|
78
|
+
end
|
79
|
+
|
80
|
+
def clear_display
|
81
|
+
i2cset(0, 0b00000001)
|
82
|
+
sleep 2.16e-3
|
83
|
+
end
|
84
|
+
|
85
|
+
def return_home
|
86
|
+
i2cset(0, 0b00000010)
|
87
|
+
sleep 1.52e-3
|
88
|
+
end
|
89
|
+
|
90
|
+
# i_d : increment or decrement: 1: increment, 0: decrement
|
91
|
+
# s : shift entire display: 1: left, 0: right
|
92
|
+
def entry_mode_set(i_d, s)
|
93
|
+
i2cset(0, 0b00000100 | (i_d<<1) | (s))
|
94
|
+
sleep 60e-6
|
95
|
+
end
|
96
|
+
|
97
|
+
# d: set entire display on/off
|
98
|
+
# c: cursor on/off
|
99
|
+
# b: blink cursor
|
100
|
+
def display_on_off_control(d, c, b)
|
101
|
+
i2cset(0, 0b00001000 | (d<<2) | (c<<1) | (b))
|
102
|
+
sleep 60e-6
|
103
|
+
end
|
104
|
+
|
105
|
+
def cursor_or_display_shift(s_c, r_l)
|
106
|
+
i2cset(0, 0b00010000 | (s_c<<3) | (r_l<<2))
|
107
|
+
sleep 60e-6
|
108
|
+
end
|
109
|
+
|
110
|
+
# dl data_length: 1: 8bit, 0: 4bit
|
111
|
+
# n number_of_display_lines: 1: 2-line, 0: 1-line
|
112
|
+
# f character_font: 1: double font, 0: normal
|
113
|
+
def function_set(dl, n, f)
|
114
|
+
i2cset(0, 0b00100000 | (dl<<4) | (n<<3) | (f<<2))
|
115
|
+
sleep 60e-6
|
116
|
+
end
|
117
|
+
|
118
|
+
def set_cgram_address(address)
|
119
|
+
address = address & 0b00111111
|
120
|
+
i2cset(0, 0b01000000 | address)
|
121
|
+
sleep 60e-6
|
122
|
+
end
|
123
|
+
|
124
|
+
def set_ddram_address(address)
|
125
|
+
address = address & 0b01111111
|
126
|
+
i2cset(0, 0b10000000 | address)
|
127
|
+
sleep 60e-6
|
128
|
+
end
|
129
|
+
|
130
|
+
def read_busy_flag_and_address
|
131
|
+
read = i2cget(0b01000000)
|
132
|
+
{
|
133
|
+
:busy => (read & 0b10000000) != 0,
|
134
|
+
:address_counter => read & 0b01111111
|
135
|
+
}
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
require "i2c"
|
3
|
+
|
4
|
+
class MPL115A2 < I2CDevice
|
5
|
+
def initialize(args={})
|
6
|
+
args[:address] = 0x60
|
7
|
+
super
|
8
|
+
coefficient = i2cget(0x04, 8).unpack("n*")
|
9
|
+
|
10
|
+
@a0 = fixed_point(coefficient[0], 12)
|
11
|
+
@b1 = fixed_point(coefficient[1], 2)
|
12
|
+
@b2 = fixed_point(coefficient[2], 1)
|
13
|
+
@c12 = fixed_point(coefficient[3], 0) / (1<<9)
|
14
|
+
end
|
15
|
+
|
16
|
+
def fixed_point(fixed, int_bits)
|
17
|
+
msb = 15
|
18
|
+
deno = (1<<(msb-int_bits)).to_f
|
19
|
+
if (fixed & (1<<15)).zero?
|
20
|
+
fixed / deno
|
21
|
+
else
|
22
|
+
-( ( (~fixed & 0xffff) + 1) / deno )
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def calculate_hPa
|
27
|
+
i2cset(0x12, 0x01) # CONVERT
|
28
|
+
|
29
|
+
sleep 0.003
|
30
|
+
|
31
|
+
data = i2cget(0x00, 4).unpack("n*")
|
32
|
+
|
33
|
+
p_adc = (data[0]) >> 6
|
34
|
+
t_adc = (data[1]) >> 6
|
35
|
+
|
36
|
+
p_comp = @a0 + (@b1 + @c12 * t_adc) * p_adc + @b2 * t_adc
|
37
|
+
hPa = p_comp * ( (1150 - 500) / 1023.0) + 500;
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
require "i2c"
|
2
|
+
=begin
|
3
|
+
Generic software I2C Driver based on /sys/class/gpio.
|
4
|
+
THIS MODULE WORKS WITH VERY SLOW SPEED ABOUT JUST 1kHz (normaly 100kHz).
|
5
|
+
=end
|
6
|
+
|
7
|
+
module I2CDevice::Driver
|
8
|
+
class GPIO
|
9
|
+
@@DEBUG = false
|
10
|
+
|
11
|
+
def self.export(pin)
|
12
|
+
File.open("/sys/class/gpio/export", "w") do |f|
|
13
|
+
f.syswrite(pin)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.unexport(pin)
|
18
|
+
File.open("/sys/class/gpio/unexport", "w") do |f|
|
19
|
+
f.syswrite(pin)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.direction(pin, direction)
|
24
|
+
# [:in, :out, :high, :low].include?(direction) or raise "direction must be :in, :out, :high or :low"
|
25
|
+
File.open("/sys/class/gpio/gpio#{pin}/direction", "w") do |f|
|
26
|
+
f.syswrite(direction)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.read(pin)
|
31
|
+
File.open("/sys/class/gpio/gpio#{pin}/value", "r") do |f|
|
32
|
+
f.sysread(1).to_i
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.write(pin, val)
|
37
|
+
File.open("/sys/class/gpio/gpio#{pin}/value", "w") do |f|
|
38
|
+
f.syswrite(val && val.nonzero?? "1" : "0")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.finalizer(ports)
|
43
|
+
proc do
|
44
|
+
ports.each do |pin|
|
45
|
+
GPIO.unexport(pin)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
attr_reader :sda, :scl, :speed
|
51
|
+
|
52
|
+
def initialize(opts={})
|
53
|
+
@sda = opts[:sda] or raise "opts[:sda] = [gpio pin number] is required"
|
54
|
+
@scl = opts[:scl] or raise "opts[:scl] = [gpio pin number] is required"
|
55
|
+
@speed = opts[:speed] || 1 # kHz but insane
|
56
|
+
@clock = 1.0 / (@speed * 1000)
|
57
|
+
|
58
|
+
begin
|
59
|
+
GPIO.export(@sda)
|
60
|
+
GPIO.export(@scl)
|
61
|
+
rescue Errno::EBUSY => e
|
62
|
+
end
|
63
|
+
ObjectSpace.define_finalizer(self, self.class.finalizer([@scl, @sda]))
|
64
|
+
begin
|
65
|
+
GPIO.direction(@sda, :high)
|
66
|
+
GPIO.direction(@scl, :high)
|
67
|
+
GPIO.direction(@sda, :in)
|
68
|
+
GPIO.direction(@scl, :in)
|
69
|
+
rescue Errno::EACCES => e # writing to gpio after export is failed in a while
|
70
|
+
retry
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def i2cget(address, param, length=1)
|
75
|
+
ret = ""
|
76
|
+
start_condition
|
77
|
+
unless write( (address << 1) + 0)
|
78
|
+
raise I2CDevice::I2CIOError, "Unknown slave device (address:#{address})"
|
79
|
+
end
|
80
|
+
write(param)
|
81
|
+
stop_condition # AVR stucked with SCL low without this (Does not AVR support Sr condition?)
|
82
|
+
start_condition
|
83
|
+
unless write( (address << 1) + 1)
|
84
|
+
raise I2CDevice::I2CIOError, "Unknown slave device (address:#{address})"
|
85
|
+
end
|
86
|
+
length.times do |n|
|
87
|
+
ret << read(n != length - 1).chr
|
88
|
+
end
|
89
|
+
ret
|
90
|
+
ensure
|
91
|
+
stop_condition
|
92
|
+
end
|
93
|
+
|
94
|
+
def i2cset(address, *data)
|
95
|
+
sent = 0
|
96
|
+
start_condition
|
97
|
+
unless write( (address << 1) + 0)
|
98
|
+
raise I2CDevice::I2CIOError, "Unknown slave device (address:#{address})"
|
99
|
+
end
|
100
|
+
data.each do |c|
|
101
|
+
unless write(c)
|
102
|
+
break
|
103
|
+
end
|
104
|
+
sent += 1
|
105
|
+
end
|
106
|
+
sent
|
107
|
+
ensure
|
108
|
+
stop_condition
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def start_condition
|
114
|
+
p :start_condition if @@DEBUG
|
115
|
+
sleep @clock
|
116
|
+
GPIO.direction(@sda, :in)
|
117
|
+
GPIO.direction(@scl, :in)
|
118
|
+
if GPIO.read(@scl) == 0
|
119
|
+
raise I2CDevice::I2CBUSBusy, "BUS is busy"
|
120
|
+
end
|
121
|
+
|
122
|
+
sleep @clock / 2
|
123
|
+
GPIO.direction(@scl, :high)
|
124
|
+
sleep @clock / 2
|
125
|
+
GPIO.direction(@sda, :low)
|
126
|
+
sleep @clock
|
127
|
+
end
|
128
|
+
|
129
|
+
def stop_condition
|
130
|
+
p :stop_condition if @@DEBUG
|
131
|
+
GPIO.direction(@scl, :low)
|
132
|
+
sleep @clock / 2
|
133
|
+
GPIO.direction(@sda, :low)
|
134
|
+
sleep @clock / 2
|
135
|
+
GPIO.direction(@scl, :in)
|
136
|
+
sleep @clock / 2
|
137
|
+
GPIO.direction(@sda, :in)
|
138
|
+
sleep @clock / 2
|
139
|
+
end
|
140
|
+
|
141
|
+
def write(byte)
|
142
|
+
p [:write, byte] if @@DEBUG
|
143
|
+
GPIO.direction(@scl, :low)
|
144
|
+
sleep @clock
|
145
|
+
|
146
|
+
7.downto(0) do |n|
|
147
|
+
GPIO.direction(@sda, byte[n] == 1 ? :high : :low)
|
148
|
+
GPIO.direction(@scl, :in)
|
149
|
+
until GPIO.read(@scl) == 1
|
150
|
+
# clock streching
|
151
|
+
sleep @clock
|
152
|
+
end
|
153
|
+
sleep @clock
|
154
|
+
GPIO.direction(@scl, :low)
|
155
|
+
GPIO.write(@sda, false)
|
156
|
+
sleep @clock
|
157
|
+
end
|
158
|
+
|
159
|
+
GPIO.direction(@sda, :in)
|
160
|
+
GPIO.direction(@scl, :in)
|
161
|
+
sleep @clock / 2
|
162
|
+
ack = GPIO.read(@sda) == 0
|
163
|
+
sleep @clock / 2
|
164
|
+
while GPIO.read(@scl) == 0
|
165
|
+
sleep @clock
|
166
|
+
end
|
167
|
+
GPIO.direction(@scl, :low)
|
168
|
+
ack
|
169
|
+
end
|
170
|
+
|
171
|
+
def read(ack=true)
|
172
|
+
p [:read, ack] if @@DEBUG
|
173
|
+
ret = 0
|
174
|
+
|
175
|
+
GPIO.direction(@scl, :low)
|
176
|
+
sleep @clock
|
177
|
+
GPIO.direction(@sda, :in)
|
178
|
+
|
179
|
+
8.times do
|
180
|
+
GPIO.direction(@scl, :in)
|
181
|
+
sleep @clock / 2
|
182
|
+
ret = (ret << 1) | GPIO.read(@sda)
|
183
|
+
sleep @clock / 2
|
184
|
+
GPIO.direction(@scl, :low)
|
185
|
+
sleep @clock
|
186
|
+
end
|
187
|
+
|
188
|
+
GPIO.direction(@sda, ack ? :low : :high)
|
189
|
+
|
190
|
+
GPIO.write(@scl, true)
|
191
|
+
sleep @clock
|
192
|
+
GPIO.write(@scl, false)
|
193
|
+
sleep @clock
|
194
|
+
ret
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
require "i2c"
|
3
|
+
|
4
|
+
module I2CDevice::Driver
|
5
|
+
class I2CDev
|
6
|
+
# ioctl command
|
7
|
+
# Ref. https://www.kernel.org/pub/linux/kernel/people/marcelo/linux-2.4/include/linux/i2c.h
|
8
|
+
I2C_RETRIES = 0x0701
|
9
|
+
I2C_TIMEOUT = 0x0702
|
10
|
+
I2C_SLAVE = 0x0703
|
11
|
+
I2C_SLAVE_FORCE = 0x0706
|
12
|
+
I2C_TENBIT = 0x0704
|
13
|
+
I2C_FUNCS = 0x0705
|
14
|
+
I2C_RDWR = 0x0707
|
15
|
+
I2C_SMBUS = 0x0720
|
16
|
+
I2C_UDELAY = 0x0705
|
17
|
+
I2C_MDELAY = 0x0706
|
18
|
+
|
19
|
+
def initialize(path=nil)
|
20
|
+
if path.nil?
|
21
|
+
path = Dir.glob("/dev/i2c-*").sort.last
|
22
|
+
end
|
23
|
+
|
24
|
+
unless File.exist?(path)
|
25
|
+
raise I2CDevice::I2CIOError, "/dev/i2c-0 is required"
|
26
|
+
end
|
27
|
+
|
28
|
+
@path = path
|
29
|
+
end
|
30
|
+
|
31
|
+
def i2cget(address, param, length)
|
32
|
+
i2c = File.open(@path, "r+")
|
33
|
+
i2c.ioctl(I2C_SLAVE, address)
|
34
|
+
i2c.syswrite(param.chr)
|
35
|
+
ret = i2c.sysread(length)
|
36
|
+
i2c.close
|
37
|
+
ret
|
38
|
+
rescue Errno::EIO => e
|
39
|
+
raise I2CDevice::I2CIOError, e.message
|
40
|
+
end
|
41
|
+
|
42
|
+
def i2cset(address, *data)
|
43
|
+
i2c = File.open(@path, "r+")
|
44
|
+
i2c.ioctl(I2C_SLAVE, address)
|
45
|
+
i2c.syswrite(data.pack("C*"))
|
46
|
+
i2c.close
|
47
|
+
rescue Errno::EIO => e
|
48
|
+
raise I2CDevice::I2CIOError, e.message
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
class MockI2CDevice
|
4
|
+
attr_reader :memory
|
5
|
+
attr_reader :ioctl
|
6
|
+
attr_reader :state
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@temp = Tempfile.new("i2c")
|
10
|
+
@ioctl = []
|
11
|
+
@memory = [ 0 ]
|
12
|
+
@address = nil
|
13
|
+
@state = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def path
|
17
|
+
@temp.path
|
18
|
+
end
|
19
|
+
|
20
|
+
def ioctl(cmd, arg)
|
21
|
+
@ioctl = [cmd, arg]
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def open
|
26
|
+
@address = nil
|
27
|
+
@state = :init
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def close
|
32
|
+
@address = nil
|
33
|
+
@state = nil
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def syswrite(buf)
|
38
|
+
buf.unpack("C*").each do |c|
|
39
|
+
case @state
|
40
|
+
when :init
|
41
|
+
# p "@address = 0x%02x" % c
|
42
|
+
@address = c
|
43
|
+
@state = :wait
|
44
|
+
when :wait
|
45
|
+
# p "@memory[0x%02x] = 0b%08b" % [@address, c]
|
46
|
+
@memory[@address] = c
|
47
|
+
@address += 1
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def sysread(size)
|
53
|
+
ret = []
|
54
|
+
case @state
|
55
|
+
when :init
|
56
|
+
raise "Invalid State"
|
57
|
+
when :wait
|
58
|
+
size.times do
|
59
|
+
ret << @memory[@address]
|
60
|
+
@address += 1
|
61
|
+
end
|
62
|
+
end
|
63
|
+
ret.pack("C*")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|