lgpio 0.1.4 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +43 -24
- data/examples/bench_in.rb +1 -1
- data/examples/bench_out.rb +1 -1
- data/examples/blink.rb +1 -1
- data/examples/dht.rb +47 -0
- data/examples/group_in.rb +2 -2
- data/examples/group_out.rb +1 -1
- data/examples/hcsr04.rb +32 -0
- data/examples/i2c_aht10.rb +3 -2
- data/examples/i2c_aht10_zip.rb +3 -2
- data/examples/i2c_bitbang-rb_aht10.rb +40 -0
- data/examples/i2c_bitbang-rb_ssd1306_bench.rb +35 -0
- data/examples/i2c_bitbang_aht10.rb +40 -0
- data/examples/i2c_bitbang_search.rb +20 -0
- data/examples/i2c_bitbang_ssd1306_bench.rb +35 -0
- data/examples/i2c_ssd1306_bench.rb +7 -6
- data/examples/infrared.rb +22 -0
- data/examples/momentary.rb +2 -2
- data/examples/one_wire_ds18b20.rb +30 -0
- data/examples/one_wire_search.rb +15 -0
- data/examples/pwm.rb +1 -1
- data/examples/reports.rb +1 -1
- data/examples/rotary_encoder.rb +3 -3
- data/examples/rotary_encoder_led.rb +4 -4
- data/examples/servo.rb +36 -0
- data/examples/spi_bitbang_loopback.rb +25 -0
- data/examples/spi_bitbang_ssd1306_bench.rb +51 -0
- data/examples/spi_bitbang_ssd1306_sim_bench.rb +48 -0
- data/examples/wave.rb +1 -1
- data/ext/lgpio/lgpio.c +454 -9
- data/lib/lgpio/hardware_pwm.rb +83 -0
- data/lib/lgpio/i2c_bitbang.rb +117 -0
- data/lib/lgpio/infrared.rb +8 -0
- data/lib/lgpio/one_wire.rb +171 -0
- data/lib/lgpio/positional_servo.rb +28 -0
- data/lib/lgpio/spi_bitbang.rb +109 -0
- data/lib/lgpio/version.rb +1 -1
- data/lib/lgpio.rb +9 -0
- metadata +22 -3
- data/examples/spi_read.rb +0 -14
@@ -0,0 +1,83 @@
|
|
1
|
+
module LGPIO
|
2
|
+
class HardwarePWM
|
3
|
+
NS_PER_S = 1_000_000_000
|
4
|
+
NS_PER_US = 1_000
|
5
|
+
SYS_FS_PWM_PATH = "/sys/class/pwm/"
|
6
|
+
|
7
|
+
attr_reader :period, :duty, :enabled
|
8
|
+
|
9
|
+
def initialize(chip, channel, frequency: nil, period: nil)
|
10
|
+
@chip = chip
|
11
|
+
@channel = channel
|
12
|
+
|
13
|
+
# Accept either frequency (in Hz) or period in nanoseconds.
|
14
|
+
if (frequency && period) || (!frequency && !period)
|
15
|
+
raise "either period: or frequency: is required, but not both"
|
16
|
+
end
|
17
|
+
|
18
|
+
period ? self.period = period : self.frequency = frequency
|
19
|
+
enable
|
20
|
+
end
|
21
|
+
|
22
|
+
def path
|
23
|
+
@path ||= "#{SYS_FS_PWM_PATH}pwmchip#{@chip}/pwm#{@channel}/"
|
24
|
+
end
|
25
|
+
|
26
|
+
def period_path
|
27
|
+
@period_path ||= "#{path}period"
|
28
|
+
end
|
29
|
+
|
30
|
+
def duty_path
|
31
|
+
@duty_path ||= "#{path}duty_cycle"
|
32
|
+
end
|
33
|
+
|
34
|
+
def enable_path
|
35
|
+
@enable_path ||= "#{path}enable"
|
36
|
+
end
|
37
|
+
|
38
|
+
def frequency=(freq)
|
39
|
+
self.period = (NS_PER_S / freq.to_f).round
|
40
|
+
end
|
41
|
+
|
42
|
+
def period=(p)
|
43
|
+
old_period = File.read("#{path}period").strip.to_i
|
44
|
+
unless (old_period == 0)
|
45
|
+
File.open(duty_path, 'w') { |f| f.write("0") }
|
46
|
+
end
|
47
|
+
File.open(period_path, 'w') { |f| f.write(p) }
|
48
|
+
@period = p
|
49
|
+
end
|
50
|
+
|
51
|
+
def duty_percent
|
52
|
+
return 0.0 if (!duty || !period) || (duty == 0)
|
53
|
+
(duty / period.to_f) * 100.0
|
54
|
+
end
|
55
|
+
|
56
|
+
def duty_percent=(d)
|
57
|
+
raise "duty_cycle: #{d} % cannot be more than 100%" if d > 100
|
58
|
+
d_ns = ((d / 100.0) * @period.to_i).round
|
59
|
+
self.duty = d_ns
|
60
|
+
end
|
61
|
+
|
62
|
+
def duty_us=(d_us)
|
63
|
+
d_ns = (d_us * NS_PER_US).round
|
64
|
+
self.duty = d_ns
|
65
|
+
end
|
66
|
+
|
67
|
+
def duty=(d_ns)
|
68
|
+
raise "duty cycle: #{d_ns} ns cannot be longer than period: #{period} ns" if d_ns > period
|
69
|
+
File.open(duty_path, 'w') { |f| f.write(d_ns) }
|
70
|
+
@duty = d_ns
|
71
|
+
end
|
72
|
+
|
73
|
+
def disable
|
74
|
+
File.open(enable_path, 'w') { |f| f.write("0") }
|
75
|
+
@enabled = false
|
76
|
+
end
|
77
|
+
|
78
|
+
def enable
|
79
|
+
File.open(enable_path, 'w') { |f| f.write("1") }
|
80
|
+
@enabled = true
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module LGPIO
|
2
|
+
class I2CBitBang
|
3
|
+
VALID_ADDRESSES = (0x08..0x77).to_a
|
4
|
+
|
5
|
+
attr_reader :handle, :scl, :sda
|
6
|
+
|
7
|
+
def initialize(handle, scl, sda)
|
8
|
+
@handle = handle
|
9
|
+
@scl = scl
|
10
|
+
@sda = sda
|
11
|
+
@sda_state = nil
|
12
|
+
initialize_pins
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize_pins
|
16
|
+
LGPIO.gpio_claim_output(handle, LGPIO::SET_PULL_NONE, scl, LGPIO::HIGH)
|
17
|
+
LGPIO.gpio_claim_output(handle, LGPIO::SET_OPEN_DRAIN | LGPIO::SET_PULL_UP, sda, LGPIO::HIGH)
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_sda(value)
|
21
|
+
return if (@sda_state == value)
|
22
|
+
LGPIO.gpio_write(handle, sda, @sda_state = value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def write_form(address)
|
26
|
+
(address << 1)
|
27
|
+
end
|
28
|
+
|
29
|
+
def read_form(address)
|
30
|
+
(address << 1) | 0b00000001
|
31
|
+
end
|
32
|
+
|
33
|
+
def start
|
34
|
+
LGPIO.gpio_write(handle, sda, 0)
|
35
|
+
LGPIO.gpio_write(handle, scl, 0)
|
36
|
+
end
|
37
|
+
|
38
|
+
def stop
|
39
|
+
LGPIO.gpio_write(handle, sda, 0)
|
40
|
+
LGPIO.gpio_write(handle, scl, 1)
|
41
|
+
LGPIO.gpio_write(handle, sda, 1)
|
42
|
+
end
|
43
|
+
|
44
|
+
def read_bit
|
45
|
+
set_sda(1)
|
46
|
+
LGPIO.gpio_write(handle, scl, 1)
|
47
|
+
bit = LGPIO.gpio_read(handle, sda)
|
48
|
+
LGPIO.gpio_write(handle, scl, 0)
|
49
|
+
bit
|
50
|
+
end
|
51
|
+
|
52
|
+
def write_bit(bit)
|
53
|
+
set_sda(bit)
|
54
|
+
LGPIO.gpio_write(handle, scl, 1)
|
55
|
+
LGPIO.gpio_write(handle, scl, 0)
|
56
|
+
end
|
57
|
+
|
58
|
+
def read_byte(ack)
|
59
|
+
byte = 0
|
60
|
+
i = 0
|
61
|
+
while i < 8
|
62
|
+
byte = (byte << 1) | read_bit
|
63
|
+
i = i + 1
|
64
|
+
end
|
65
|
+
write_bit(ack ? 0 : 1)
|
66
|
+
byte
|
67
|
+
end
|
68
|
+
|
69
|
+
def write_byte(byte)
|
70
|
+
i = 7
|
71
|
+
while i >= 0
|
72
|
+
write_bit (byte >> i) & 0b1
|
73
|
+
i = i - 1
|
74
|
+
end
|
75
|
+
# Return ACK (SDA pulled low) or NACK (SDA stayed high).
|
76
|
+
(read_bit == 0)
|
77
|
+
end
|
78
|
+
|
79
|
+
def read(address, length)
|
80
|
+
raise ArgumentError, "invalid I2C address: #{address}. Range is 0x08..0x77" unless VALID_ADDRESSES.include?(address)
|
81
|
+
raise ArgumentError, "invalid Integer for read length: #{length}" unless length.kind_of?(Integer)
|
82
|
+
|
83
|
+
start
|
84
|
+
ack = write_byte(read_form(address))
|
85
|
+
return nil unless ack
|
86
|
+
|
87
|
+
# Read length bytes, and ACK for all but the last one.
|
88
|
+
bytes = []
|
89
|
+
(length-1).times { bytes << read_byte(true) }
|
90
|
+
bytes << read_byte(false)
|
91
|
+
stop
|
92
|
+
|
93
|
+
bytes
|
94
|
+
end
|
95
|
+
|
96
|
+
def write(address, bytes)
|
97
|
+
raise ArgumentError, "invalid I2C address: #{address}. Range is 0x08..0x77" unless VALID_ADDRESSES.include?(address)
|
98
|
+
raise ArgumentError, "invalid byte Array to write: #{bytes}" unless bytes.kind_of?(Array)
|
99
|
+
|
100
|
+
start
|
101
|
+
write_byte(write_form(address))
|
102
|
+
bytes.each { |byte| write_byte(byte) }
|
103
|
+
stop
|
104
|
+
end
|
105
|
+
|
106
|
+
def search
|
107
|
+
found = []
|
108
|
+
VALID_ADDRESSES.each do |address|
|
109
|
+
start
|
110
|
+
# Device present if ACK received when we write its address to the bus.
|
111
|
+
found << address if write_byte(write_form(address))
|
112
|
+
stop
|
113
|
+
end
|
114
|
+
found
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
module LGPIO
|
2
|
+
class OneWire
|
3
|
+
# Constants
|
4
|
+
READ_POWER_SUPPLY = 0xB4
|
5
|
+
CONVERT_T = 0x44
|
6
|
+
SEARCH_ROM = 0xF0
|
7
|
+
READ_ROM = 0x33
|
8
|
+
SKIP_ROM = 0xCC
|
9
|
+
MATCH_ROM = 0x55
|
10
|
+
ALARM_SEARCH = 0xEC
|
11
|
+
READ_SCRATCH = 0xBE
|
12
|
+
WRITE_SCRATCH = 0x4E
|
13
|
+
COPY_SCRATCH = 0x48
|
14
|
+
RECALL_EEPROM = 0xB8
|
15
|
+
|
16
|
+
attr_reader :handle, :gpio, :found_addresses
|
17
|
+
|
18
|
+
def initialize(handle, gpio)
|
19
|
+
@handle = handle
|
20
|
+
@gpio = gpio
|
21
|
+
@found_addresses = []
|
22
|
+
LGPIO.gpio_claim_output(handle, LGPIO::SET_OPEN_DRAIN | LGPIO::SET_PULL_UP, gpio, LGPIO::HIGH)
|
23
|
+
end
|
24
|
+
|
25
|
+
def reset
|
26
|
+
# LGPIO.one_wire_reset returns 0 if device present on bus.
|
27
|
+
return (LGPIO.one_wire_reset(handle, gpio) == 0)
|
28
|
+
end
|
29
|
+
|
30
|
+
def read(byte_count)
|
31
|
+
read_bytes = []
|
32
|
+
byte_count.times do
|
33
|
+
byte = 0b00000000
|
34
|
+
8.times { |i| byte |= LGPIO.one_wire_bit_read(handle, gpio) << i }
|
35
|
+
read_bytes << byte
|
36
|
+
end
|
37
|
+
read_bytes
|
38
|
+
end
|
39
|
+
|
40
|
+
def write(byte_array, parasite: nil)
|
41
|
+
byte_array.each do |byte|
|
42
|
+
8.times { |i| LGPIO.one_wire_bit_write(handle, gpio, (byte >> i) & 0b1) }
|
43
|
+
end
|
44
|
+
# Drive bus high to feed parasite capacitor after write.
|
45
|
+
LGPIO.gpio_write(handle, gpio, LGPIO::HIGH) if parasite
|
46
|
+
end
|
47
|
+
|
48
|
+
def search
|
49
|
+
branch_mask = 0
|
50
|
+
high_discrepancy = 0
|
51
|
+
|
52
|
+
loop do
|
53
|
+
reset
|
54
|
+
write [SEARCH_ROM]
|
55
|
+
result = search_pass(branch_mask)
|
56
|
+
|
57
|
+
address, high_discrepancy = parse_search_result(result)
|
58
|
+
@found_addresses << address
|
59
|
+
|
60
|
+
# No unsearched discrepancies left.
|
61
|
+
break if high_discrepancy == -1
|
62
|
+
|
63
|
+
# Force highest new discrepancy to be a 1 on the next search.
|
64
|
+
# i.e. Go as deep as possible into each branch found then back out.
|
65
|
+
#
|
66
|
+
branch_mask = branch_mask | (2 ** high_discrepancy)
|
67
|
+
|
68
|
+
# Clear bits above high_discrepancy so we don't repeat branches.
|
69
|
+
# When high_discrepancy < MSB of branch_mask, this moves us
|
70
|
+
# one node out, closer to the root, and finishing the search.
|
71
|
+
#
|
72
|
+
unset_mask = 0xFFFFFFFFFFFFFFFF >> (63 - high_discrepancy)
|
73
|
+
branch_mask = branch_mask & unset_mask
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Read a single 64-bit address and complement from the bus.
|
78
|
+
def search_pass(mask)
|
79
|
+
bytes = []
|
80
|
+
|
81
|
+
8.times do |i|
|
82
|
+
addr = 0
|
83
|
+
comp = 0
|
84
|
+
8.times do |j|
|
85
|
+
addr |= LGPIO.one_wire_bit_read(handle, gpio) << j
|
86
|
+
comp |= LGPIO.one_wire_bit_read(handle, gpio) << j
|
87
|
+
|
88
|
+
# A set (1) mask bit means we're searching a branch with that bit set.
|
89
|
+
# Force it to be 1 on this pass. Write 1 to both the bus and address bit.
|
90
|
+
#
|
91
|
+
# Don't change the complement bit from 0, Even if the bus said 0/0,
|
92
|
+
# send back 1/0, hiding known discrepancies, only sending new ones.
|
93
|
+
#
|
94
|
+
# Mask is a 64-bit number, not byte array.
|
95
|
+
if ((mask >> (i*8 + j)) & 0b1) == 1
|
96
|
+
LGPIO.one_wire_bit_write(handle, gpio, 1)
|
97
|
+
addr |= 1 << j
|
98
|
+
|
99
|
+
# Whether there was no "1-branch" marked for this bit, or there is no
|
100
|
+
# discrepancy at all, just echo address bit to the bus. We will
|
101
|
+
# compare addr/comp to find discrepancies for future passes.
|
102
|
+
else
|
103
|
+
LGPIO.one_wire_bit_write(handle, gpio, (addr >> j) & 0b1)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
bytes << addr
|
107
|
+
bytes << comp
|
108
|
+
end
|
109
|
+
|
110
|
+
# 16 bytes, address and complement bytes interleaved LSByteFIRST.
|
111
|
+
# DON'T CHANGE! #split_search_result deals with it. denko uses #search_pass
|
112
|
+
# directly on Linux, but MCUs send it this format to save RAM. Always match it.
|
113
|
+
return bytes
|
114
|
+
end
|
115
|
+
|
116
|
+
def parse_search_result(result)
|
117
|
+
address, complement = split_search_result(result)
|
118
|
+
|
119
|
+
raise "OneWire device not connected, or disconnected during search" if (address & complement) > 0
|
120
|
+
raise "CRC error during OneWire search" unless OneWire.crc(address)
|
121
|
+
|
122
|
+
# Gives 0 at every discrepancy we didn't write 1 for on this pass.
|
123
|
+
new_discrepancies = address ^ complement
|
124
|
+
|
125
|
+
high_discrepancy = -1
|
126
|
+
(0..63).each { |i| high_discrepancy = i if new_discrepancies[i] == 0 }
|
127
|
+
|
128
|
+
[address, high_discrepancy]
|
129
|
+
end
|
130
|
+
|
131
|
+
# Convert interleaved address/complement bytes to 64-bit numbers.
|
132
|
+
def split_search_result(data)
|
133
|
+
address = 0
|
134
|
+
complement = 0
|
135
|
+
data.reverse.each_slice(2) do |comp_byte, addr_byte|
|
136
|
+
address = (address << 8) | addr_byte
|
137
|
+
complement = (complement << 8) | comp_byte
|
138
|
+
end
|
139
|
+
[address, complement]
|
140
|
+
end
|
141
|
+
|
142
|
+
# CRC Class Methods
|
143
|
+
def self.crc(data)
|
144
|
+
calculated, received = self.calculate_crc(data)
|
145
|
+
calculated == received
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.calculate_crc(data)
|
149
|
+
if data.class == Integer
|
150
|
+
bytes = address_to_bytes(data)
|
151
|
+
else
|
152
|
+
bytes = data
|
153
|
+
end
|
154
|
+
|
155
|
+
crc = 0b00000000
|
156
|
+
bytes.take(bytes.length - 1).each do |byte|
|
157
|
+
for bit in (0..7)
|
158
|
+
xor = byte[bit] ^ crc[0]
|
159
|
+
crc = crc ^ ((xor * (2 ** 3)) | (xor * (2 ** 4)))
|
160
|
+
crc = crc >> 1
|
161
|
+
crc = crc | (xor * (2 ** 7))
|
162
|
+
end
|
163
|
+
end
|
164
|
+
[crc, bytes.last]
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.address_to_bytes(address)
|
168
|
+
[address].pack('Q<').split("").map(&:ord)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module LGPIO
|
2
|
+
class PositionalServo < HardwarePWM
|
3
|
+
FREQUENCY = 50
|
4
|
+
|
5
|
+
attr_reader :angle
|
6
|
+
|
7
|
+
def initialize(chip, channel, min_us, max_us, min_angle, max_angle)
|
8
|
+
super(chip, channel, frequency: FREQUENCY)
|
9
|
+
|
10
|
+
raise "min_us: #{min_us} cannot be lower than max_us: #{max_us}" if max_us < min_us
|
11
|
+
@min_us = min_us
|
12
|
+
@max_us = max_us
|
13
|
+
@us_range = @max_us - @min_us
|
14
|
+
|
15
|
+
@min_angle = min_angle
|
16
|
+
@max_angle = max_angle
|
17
|
+
end
|
18
|
+
|
19
|
+
def angle=(a)
|
20
|
+
ratio = (a - @min_angle).to_f / (@max_angle - @min_angle)
|
21
|
+
raise "angle: #{a} outside servo range" if (ratio < 0) || (ratio > 1)
|
22
|
+
|
23
|
+
d_us = (@us_range * ratio) + @min_us
|
24
|
+
self.duty_us = d_us
|
25
|
+
@angle = a
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module LGPIO
|
2
|
+
class SPIBitBang
|
3
|
+
attr_reader :handle, :clock, :input, :output
|
4
|
+
|
5
|
+
def initialize(options={})
|
6
|
+
@handle = options[:handle]
|
7
|
+
@clock = options[:clock] || options[:sck] || options[:clk]
|
8
|
+
@input = options[:input] || options[:poci] || options[:miso]
|
9
|
+
@output = options[:output] || options[:pico] || options[:mosi]
|
10
|
+
|
11
|
+
raise ArgumentError, "a gpiochip :handle is required" unless @handle
|
12
|
+
raise ArgumentError, "either input/:poci/:miso OR :output/:pico/:mosi pin required" unless (@input || @output)
|
13
|
+
raise ArgumentError, ":clock/:sck/:clk pin required" unless @clock
|
14
|
+
|
15
|
+
@output_state = nil
|
16
|
+
initialize_pins
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize_pins
|
20
|
+
LGPIO.gpio_claim_output(handle, LGPIO::SET_PULL_NONE, clock, LGPIO::LOW)
|
21
|
+
LGPIO.gpio_claim_input(handle, LGPIO::SET_PULL_NONE, input) if input
|
22
|
+
LGPIO.gpio_claim_output(handle, LGPIO::SET_PULL_NONE, output, LGPIO::LOW) if output
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_output(level)
|
26
|
+
return if (level == @output_state)
|
27
|
+
LGPIO.gpio_write(handle, output, @output_state = level)
|
28
|
+
end
|
29
|
+
|
30
|
+
def transfer_bit(write_bit, reading: false, mode: 0)
|
31
|
+
case mode
|
32
|
+
when 0
|
33
|
+
set_output(write_bit) if write_bit
|
34
|
+
LGPIO.gpio_write(handle, clock, 1)
|
35
|
+
read_bit = LGPIO.gpio_read(handle, input) if reading
|
36
|
+
LGPIO.gpio_write(handle, clock, 0)
|
37
|
+
when 1
|
38
|
+
LGPIO.gpio_write(handle, clock, 1)
|
39
|
+
set_output(write_bit) if write_bit
|
40
|
+
LGPIO.gpio_write(handle, clock, 0)
|
41
|
+
read_bit = LGPIO.gpio_read(handle, input) if reading
|
42
|
+
when 2
|
43
|
+
set_output(write_bit) if write_bit
|
44
|
+
LGPIO.gpio_write(handle, clock, 0)
|
45
|
+
read_bit = LGPIO.gpio_read(handle, input) if reading
|
46
|
+
LGPIO.gpio_write(handle, clock, 1)
|
47
|
+
when 3
|
48
|
+
LGPIO.gpio_write(handle, clock, 0)
|
49
|
+
set_output(write_bit) if write_bit
|
50
|
+
LGPIO.gpio_write(handle, clock, 1)
|
51
|
+
read_bit = LGPIO.gpio_read(handle, input) if reading
|
52
|
+
else
|
53
|
+
raise ArgumentError, "invalid SPI mode: #{mode} given"
|
54
|
+
end
|
55
|
+
return reading ? read_bit : nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def transfer_byte(byte, reading: false, order: :msbfirst, mode: 0)
|
59
|
+
read_byte = reading ? 0 : nil
|
60
|
+
|
61
|
+
if (order == :msbfirst)
|
62
|
+
i = 7
|
63
|
+
while i >= 0
|
64
|
+
write_bit = byte ? (byte >> i) & 0b1 : nil
|
65
|
+
read_bit = transfer_bit(write_bit, mode: mode, reading: reading)
|
66
|
+
read_byte = (read_byte << 1) | read_bit if reading
|
67
|
+
i = i - 1
|
68
|
+
end
|
69
|
+
else
|
70
|
+
i = 0
|
71
|
+
while i < 8
|
72
|
+
write_bit = byte ? (byte >> i) : nil
|
73
|
+
read_bit = transfer_bit(write_bit, mode: mode, reading: reading)
|
74
|
+
read_byte = read_byte | (read_bit << i) if reading
|
75
|
+
i = i + 1
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
read_byte
|
80
|
+
end
|
81
|
+
|
82
|
+
def transfer(write: [], read: 0, select: nil, order: :msbfirst, mode: 0)
|
83
|
+
# Idle clock state depends on SPI mode.
|
84
|
+
case mode
|
85
|
+
when 0..1
|
86
|
+
LGPIO.gpio_write(handle, clock, 0)
|
87
|
+
when 2..3
|
88
|
+
LGPIO.gpio_write(handle, clock, 1)
|
89
|
+
else
|
90
|
+
raise ArgumentError, "invalid SPI mode: #{mode} given"
|
91
|
+
end
|
92
|
+
raise ArgumentError, "invalid Array for write: #{write}" unless write.kind_of?(Array)
|
93
|
+
raise ArgumentError, "invalid Integer for read: #{read}" unless read.kind_of?(Integer)
|
94
|
+
|
95
|
+
read_bytes = (read > 0) ? [] : nil
|
96
|
+
LGPIO.gpio_write(handle, select, 0) if select
|
97
|
+
|
98
|
+
i = 0
|
99
|
+
while (i < read) || (i < write.length)
|
100
|
+
read_byte = transfer_byte(write[i], reading: (i < read), order: order, mode: mode)
|
101
|
+
read_bytes << read_byte if read_byte
|
102
|
+
i = i + 1
|
103
|
+
end
|
104
|
+
|
105
|
+
LGPIO.gpio_write(handle, select, 1) if select
|
106
|
+
read_bytes
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/lib/lgpio/version.rb
CHANGED
data/lib/lgpio.rb
CHANGED
@@ -1,4 +1,13 @@
|
|
1
1
|
require_relative 'lgpio/lgpio'
|
2
|
+
require_relative 'lgpio/version'
|
3
|
+
|
4
|
+
require_relative 'lgpio/i2c_bitbang'
|
5
|
+
require_relative 'lgpio/spi_bitbang'
|
6
|
+
require_relative 'lgpio/one_wire'
|
7
|
+
|
8
|
+
require_relative 'lgpio/hardware_pwm'
|
9
|
+
require_relative 'lgpio/positional_servo'
|
10
|
+
require_relative 'lgpio/infrared'
|
2
11
|
|
3
12
|
module LGPIO
|
4
13
|
LOW = 0
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lgpio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- vickash
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Use GPIO / PWM / I2C / SPI / UART on Linux SBCs in Ruby
|
14
14
|
email: mail@vickash.com
|
@@ -24,18 +24,31 @@ files:
|
|
24
24
|
- examples/bench_in.rb
|
25
25
|
- examples/bench_out.rb
|
26
26
|
- examples/blink.rb
|
27
|
+
- examples/dht.rb
|
27
28
|
- examples/group_in.rb
|
28
29
|
- examples/group_out.rb
|
30
|
+
- examples/hcsr04.rb
|
29
31
|
- examples/i2c_aht10.rb
|
30
32
|
- examples/i2c_aht10_zip.rb
|
33
|
+
- examples/i2c_bitbang-rb_aht10.rb
|
34
|
+
- examples/i2c_bitbang-rb_ssd1306_bench.rb
|
35
|
+
- examples/i2c_bitbang_aht10.rb
|
36
|
+
- examples/i2c_bitbang_search.rb
|
37
|
+
- examples/i2c_bitbang_ssd1306_bench.rb
|
31
38
|
- examples/i2c_ssd1306_bench.rb
|
39
|
+
- examples/infrared.rb
|
32
40
|
- examples/momentary.rb
|
41
|
+
- examples/one_wire_ds18b20.rb
|
42
|
+
- examples/one_wire_search.rb
|
33
43
|
- examples/pwm.rb
|
34
44
|
- examples/reports.rb
|
35
45
|
- examples/rotary_encoder.rb
|
36
46
|
- examples/rotary_encoder_led.rb
|
47
|
+
- examples/servo.rb
|
48
|
+
- examples/spi_bitbang_loopback.rb
|
49
|
+
- examples/spi_bitbang_ssd1306_bench.rb
|
50
|
+
- examples/spi_bitbang_ssd1306_sim_bench.rb
|
37
51
|
- examples/spi_loopback.rb
|
38
|
-
- examples/spi_read.rb
|
39
52
|
- examples/spi_ws2812.rb
|
40
53
|
- examples/spi_ws2812_bounce.rb
|
41
54
|
- examples/wave.rb
|
@@ -43,6 +56,12 @@ files:
|
|
43
56
|
- ext/lgpio/lgpio.c
|
44
57
|
- lgpio.gemspec
|
45
58
|
- lib/lgpio.rb
|
59
|
+
- lib/lgpio/hardware_pwm.rb
|
60
|
+
- lib/lgpio/i2c_bitbang.rb
|
61
|
+
- lib/lgpio/infrared.rb
|
62
|
+
- lib/lgpio/one_wire.rb
|
63
|
+
- lib/lgpio/positional_servo.rb
|
64
|
+
- lib/lgpio/spi_bitbang.rb
|
46
65
|
- lib/lgpio/version.rb
|
47
66
|
homepage: https://github.com/denko-rb/lgpio
|
48
67
|
licenses:
|
data/examples/spi_read.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'lgpio'
|
2
|
-
|
3
|
-
SPI_DEV = 1
|
4
|
-
SPI_CHAN = 0
|
5
|
-
SPI_MODE = 0
|
6
|
-
SPI_BAUD = 1_000_000
|
7
|
-
|
8
|
-
spi_handle = LGPIO.spi_open(SPI_DEV, SPI_CHAN, SPI_BAUD, SPI_MODE)
|
9
|
-
|
10
|
-
# Pull MISO high or low. High gives array of 255, low array of 0.
|
11
|
-
rx_bytes = LGPIO.spi_read(spi_handle, 8)
|
12
|
-
puts "RX bytes: #{rx_bytes.inspect}"
|
13
|
-
|
14
|
-
LGPIO.spi_close(spi_handle)
|