lgpio 0.1.5 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +42 -28
  3. data/examples/dht.rb +47 -0
  4. data/examples/{bench_in.rb → gpio_bench_in.rb} +1 -1
  5. data/examples/{bench_out.rb → gpio_bench_out.rb} +1 -1
  6. data/examples/{blink.rb → gpio_blink.rb} +1 -1
  7. data/examples/{group_in.rb → gpio_group_in.rb} +2 -2
  8. data/examples/{group_out.rb → gpio_group_out.rb} +1 -1
  9. data/examples/{momentary.rb → gpio_momentary.rb} +2 -2
  10. data/examples/{reports.rb → gpio_reports.rb} +1 -1
  11. data/examples/{rotary_encoder.rb → gpio_rotary_encoder.rb} +3 -3
  12. data/examples/{rotary_encoder_led.rb → gpio_rotary_encoder_led.rb} +4 -4
  13. data/examples/{wave.rb → gpio_wave.rb} +1 -1
  14. data/examples/hcsr04.rb +32 -0
  15. data/examples/i2c_bb_aht10.rb +40 -0
  16. data/examples/i2c_bb_search.rb +19 -0
  17. data/examples/i2c_bb_ssd1306_bench.rb +35 -0
  18. data/examples/{i2c_aht10.rb → i2c_hw_aht10.rb} +2 -1
  19. data/examples/{i2c_aht10_zip.rb → i2c_hw_aht10_zip.rb} +2 -1
  20. data/examples/{i2c_ssd1306_bench.rb → i2c_hw_ssd1306_bench.rb} +6 -5
  21. data/examples/infrared.rb +22 -0
  22. data/examples/one_wire_ds18b20.rb +30 -0
  23. data/examples/one_wire_search.rb +15 -0
  24. data/examples/pwm_hw_bench.rb +20 -0
  25. data/examples/{pwm.rb → pwm_sw.rb} +1 -1
  26. data/examples/spi_bb_loopback.rb +25 -0
  27. data/examples/spi_bb_sim_bench.rb +48 -0
  28. data/examples/spi_bb_ssd1306_bench.rb +51 -0
  29. data/ext/lgpio/lgpio.c +289 -11
  30. data/lib/lgpio/hardware_pwm.rb +18 -5
  31. data/lib/lgpio/i2c_bitbang.rb +117 -0
  32. data/lib/lgpio/infrared.rb +8 -0
  33. data/lib/lgpio/one_wire.rb +171 -0
  34. data/lib/lgpio/spi_bitbang.rb +109 -0
  35. data/lib/lgpio/version.rb +1 -1
  36. data/lib/lgpio.rb +6 -0
  37. metadata +36 -21
  38. data/examples/spi_read.rb +0 -14
  39. /data/examples/{servo.rb → pwm_hw_servo.rb} +0 -0
  40. /data/examples/{spi_loopback.rb → spi_hw_loopback.rb} +0 -0
  41. /data/examples/{spi_ws2812.rb → spi_hw_ws2812.rb} +0 -0
  42. /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
@@ -1,3 +1,3 @@
1
1
  module LGPIO
2
- VERSION = "0.1.5"
2
+ VERSION = "0.1.7"
3
3
  end
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.5
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-08-17 00:00:00.000000000 Z
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/bench_in.rb
25
- - examples/bench_out.rb
26
- - examples/blink.rb
27
- - examples/group_in.rb
28
- - examples/group_out.rb
29
- - examples/i2c_aht10.rb
30
- - examples/i2c_aht10_zip.rb
31
- - examples/i2c_ssd1306_bench.rb
32
- - examples/momentary.rb
33
- - examples/pwm.rb
34
- - examples/reports.rb
35
- - examples/rotary_encoder.rb
36
- - examples/rotary_encoder_led.rb
37
- - examples/servo.rb
38
- - examples/spi_loopback.rb
39
- - examples/spi_read.rb
40
- - examples/spi_ws2812.rb
41
- - examples/spi_ws2812_bounce.rb
42
- - examples/wave.rb
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