lgpio 0.1.5 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +42 -28
- data/examples/dht.rb +47 -0
- data/examples/{bench_in.rb → gpio_bench_in.rb} +1 -1
- data/examples/{bench_out.rb → gpio_bench_out.rb} +1 -1
- data/examples/{blink.rb → gpio_blink.rb} +1 -1
- data/examples/{group_in.rb → gpio_group_in.rb} +2 -2
- data/examples/{group_out.rb → gpio_group_out.rb} +1 -1
- data/examples/{momentary.rb → gpio_momentary.rb} +2 -2
- data/examples/{reports.rb → gpio_reports.rb} +1 -1
- data/examples/{rotary_encoder.rb → gpio_rotary_encoder.rb} +3 -3
- data/examples/{rotary_encoder_led.rb → gpio_rotary_encoder_led.rb} +4 -4
- data/examples/{wave.rb → gpio_wave.rb} +1 -1
- data/examples/hcsr04.rb +32 -0
- data/examples/i2c_bb_aht10.rb +40 -0
- data/examples/i2c_bb_search.rb +19 -0
- data/examples/i2c_bb_ssd1306_bench.rb +35 -0
- data/examples/{i2c_aht10.rb → i2c_hw_aht10.rb} +2 -1
- data/examples/{i2c_aht10_zip.rb → i2c_hw_aht10_zip.rb} +2 -1
- data/examples/{i2c_ssd1306_bench.rb → i2c_hw_ssd1306_bench.rb} +6 -5
- data/examples/infrared.rb +22 -0
- data/examples/one_wire_ds18b20.rb +30 -0
- data/examples/one_wire_search.rb +15 -0
- data/examples/pwm_hw_bench.rb +20 -0
- data/examples/{pwm.rb → pwm_sw.rb} +1 -1
- data/examples/spi_bb_loopback.rb +25 -0
- data/examples/spi_bb_sim_bench.rb +48 -0
- data/examples/spi_bb_ssd1306_bench.rb +51 -0
- data/ext/lgpio/lgpio.c +289 -11
- data/lib/lgpio/hardware_pwm.rb +18 -5
- 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/spi_bitbang.rb +109 -0
- data/lib/lgpio/version.rb +1 -1
- data/lib/lgpio.rb +6 -0
- metadata +36 -21
- data/examples/spi_read.rb +0 -14
- /data/examples/{servo.rb → pwm_hw_servo.rb} +0 -0
- /data/examples/{spi_loopback.rb → spi_hw_loopback.rb} +0 -0
- /data/examples/{spi_ws2812.rb → spi_hw_ws2812.rb} +0 -0
- /data/examples/{spi_ws2812_bounce.rb → spi_hw_ws2812_bounce.rb} +0 -0
@@ -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,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,7 +1,13 @@
|
|
1
1
|
require_relative 'lgpio/lgpio'
|
2
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
|
+
|
3
8
|
require_relative 'lgpio/hardware_pwm'
|
4
9
|
require_relative 'lgpio/positional_servo'
|
10
|
+
require_relative 'lgpio/infrared'
|
5
11
|
|
6
12
|
module LGPIO
|
7
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.7
|
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
|
@@ -21,31 +21,46 @@ files:
|
|
21
21
|
- LICENSE
|
22
22
|
- README.md
|
23
23
|
- Rakefile
|
24
|
-
- examples/
|
25
|
-
- examples/
|
26
|
-
- examples/
|
27
|
-
- examples/
|
28
|
-
- examples/
|
29
|
-
- examples/
|
30
|
-
- examples/
|
31
|
-
- examples/
|
32
|
-
- examples/
|
33
|
-
- examples/
|
34
|
-
- examples/
|
35
|
-
- examples/
|
36
|
-
- examples/
|
37
|
-
- examples/
|
38
|
-
- examples/
|
39
|
-
- examples/
|
40
|
-
- examples/
|
41
|
-
- examples/
|
42
|
-
- examples/
|
24
|
+
- examples/dht.rb
|
25
|
+
- examples/gpio_bench_in.rb
|
26
|
+
- examples/gpio_bench_out.rb
|
27
|
+
- examples/gpio_blink.rb
|
28
|
+
- examples/gpio_group_in.rb
|
29
|
+
- examples/gpio_group_out.rb
|
30
|
+
- examples/gpio_momentary.rb
|
31
|
+
- examples/gpio_reports.rb
|
32
|
+
- examples/gpio_rotary_encoder.rb
|
33
|
+
- examples/gpio_rotary_encoder_led.rb
|
34
|
+
- examples/gpio_wave.rb
|
35
|
+
- examples/hcsr04.rb
|
36
|
+
- examples/i2c_bb_aht10.rb
|
37
|
+
- examples/i2c_bb_search.rb
|
38
|
+
- examples/i2c_bb_ssd1306_bench.rb
|
39
|
+
- examples/i2c_hw_aht10.rb
|
40
|
+
- examples/i2c_hw_aht10_zip.rb
|
41
|
+
- examples/i2c_hw_ssd1306_bench.rb
|
42
|
+
- examples/infrared.rb
|
43
|
+
- examples/one_wire_ds18b20.rb
|
44
|
+
- examples/one_wire_search.rb
|
45
|
+
- examples/pwm_hw_bench.rb
|
46
|
+
- examples/pwm_hw_servo.rb
|
47
|
+
- examples/pwm_sw.rb
|
48
|
+
- examples/spi_bb_loopback.rb
|
49
|
+
- examples/spi_bb_sim_bench.rb
|
50
|
+
- examples/spi_bb_ssd1306_bench.rb
|
51
|
+
- examples/spi_hw_loopback.rb
|
52
|
+
- examples/spi_hw_ws2812.rb
|
53
|
+
- examples/spi_hw_ws2812_bounce.rb
|
43
54
|
- ext/lgpio/extconf.rb
|
44
55
|
- ext/lgpio/lgpio.c
|
45
56
|
- lgpio.gemspec
|
46
57
|
- lib/lgpio.rb
|
47
58
|
- lib/lgpio/hardware_pwm.rb
|
59
|
+
- lib/lgpio/i2c_bitbang.rb
|
60
|
+
- lib/lgpio/infrared.rb
|
61
|
+
- lib/lgpio/one_wire.rb
|
48
62
|
- lib/lgpio/positional_servo.rb
|
63
|
+
- lib/lgpio/spi_bitbang.rb
|
49
64
|
- lib/lgpio/version.rb
|
50
65
|
homepage: https://github.com/denko-rb/lgpio
|
51
66
|
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)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|