lgpio 0.1.9 → 0.1.11

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 968e706762503c9d172b41ff4df8a9c17021af5b4a4cb4372eeaaa166c30f5ff
4
- data.tar.gz: 82d8221f8e28293f542c4bd1913f85addb567c217d071fdbb1e47451c3be1da1
3
+ metadata.gz: 3371c46b2f589524fba421f579bd0f779d5e006eb62ef6d7480e8ca39cdf7540
4
+ data.tar.gz: 704f472a31ca5d9711a300ce5ce05ce89f930dd2322192a7251ce8cdab8f5abe
5
5
  SHA512:
6
- metadata.gz: 8749280110629032e20ac94c203c512a699e90b58266f78f7c26a9305b8e22ef0f97822247744403c2723b7417838b5972c2618a65ebcb0ee2ae483cd0d12e41
7
- data.tar.gz: a9bbac8e2f8c80de6adf86249bf559b10c590234597a15d3bf6dce37bc3b60ce27856f2343bdc1fe885a1a78105401419e221eb0ae5a10fa404ec7fc9c90a4fd
6
+ metadata.gz: cf2c6d2fec7b57b371a69c11ba1e7fd66997c72581a19c09bc73dc224e59be6e7347450d2499d84af38b4b73465e97bcf6d3021975b9427fd7f52a729cad2b08
7
+ data.tar.gz: 8fe39d478e3453cf5d42452a59552660742b249b9f533fbec506dfde165c746fe2ab3dada38fbde8ea60e8567e57419a6393faa9236b97772954a9d96f619829
data/README.md CHANGED
@@ -38,7 +38,7 @@ These use the sysfs PWM interface, not lgpio C, but are a good fit for this gem.
38
38
  - Carrier generated by hardware PWM. Software modulated with monotonic clock timing.
39
39
  - Useful for sending infrared signals at 38kHz, for example.
40
40
 
41
- **Note:** Once hardware PWM is used on a pin, it stays PWM until reboot. The associated GPIO won't work.
41
+ **Note:** Once a pin is bound to hardware PWM in the device tree, it shouldn't be used as regular GPIO. Behavior is inconsistent across different hardware.
42
42
 
43
43
  ## Installation
44
44
  On Debian-based Linuxes (RaspberryPi OS, Armbian, DietPi etc.):
@@ -9,13 +9,19 @@ SOFT_RESET = [0xBA]
9
9
  INIT_AND_CALIBRATE = [0xE1, 0x08, 0x00]
10
10
  START_MEASUREMENT = [0xAC, 0x33, 0x00]
11
11
 
12
- GPIO_CHIP = 0
12
+ SCL_CHIP = 0
13
13
  SCL_PIN = 228
14
+ SDA_CHIP = 0
14
15
  SDA_PIN = 270
15
16
  ADDRESS = 0x38
16
17
 
17
- chip_handle = LGPIO.chip_open(GPIO_CHIP)
18
- i2c_bb = LGPIO::I2CBitBang.new(chip_handle, SCL_PIN, SDA_PIN)
18
+ scl_handle = LGPIO.chip_open(SCL_CHIP)
19
+ sda_handle = (SCL_CHIP == SDA_CHIP) ? scl_handle : LGPIO.chip_open(SDA_CHIP)
20
+ pin_hash = {
21
+ scl: { handle: scl_handle, line: SCL_PIN },
22
+ sda: { handle: sda_handle, line: SDA_PIN },
23
+ }
24
+ i2c_bb = LGPIO::I2CBitBang.new(pin_hash)
19
25
 
20
26
  # Startup sequence
21
27
  sleep(POWER_ON_DELAY)
@@ -24,11 +30,15 @@ sleep(RESET_DELAY)
24
30
  i2c_bb.write(ADDRESS, INIT_AND_CALIBRATE)
25
31
  sleep(COMMAND_DELAY)
26
32
 
27
- # Read and close
33
+ # Read
28
34
  i2c_bb.write(ADDRESS, START_MEASUREMENT)
29
35
  sleep(MEASURE_DELAY)
30
36
  bytes = i2c_bb.read(ADDRESS, DATA_LENGTH)
31
37
 
38
+ # Close
39
+ LGPIO.chip_close(scl_handle)
40
+ LGPIO.chip_close(sda_handle) unless (scl_handle == sda_handle)
41
+
32
42
  # Humidity uses the upper 4 bits of the shared byte as its lowest 4 bits.
33
43
  h_raw = ((bytes[1] << 16) | (bytes[2] << 8) | (bytes[3])) >> 4
34
44
  humidity = (h_raw.to_f / 2**20) * 100
@@ -1,13 +1,23 @@
1
1
  require 'lgpio'
2
2
 
3
- GPIO_CHIP = 0
3
+ SCL_CHIP = 0
4
4
  SCL_PIN = 228
5
+ SDA_CHIP = 0
5
6
  SDA_PIN = 270
6
7
 
7
- chip_handle = LGPIO.chip_open(GPIO_CHIP)
8
- i2c_bb = LGPIO::I2CBitBang.new(chip_handle, SCL_PIN, SDA_PIN)
8
+ scl_handle = LGPIO.chip_open(SCL_CHIP)
9
+ sda_handle = (SCL_CHIP == SDA_CHIP) ? scl_handle : LGPIO.chip_open(SDA_CHIP)
10
+ pin_hash = {
11
+ scl: { handle: scl_handle, line: SCL_PIN },
12
+ sda: { handle: sda_handle, line: SDA_PIN },
13
+ }
14
+ i2c_bb = LGPIO::I2CBitBang.new(pin_hash)
15
+
9
16
  devices = i2c_bb.search
10
17
 
18
+ LGPIO.chip_close(scl_handle)
19
+ LGPIO.chip_close(sda_handle) unless (scl_handle == sda_handle)
20
+
11
21
  if devices.empty?
12
22
  puts "No devices found on I2C bus"
13
23
  else
@@ -5,13 +5,19 @@ START_ARRAY = [0, 33, 0, 127, 34, 0, 7]
5
5
  PATTERN_1 = [64] + Array.new(1024) { 0b00110011 }
6
6
  PATTERN_2 = [64] + Array.new(1024) { 0b11001100 }
7
7
 
8
- GPIO_CHIP = 0
8
+ SCL_CHIP = 0
9
9
  SCL_PIN = 228
10
+ SDA_CHIP = 0
10
11
  SDA_PIN = 270
11
12
  ADDRESS = 0x3C
12
13
 
13
- chip_handle = LGPIO.chip_open(GPIO_CHIP)
14
- i2c_bb = LGPIO::I2CBitBang.new(chip_handle, SCL_PIN, SDA_PIN)
14
+ scl_handle = LGPIO.chip_open(SCL_CHIP)
15
+ sda_handle = (SCL_CHIP == SDA_CHIP) ? scl_handle : LGPIO.chip_open(SDA_CHIP)
16
+ pin_hash = {
17
+ scl: { handle: scl_handle, line: SCL_PIN },
18
+ sda: { handle: sda_handle, line: SDA_PIN },
19
+ }
20
+ i2c_bb = LGPIO::I2CBitBang.new(pin_hash)
15
21
 
16
22
  i2c_bb.write(ADDRESS, INIT_ARRAY)
17
23
  FRAME_COUNT = 400
@@ -25,11 +31,12 @@ start = Time.now
25
31
  end
26
32
  finish = Time.now
27
33
 
28
- LGPIO.chip_close(chip_handle)
34
+ LGPIO.chip_close(scl_handle)
35
+ LGPIO.chip_close(sda_handle) unless (scl_handle == sda_handle)
29
36
 
30
37
  fps = FRAME_COUNT / (finish - start)
31
- # Also calculate C calls per second, using roughly 23 calls per byte written.
32
- cps = (START_ARRAY.length + ((PATTERN_1.length + PATTERN_2.length) / 2) + 2) * 23 * fps
38
+ # Also calculate C calls per second, using roughly 24.5 GPIO calls per byte written.
39
+ cps = (START_ARRAY.length + ((PATTERN_1.length + PATTERN_2.length) / 2) + 2) * 24.5 * fps
33
40
  cps = (cps / 1000.0).round
34
41
 
35
42
  puts "SSD1306 benchmark result: #{fps.round(2)} fps | #{cps}k C calls/s"
@@ -10,7 +10,12 @@ ORDERS = [:msbfirst, :lsbfirst]
10
10
  TX_BYTES = [0, 1, 2, 3, 4, 5, 6, 7]
11
11
 
12
12
  chip_handle = LGPIO.chip_open(GPIO_CHIP)
13
- spi_bb = LGPIO::SPIBitBang.new(handle: chip_handle, clock: CLOCK_PIN, input: INPUT_PIN, output: OUTPUT_PIN)
13
+ pin_hash = {
14
+ clock: { handle: chip_handle, line: CLOCK_PIN },
15
+ input: { handle: chip_handle, line: INPUT_PIN },
16
+ output: { handle: chip_handle, line: OUTPUT_PIN },
17
+ }
18
+ spi_bb = LGPIO::SPIBitBang.new(pin_hash)
14
19
 
15
20
  puts "TX bytes => #{TX_BYTES.inspect}"
16
21
 
@@ -12,7 +12,12 @@ PATTERN_2 = [0x3C << 1] + [64] + Array.new(1179) { 0b11001100 }
12
12
  FRAME_COUNT = 400
13
13
 
14
14
  chip_handle = LGPIO.chip_open(GPIO_CHIP)
15
- spi_bb = LGPIO::SPIBitBang.new(handle: chip_handle, clock: CLOCK_PIN, input: INPUT_PIN, output: OUTPUT_PIN)
15
+ pin_hash = {
16
+ clock: { handle: chip_handle, line: CLOCK_PIN },
17
+ input: { handle: chip_handle, line: INPUT_PIN },
18
+ output: { handle: chip_handle, line: OUTPUT_PIN },
19
+ }
20
+ spi_bb = LGPIO::SPIBitBang.new(pin_hash)
16
21
 
17
22
  start = Time.now
18
23
  (FRAME_COUNT / 2).times do
@@ -5,41 +5,53 @@ START_ARRAY = [0, 33, 0, 127, 34, 0, 7]
5
5
  PATTERN_1 = Array.new(1024) { 0b00110011 }
6
6
  PATTERN_2 = Array.new(1024) { 0b11001100 }
7
7
 
8
- GPIO_CHIP = 0
9
- CLOCK_PIN = 17
10
- OUTPUT_PIN = 27
11
- SELECT_PIN = 22
12
- RESET_PIN = 5
13
- DC_PIN = 6
14
-
15
- # Initialize
16
- chip_handle = LGPIO.chip_open(GPIO_CHIP)
17
- spi_bb = LGPIO::SPIBitBang.new(handle: chip_handle, clock: CLOCK_PIN, output: OUTPUT_PIN)
18
- LGPIO.gpio_claim_output(chip_handle, SELECT_PIN, LGPIO::SET_PULL_NONE, LGPIO::HIGH)
19
- LGPIO.gpio_claim_output(chip_handle, RESET_PIN, LGPIO::SET_PULL_NONE, LGPIO::LOW)
20
- LGPIO.gpio_claim_output(chip_handle, DC_PIN, LGPIO::SET_PULL_NONE, LGPIO::LOW)
8
+ # Pins for Radxa Zero 3W
9
+ # Demonstration of how to use GPIOs across multiple /dev/gpiochip devices.
10
+ pins = {
11
+ clock: { chip: 1, line: 4 },
12
+ output: { chip: 3, line: 4 },
13
+ reset: { chip: 3, line: 5 },
14
+ dc: { chip: 3, line: 6 },
15
+ cs: { chip: 3, line: 7 },
16
+ }
17
+
18
+ # Open chip handles without duplicating.
19
+ open_handles = []
20
+ pins.each_value do |hash|
21
+ if hash[:handle] = open_handles[hash[:chip]]
22
+ else
23
+ hash[:handle] = LGPIO.chip_open(hash[:chip])
24
+ open_handles[hash[:chip]] = hash[:handle]
25
+ end
26
+ end
27
+
28
+ spi_bb = LGPIO::SPIBitBang.new(clock: pins[:clock], output: pins[:output])
29
+ LGPIO.gpio_claim_output(pins[:reset][:handle], pins[:reset][:line], LGPIO::SET_PULL_NONE, LGPIO::LOW)
30
+ LGPIO.gpio_claim_output(pins[:dc][:handle], pins[:dc][:line], LGPIO::SET_PULL_NONE, LGPIO::LOW)
31
+ LGPIO.gpio_claim_output(pins[:cs][:handle], pins[:cs][:line], LGPIO::SET_PULL_NONE, LGPIO::HIGH)
21
32
 
22
33
  # OLED STARTUP
23
- LGPIO.gpio_write(chip_handle, RESET_PIN, 1)
24
- LGPIO.gpio_write(chip_handle, DC_PIN, 0)
25
- spi_bb.transfer(write: INIT_ARRAY, select: SELECT_PIN)
34
+ LGPIO.gpio_write(pins[:reset][:handle], pins[:reset][:line], 1)
35
+ LGPIO.gpio_write(pins[:dc][:handle], pins[:dc][:line], 0)
36
+ spi_bb.transfer(write: INIT_ARRAY, select: pins[:cs])
26
37
 
27
38
  FRAME_COUNT = 400
28
39
 
29
40
  start = Time.now
30
41
  (FRAME_COUNT / 2).times do
31
- LGPIO.gpio_write(chip_handle, DC_PIN, 0)
32
- spi_bb.transfer(write: START_ARRAY, select: SELECT_PIN)
33
- LGPIO.gpio_write(chip_handle, DC_PIN, 1)
34
- spi_bb.transfer(write: PATTERN_1, select: SELECT_PIN)
35
- LGPIO.gpio_write(chip_handle, DC_PIN, 0)
36
- spi_bb.transfer(write: START_ARRAY, select: SELECT_PIN)
37
- LGPIO.gpio_write(chip_handle, DC_PIN, 1)
38
- spi_bb.transfer(write: PATTERN_2, select: SELECT_PIN)
42
+ LGPIO.gpio_write(pins[:dc][:handle], pins[:dc][:line], 0)
43
+ spi_bb.transfer(write: START_ARRAY, select: pins[:cs])
44
+ LGPIO.gpio_write(pins[:dc][:handle], pins[:dc][:line], 1)
45
+ spi_bb.transfer(write: PATTERN_1, select: pins[:cs])
46
+ LGPIO.gpio_write(pins[:dc][:handle], pins[:dc][:line], 0)
47
+ spi_bb.transfer(write: START_ARRAY, select: pins[:cs])
48
+ LGPIO.gpio_write(pins[:dc][:handle], pins[:dc][:line], 1)
49
+ spi_bb.transfer(write: PATTERN_2, select: pins[:cs])
39
50
  end
40
51
  finish = Time.now
41
52
 
42
- LGPIO.chip_close(chip_handle)
53
+ # Close all handles.
54
+ open_handles.compact.each { |h| LGPIO.chip_close(h) }
43
55
 
44
56
  fps = FRAME_COUNT / (finish - start)
45
57
  # Also calculate C calls per second, using roughly 20 calls per byte written.
@@ -12,7 +12,7 @@ puts "TX bytes: #{tx_bytes.inspect}"
12
12
 
13
13
  # rx_bytes == tx_bytes if MOSI looped back to MISO.
14
14
  # rx_byte all 255 when MOSI and MISO not connected.
15
- rx_bytes = LGPIO.spi_xfer(spi_handle, tx_bytes)
15
+ rx_bytes = LGPIO.spi_xfer(spi_handle, tx_bytes, tx_bytes.length)
16
16
  puts "RX bytes: #{rx_bytes.inspect}"
17
17
 
18
18
  LGPIO.spi_close(spi_handle)
data/ext/lgpio/lgpio.c CHANGED
@@ -42,6 +42,11 @@ static void microDelay(uint64_t micros) {
42
42
  nanoDelay(micros * 1000);
43
43
  }
44
44
 
45
+ static VALUE rbMicroDelay(VALUE self, VALUE micros) {
46
+ microDelay(NUM2ULL(micros));
47
+ return Qnil;
48
+ }
49
+
45
50
  /*****************************************************************************/
46
51
  /* CHIP & GPIO */
47
52
  /*****************************************************************************/
@@ -86,6 +91,7 @@ static VALUE gpio_write(VALUE self, VALUE handle, VALUE gpio, VALUE level) {
86
91
  }
87
92
 
88
93
  static VALUE group_claim_input(VALUE self, VALUE handle, VALUE gpios, VALUE flags) {
94
+ Check_Type(gpios, T_ARRAY);
89
95
  int count = rb_array_len(gpios);
90
96
  int lgGpios[count];
91
97
  int i;
@@ -97,6 +103,7 @@ static VALUE group_claim_input(VALUE self, VALUE handle, VALUE gpios, VALUE flag
97
103
  }
98
104
 
99
105
  static VALUE group_claim_output(VALUE self, VALUE handle, VALUE gpios, VALUE flags, VALUE levels) {
106
+ Check_Type(gpios, T_ARRAY);
100
107
  int count = rb_array_len(gpios);
101
108
  int lgGpios[count];
102
109
  int lgLevels[count];
@@ -190,7 +197,7 @@ static VALUE tx_pulse(VALUE self, VALUE handle, VALUE gpio, VALUE on, VALUE off,
190
197
  }
191
198
 
192
199
  static VALUE tx_pwm(VALUE self, VALUE handle, VALUE gpio, VALUE freq, VALUE duty, VALUE offset, VALUE cycles) {
193
- int result = lgTxPwm(NUM2INT(handle), NUM2INT(gpio), NUM2INT(freq), NUM2INT(duty), NUM2INT(offset), NUM2INT(cycles));
200
+ int result = lgTxPwm(NUM2INT(handle), NUM2INT(gpio), NUM2INT(freq), NUM2DBL(duty), NUM2INT(offset), NUM2INT(cycles));
194
201
  return INT2NUM(result);
195
202
  }
196
203
 
@@ -201,6 +208,7 @@ static VALUE tx_servo(VALUE self, VALUE handle, VALUE gpio, VALUE width, VALUE f
201
208
 
202
209
  static VALUE tx_wave(VALUE self, VALUE handle, VALUE lead_gpio, VALUE pulses) {
203
210
  // Copy Ruby array to array of lgPulse_t.
211
+ Check_Type(pulses, T_ARRAY);
204
212
  int pulseCount = rb_array_len(pulses);
205
213
  lgPulse_t pulsesOut[pulseCount];
206
214
  VALUE rbPulse;
@@ -225,9 +233,10 @@ static VALUE tx_wave_ook(VALUE self, VALUE dutyPath, VALUE dutyString, VALUE pul
225
233
  // The Ruby class LGPIO::HardwarePWM should have already set the PWM carrier frequency.
226
234
  //
227
235
  // Convert pulses from microseconds to nanoseconds.
236
+ Check_Type(pulses, T_ARRAY);
228
237
  uint32_t pulseCount = rb_array_len(pulses);
229
238
  uint64_t nanoPulses[pulseCount];
230
- for (int i=0; i<pulseCount; i++) {
239
+ for (uint32_t i=0; i<pulseCount; i++) {
231
240
  nanoPulses[i] = NUM2UINT(rb_ary_entry(pulses, i)) * 1000;
232
241
  }
233
242
 
@@ -242,7 +251,7 @@ static VALUE tx_wave_ook(VALUE self, VALUE dutyPath, VALUE dutyString, VALUE pul
242
251
  const char *cDuty = StringValueCStr(dutyString);
243
252
 
244
253
  // Toggle duty cycle between given value and 0, to modulate the PWM carrier.
245
- for (int i=0; i<pulseCount; i++) {
254
+ for (uint32_t i=0; i<pulseCount; i++) {
246
255
  if (i % 2 == 0) {
247
256
  dutyFile = fopen(filePath, "w");
248
257
  fputs(cDuty, dutyFile);
@@ -259,6 +268,7 @@ static VALUE tx_wave_ook(VALUE self, VALUE dutyPath, VALUE dutyString, VALUE pul
259
268
  dutyFile = fopen(filePath, "w");
260
269
  fputs("0", dutyFile);
261
270
  fclose(dutyFile);
271
+ return UINT2NUM(pulseCount);
262
272
  }
263
273
 
264
274
  /*****************************************************************************/
@@ -274,12 +284,13 @@ static VALUE i2c_close(VALUE self, VALUE handle){
274
284
  return INT2NUM(result);
275
285
  }
276
286
 
277
- static VALUE i2c_write_device(VALUE self, VALUE handle, VALUE byteArray){
278
- int count = RARRAY_LEN(byteArray);
287
+ static VALUE i2c_write_device(VALUE self, VALUE handle, VALUE txArray){
288
+ Check_Type(txArray, T_ARRAY);
289
+ int count = rb_array_len(txArray);
279
290
  uint8_t txBuf[count];
280
291
  VALUE currentByte;
281
292
  for(int i=0; i<count; i++){
282
- currentByte = rb_ary_entry(byteArray, i);
293
+ currentByte = rb_ary_entry(txArray, i);
283
294
  Check_Type(currentByte, T_FIXNUM);
284
295
  txBuf[i] = NUM2CHR(currentByte);
285
296
  }
@@ -303,7 +314,8 @@ static VALUE i2c_read_device(VALUE self, VALUE handle, VALUE count){
303
314
  }
304
315
 
305
316
  static VALUE i2c_zip(VALUE self, VALUE handle, VALUE txArray, VALUE rb_rxCount){
306
- int txCount = RARRAY_LEN(txArray);
317
+ Check_Type(txArray, T_ARRAY);
318
+ int txCount = rb_array_len(txArray);
307
319
  uint8_t txBuf[txCount];
308
320
  VALUE currentByte;
309
321
  for(int i=0; i<txCount; i++){
@@ -357,7 +369,8 @@ static VALUE spi_read(VALUE self, VALUE handle, VALUE rxCount){
357
369
  }
358
370
 
359
371
  static VALUE spi_write(VALUE self, VALUE handle, VALUE txArray){
360
- int count = RARRAY_LEN(txArray);
372
+ Check_Type(txArray, T_ARRAY);
373
+ int count = rb_array_len(txArray);
361
374
  uint8_t txBuf[count];
362
375
  VALUE currentByte;
363
376
  for(int i=0; i<count; i++){
@@ -370,31 +383,42 @@ static VALUE spi_write(VALUE self, VALUE handle, VALUE txArray){
370
383
  return INT2NUM(result);
371
384
  }
372
385
 
373
- static VALUE spi_xfer(VALUE self, VALUE handle, VALUE txArray){
374
- int count = RARRAY_LEN(txArray);
386
+ static VALUE spi_xfer(VALUE self, VALUE handle, VALUE txArray, VALUE rxLength){
387
+ Check_Type(txArray, T_ARRAY);
388
+ int txCount = rb_array_len(txArray);
389
+ int rxCount = NUM2INT(rxLength);
390
+ int count = txCount;
391
+ if (rxCount > txCount) count = rxCount;
392
+
375
393
  uint8_t txBuf[count];
394
+ // Not sure if this needs null termination like I2C. +1 won't hurt.
395
+ uint8_t rxBuf[count+1];
396
+
397
+ // Add given bytes to transmit.
376
398
  VALUE currentByte;
377
- for(int i=0; i<count; i++){
399
+ for(int i=0; i<txCount; i++){
378
400
  currentByte = rb_ary_entry(txArray, i);
379
401
  Check_Type(currentByte, T_FIXNUM);
380
402
  txBuf[i] = NUM2CHR(currentByte);
381
403
  }
382
-
383
- // Not sure if this needs null termination like I2C. +1 won't hurt.
384
- uint8_t rxBuf[count+1];
404
+ // Extend with zeroes if reading more than writing.
405
+ if (count > txCount) {
406
+ for(int i=txCount; i<count; i++) txBuf[i] = 0;
407
+ }
385
408
 
386
409
  int result = lgSpiXfer(NUM2INT(handle), txBuf, rxBuf, count);
387
410
  if(result < 0) return INT2NUM(result);
388
411
 
389
- VALUE retArray = rb_ary_new2(count);
390
- for(int i=0; i<count; i++){
412
+ VALUE retArray = rb_ary_new2(rxCount);
413
+ for(int i=0; i<rxCount; i++){
391
414
  rb_ary_store(retArray, i, UINT2NUM(rxBuf[i]));
392
415
  }
393
416
  return retArray;
394
417
  }
395
418
 
396
419
  static VALUE spi_ws2812_write(VALUE self, VALUE handle, VALUE pixelArray){
397
- int count = RARRAY_LEN(pixelArray);
420
+ Check_Type(pixelArray, T_ARRAY);
421
+ int count = rb_array_len(pixelArray);
398
422
 
399
423
  // Pull low for at least one byte at 2.4 Mhz before data, and 90 after.
400
424
  int zeroesBefore = 1;
@@ -522,7 +546,7 @@ static VALUE gpio_read_pulses_us(VALUE self, VALUE rbHandle, VALUE rbGPIO, VALUE
522
546
  // Return Ruby array of pulse as microseconds
523
547
  if (pulseIndex == 0) return Qnil;
524
548
  VALUE retArray = rb_ary_new2(pulseIndex);
525
- for(int i=0; i<pulseIndex; i++){
549
+ for(uint32_t i=0; i<pulseIndex; i++){
526
550
  uint32_t pulse_us = round(pulses_ns[i] / 1000.0);
527
551
  rb_ary_store(retArray, i, UINT2NUM(pulse_us));
528
552
  }
@@ -598,6 +622,83 @@ static VALUE one_wire_reset(VALUE self, VALUE rbHandle, VALUE rbGPIO) {
598
622
  return UINT2NUM(presence);
599
623
  }
600
624
 
625
+ /*****************************************************************************/
626
+ /* Bit-Bang I2C Helpers */
627
+ /*****************************************************************************/
628
+ static uint8_t bitReadU8(uint8_t* b, uint8_t i) {
629
+ return (*b >> i) & 0b1;
630
+ }
631
+
632
+ static void bitWriteU8(uint8_t* b, uint8_t i, uint8_t v) {
633
+ if (v == 0) {
634
+ *b &= ~(1 << i);
635
+ } else {
636
+ *b |= (1 << i);
637
+ }
638
+ }
639
+
640
+ static void i2c_bb_set_sda(int handle, int gpio, uint8_t* state, uint8_t level) {
641
+ if (level == *state) return;
642
+ lgGpioWrite(handle, gpio, level);
643
+ *state = level;
644
+ }
645
+
646
+ static VALUE i2c_bb_read_byte(VALUE self, VALUE rbSCL_H, VALUE rbSCL_L, VALUE rbSDA_H, VALUE rbSDA_L, VALUE rbACK) {
647
+ int scl_H = NUM2INT(rbSCL_H);
648
+ int scl_L = NUM2INT(rbSCL_L);
649
+ int sda_H = NUM2INT(rbSDA_H);
650
+ int sda_L = NUM2INT(rbSDA_L);
651
+ uint8_t ack = RTEST(rbACK);
652
+
653
+ // Prevent caching by setting opposite to first state SDA will take.
654
+ uint8_t sdaState = 0;
655
+ uint8_t b;
656
+ uint8_t bit;
657
+
658
+ // Receive MSB first.
659
+ for (int i=7; i>=0; i--) {
660
+ i2c_bb_set_sda(sda_H, sda_L, &sdaState, 1);
661
+ lgGpioWrite(scl_H, scl_L, 1);
662
+ bit = lgGpioRead(sda_H, sda_L);
663
+ lgGpioWrite(scl_H, scl_L, 0);
664
+
665
+ bitWriteU8(&b, i, bit);
666
+ }
667
+
668
+ // Send ACK or NACK.
669
+ i2c_bb_set_sda(sda_H, sda_L, &sdaState, ack ^ 0b1);
670
+ lgGpioWrite(scl_H, scl_L, 1);
671
+ lgGpioWrite(scl_H, scl_L, 0);
672
+
673
+ return UINT2NUM(b);
674
+ }
675
+
676
+ static VALUE i2c_bb_write_byte(VALUE self, VALUE rbSCL_H, VALUE rbSCL_L, VALUE rbSDA_H, VALUE rbSDA_L, VALUE rbByte) {
677
+ int scl_H = NUM2INT(rbSCL_H);
678
+ int scl_L = NUM2INT(rbSCL_L);
679
+ int sda_H = NUM2INT(rbSDA_H);
680
+ int sda_L = NUM2INT(rbSDA_L);
681
+ uint8_t b = NUM2UINT(rbByte);
682
+
683
+ // Prevent caching by setting opposite to first state SDA will take.
684
+ uint8_t sdaState = bitReadU8(&b, 7) ^ 0b1;
685
+ uint8_t ack;
686
+
687
+ // Write MSBFIRST.
688
+ for (int i=7; i>=0; i--) {
689
+ i2c_bb_set_sda(sda_H, sda_L, &sdaState, bitReadU8(&b, i));
690
+ lgGpioWrite(scl_H, scl_L, 1);
691
+ lgGpioWrite(scl_H, scl_L, 0);
692
+ }
693
+
694
+ // Read and return ACK.
695
+ i2c_bb_set_sda(sda_H, sda_L, &sdaState, 1);
696
+ lgGpioWrite(scl_H, scl_L, 1);
697
+ ack = lgGpioRead(sda_H, sda_L);
698
+ lgGpioWrite(scl_H, scl_L, 0);
699
+ return (ack == 0) ? Qtrue : Qfalse;
700
+ }
701
+
601
702
  /*****************************************************************************/
602
703
  /* EXTENSION INIT */
603
704
  /*****************************************************************************/
@@ -605,7 +706,7 @@ void Init_lgpio(void) {
605
706
  // Modules
606
707
  VALUE mLGPIO = rb_define_module("LGPIO");
607
708
 
608
- // Basics
709
+ // Constants
609
710
  rb_define_const(mLGPIO, "SET_ACTIVE_LOW", INT2NUM(LG_SET_ACTIVE_LOW));
610
711
  rb_define_const(mLGPIO, "SET_OPEN_DRAIN", INT2NUM(LG_SET_OPEN_DRAIN));
611
712
  rb_define_const(mLGPIO, "SET_OPEN_SOURCE", INT2NUM(LG_SET_OPEN_SOURCE));
@@ -615,6 +716,11 @@ void Init_lgpio(void) {
615
716
  rb_define_const(mLGPIO, "RISING_EDGE", INT2NUM(LG_RISING_EDGE));
616
717
  rb_define_const(mLGPIO, "FALLING_EDGE", INT2NUM(LG_FALLING_EDGE));
617
718
  rb_define_const(mLGPIO, "BOTH_EDGES", INT2NUM(LG_BOTH_EDGES));
719
+
720
+ // Generic Helpers
721
+ rb_define_singleton_method(mLGPIO, "micro_delay", rbMicroDelay, 1);
722
+
723
+ // Basic GPIO
618
724
  rb_define_singleton_method(mLGPIO, "chip_open", chip_open, 1);
619
725
  rb_define_singleton_method(mLGPIO, "chip_close", chip_close, 1);
620
726
  rb_define_singleton_method(mLGPIO, "gpio_get_mode", gpio_get_mode, 2);
@@ -648,23 +754,23 @@ void Init_lgpio(void) {
648
754
  // Don't use this. Servo will jitter.
649
755
  rb_define_singleton_method(mLGPIO, "tx_servo", tx_servo, 6);
650
756
 
651
- // Hardware PWM waves for on-off-keying.
757
+ // HW PWM waves, on-off-keying only.
652
758
  VALUE cHardwarePWM = rb_define_class_under(mLGPIO, "HardwarePWM", rb_cObject);
653
759
  rb_define_method(cHardwarePWM, "tx_wave_ook", tx_wave_ook, 3);
654
760
 
655
- // I2C
761
+ // HW I2C
656
762
  rb_define_singleton_method(mLGPIO, "i2c_open", i2c_open, 3);
657
763
  rb_define_singleton_method(mLGPIO, "i2c_close", i2c_close, 1);
658
764
  rb_define_singleton_method(mLGPIO, "i2c_write_device", i2c_write_device, 2);
659
765
  rb_define_singleton_method(mLGPIO, "i2c_read_device", i2c_read_device, 2);
660
766
  rb_define_singleton_method(mLGPIO, "i2c_zip", i2c_zip, 3);
661
767
 
662
- // SPI
768
+ // HW SPI
663
769
  rb_define_singleton_method(mLGPIO, "spi_open", spi_open, 4);
664
770
  rb_define_singleton_method(mLGPIO, "spi_close", spi_close, 1);
665
771
  rb_define_singleton_method(mLGPIO, "spi_read", spi_read, 2);
666
772
  rb_define_singleton_method(mLGPIO, "spi_write", spi_write, 2);
667
- rb_define_singleton_method(mLGPIO, "spi_xfer", spi_xfer, 2);
773
+ rb_define_singleton_method(mLGPIO, "spi_xfer", spi_xfer, 3);
668
774
  rb_define_singleton_method(mLGPIO, "spi_ws2812_write", spi_ws2812_write, 2);
669
775
 
670
776
  // Bit-Bang Pulse Input
@@ -675,4 +781,8 @@ void Init_lgpio(void) {
675
781
  rb_define_singleton_method(mLGPIO, "one_wire_bit_read", one_wire_bit_read, 2);
676
782
  rb_define_singleton_method(mLGPIO, "one_wire_bit_write", one_wire_bit_write, 3);
677
783
  rb_define_singleton_method(mLGPIO, "one_wire_reset", one_wire_reset, 2);
784
+
785
+ // Bit-bang I2C Helpers
786
+ rb_define_singleton_method(mLGPIO, "i2c_bb_write_byte", i2c_bb_write_byte, 5);
787
+ rb_define_singleton_method(mLGPIO, "i2c_bb_read_byte", i2c_bb_read_byte, 5);
678
788
  }
data/lgpio.gemspec CHANGED
@@ -4,7 +4,7 @@ Gem::Specification.new do |s|
4
4
  s.name = 'lgpio'
5
5
  s.version = LGPIO::VERSION
6
6
  s.licenses = ['MIT']
7
- s.summary = "Use Linux GPIO, I2C, SPI and PWM in Ruby"
7
+ s.summary = "Ruby C extension for Linux GPIO"
8
8
  s.description = "Use Linux GPIO, I2C, SPI and PWM in Ruby"
9
9
 
10
10
  s.authors = ["vickash"]
@@ -13,5 +13,6 @@ Gem::Specification.new do |s|
13
13
  s.homepage = 'https://github.com/denko-rb/lgpio'
14
14
  s.metadata = { "source_code_uri" => "https://github.com/denko-rb/lgpio" }
15
15
 
16
+ s.required_ruby_version = '>=3'
16
17
  s.extensions = %w[ext/lgpio/extconf.rb]
17
18
  end
@@ -4,7 +4,7 @@ module LGPIO
4
4
  NS_PER_US = 1_000
5
5
  SYS_FS_PWM_PATH = "/sys/class/pwm/"
6
6
 
7
- attr_reader :period, :duty, :enabled
7
+ attr_reader :polarity, :period, :duty, :enabled
8
8
 
9
9
  def initialize(chip, channel, frequency: nil, period: nil)
10
10
  @chip = chip
@@ -16,6 +16,11 @@ module LGPIO
16
16
  end
17
17
 
18
18
  period ? self.period = period : self.frequency = frequency
19
+
20
+ # Default to with 0 duty cycle and normal polarity.
21
+ self.duty = 0
22
+ self.polarity = :normal
23
+
19
24
  enable
20
25
  end
21
26
 
@@ -23,6 +28,10 @@ module LGPIO
23
28
  @path ||= "#{SYS_FS_PWM_PATH}pwmchip#{@chip}/pwm#{@channel}/"
24
29
  end
25
30
 
31
+ def polarity_path
32
+ @polarity_path ||= "#{path}polarity"
33
+ end
34
+
26
35
  def period_path
27
36
  @period_path ||= "#{path}period"
28
37
  end
@@ -35,8 +44,24 @@ module LGPIO
35
44
  @enable_path ||= "#{path}enable"
36
45
  end
37
46
 
47
+ def polarity=(p=:normal)
48
+ if p == :inversed
49
+ File.open(polarity_path, 'w') { |f| f.write("inversed") }
50
+ @polarity = :inversed
51
+ else
52
+ File.open(polarity_path, 'w') { |f| f.write("normal") }
53
+ @polarity = :normal
54
+ end
55
+ end
56
+
38
57
  def frequency=(freq)
39
58
  self.period = (NS_PER_S / freq.to_f).round
59
+ @frequency = freq
60
+ end
61
+
62
+ def frequency
63
+ # If not set explicitly, calculate from period, rounded to nearest Hz.
64
+ @frequency ||= (1_000_000_000.0 / period).round
40
65
  end
41
66
 
42
67
  def period=(p)
@@ -45,6 +70,7 @@ module LGPIO
45
70
  File.open(duty_path, 'w') { |f| f.write("0") }
46
71
  end
47
72
  File.open(period_path, 'w') { |f| f.write(p) }
73
+ @frequency = nil
48
74
  @period = p
49
75
  end
50
76
 
@@ -2,24 +2,36 @@ module LGPIO
2
2
  class I2CBitBang
3
3
  VALID_ADDRESSES = (0x08..0x77).to_a
4
4
 
5
- attr_reader :handle, :scl, :sda
5
+ attr_reader :scl_handle, :scl_line, :sda_handle, :sda_line
6
6
 
7
- def initialize(handle, scl, sda)
8
- @handle = handle
9
- @scl = scl
10
- @sda = sda
11
- @sda_state = nil
7
+ def initialize(options={})
8
+ scl = options[:scl]
9
+ sda = options[:sda]
10
+
11
+ @scl_handle = scl[:handle] if scl
12
+ @scl_line = scl[:line] if scl
13
+ raise ArgumentError, ":scl pin required as Hash, with :handle and :line required" unless (@scl_handle && @scl_line)
14
+
15
+ @sda_handle = sda[:handle] if sda
16
+ @sda_line = sda[:line] if sda
17
+ raise ArgumentError, ":sda pin required as Hash, with :handle and :line required" unless (@sda_handle && @sda_line)
18
+
19
+ @sda_state = nil
12
20
  initialize_pins
13
21
  end
14
22
 
23
+ def config
24
+ @config ||= [scl_handle, scl_line, sda_handle, scl_line]
25
+ end
26
+
15
27
  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)
28
+ LGPIO.gpio_claim_output(scl_handle, scl_line, LGPIO::SET_PULL_NONE, LGPIO::HIGH)
29
+ LGPIO.gpio_claim_output(sda_handle, sda_line, LGPIO::SET_OPEN_DRAIN | LGPIO::SET_PULL_UP, LGPIO::HIGH)
18
30
  end
19
31
 
20
32
  def set_sda(value)
21
33
  return if (@sda_state == value)
22
- LGPIO.gpio_write(handle, sda, @sda_state = value)
34
+ LGPIO.gpio_write(sda_handle, sda_line, @sda_state = value)
23
35
  end
24
36
 
25
37
  def write_form(address)
@@ -31,28 +43,28 @@ module LGPIO
31
43
  end
32
44
 
33
45
  def start
34
- LGPIO.gpio_write(handle, sda, 0)
35
- LGPIO.gpio_write(handle, scl, 0)
46
+ LGPIO.gpio_write(sda_handle, sda_line, 0)
47
+ LGPIO.gpio_write(scl_handle, scl_line, 0)
36
48
  end
37
49
 
38
50
  def stop
39
- LGPIO.gpio_write(handle, sda, 0)
40
- LGPIO.gpio_write(handle, scl, 1)
41
- LGPIO.gpio_write(handle, sda, 1)
51
+ LGPIO.gpio_write(sda_handle, sda_line, 0)
52
+ LGPIO.gpio_write(scl_handle, scl_line, 1)
53
+ LGPIO.gpio_write(sda_handle, sda_line, 1)
42
54
  end
43
55
 
44
56
  def read_bit
45
57
  set_sda(1)
46
- LGPIO.gpio_write(handle, scl, 1)
47
- bit = LGPIO.gpio_read(handle, sda)
48
- LGPIO.gpio_write(handle, scl, 0)
58
+ LGPIO.gpio_write(scl_handle, scl_line, 1)
59
+ bit = LGPIO.gpio_read(sda_handle, sda_line)
60
+ LGPIO.gpio_write(scl_handle, scl_line, 0)
49
61
  bit
50
62
  end
51
63
 
52
64
  def write_bit(bit)
53
65
  set_sda(bit)
54
- LGPIO.gpio_write(handle, scl, 1)
55
- LGPIO.gpio_write(handle, scl, 0)
66
+ LGPIO.gpio_write(scl_handle, scl_line, 1)
67
+ LGPIO.gpio_write(scl_handle, scl_line, 0)
56
68
  end
57
69
 
58
70
  def read_byte(ack)
@@ -76,6 +88,14 @@ module LGPIO
76
88
  (read_bit == 0)
77
89
  end
78
90
 
91
+ def read_byte_c(ack)
92
+ LGPIO.i2c_bb_read_byte(@scl_handle, @scl_line, @sda_handle, @sda_line, ack)
93
+ end
94
+
95
+ def write_byte_c(byte)
96
+ LGPIO.i2c_bb_write_byte(@scl_handle, @scl_line, @sda_handle, @sda_line, byte)
97
+ end
98
+
79
99
  def read(address, length)
80
100
  raise ArgumentError, "invalid I2C address: #{address}. Range is 0x08..0x77" unless VALID_ADDRESSES.include?(address)
81
101
  raise ArgumentError, "invalid Integer for read length: #{length}" unless length.kind_of?(Integer)
@@ -86,10 +106,16 @@ module LGPIO
86
106
 
87
107
  # Read length bytes, and ACK for all but the last one.
88
108
  bytes = []
89
- (length-1).times { bytes << read_byte(true) }
90
- bytes << read_byte(false)
91
- stop
92
109
 
110
+ # Bit-bang per-byte in Ruby.
111
+ # (length-1).times { bytes << read_byte(true) }
112
+ # bytes << read_byte(false)
113
+
114
+ # Bit-bang per-byte in C.
115
+ (length-1).times { bytes << read_byte_c(true) }
116
+ bytes << read_byte_c(false)
117
+
118
+ stop
93
119
  bytes
94
120
  end
95
121
 
@@ -99,7 +125,13 @@ module LGPIO
99
125
 
100
126
  start
101
127
  write_byte(write_form(address))
102
- bytes.each { |byte| write_byte(byte) }
128
+
129
+ # Bit-bang per-byte in Ruby.
130
+ # bytes.each { |byte| write_byte(byte) }
131
+
132
+ # Bit-bang per-byte in C.
133
+ bytes.each { |byte| write_byte_c(byte) }
134
+
103
135
  stop
104
136
  end
105
137
 
@@ -19,7 +19,7 @@ module LGPIO
19
19
  @handle = handle
20
20
  @gpio = gpio
21
21
  @found_addresses = []
22
- LGPIO.gpio_claim_output(handle, LGPIO::SET_OPEN_DRAIN | LGPIO::SET_PULL_UP, gpio, LGPIO::HIGH)
22
+ LGPIO.gpio_claim_output(handle, gpio, LGPIO::SET_OPEN_DRAIN | LGPIO::SET_PULL_UP, LGPIO::HIGH)
23
23
  end
24
24
 
25
25
  def reset
@@ -1,58 +1,66 @@
1
1
  module LGPIO
2
2
  class SPIBitBang
3
- attr_reader :handle, :clock, :input, :output
3
+ attr_reader :clock_handle, :input_handle, :output_handle
4
+ attr_reader :clock_line, :input_line, :output_line
4
5
 
5
6
  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]
7
+ clock = options[:clock]
8
+ input = options[:input]
9
+ output = options[:output]
10
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
11
+ @clock_handle = clock[:handle] if clock
12
+ @clock_line = clock[:line] if clock
13
+ raise ArgumentError, ":clock pin required as Hash, with :handle and :line required" unless (@clock_handle && @clock_line)
14
+
15
+ @input_handle = input[:handle] if input
16
+ @input_line = input[:line] if input
17
+ @output_handle = output[:handle] if output
18
+ @output_line = output[:line] if output
19
+ unless ((@input_handle && @input_line) || (@output_handle && @output_line))
20
+ raise ArgumentError, "either :input or :output pin required as Hash, with :handle and :line required"
21
+ end
14
22
 
15
23
  @output_state = nil
16
24
  initialize_pins
17
25
  end
18
26
 
19
27
  def config
20
- @config ||= { handle: handle, clock: clock, input: input, output: output }
28
+ @config ||= [clock_handle, clock_line, input_handle, input_line, output_handle, output_line]
21
29
  end
22
30
 
23
31
  def initialize_pins
24
- LGPIO.gpio_claim_output(handle, LGPIO::SET_PULL_NONE, clock, LGPIO::LOW)
25
- LGPIO.gpio_claim_input(handle, LGPIO::SET_PULL_NONE, input) if input
26
- LGPIO.gpio_claim_output(handle, LGPIO::SET_PULL_NONE, output, LGPIO::LOW) if output
32
+ LGPIO.gpio_claim_output(clock_handle, clock_line, LGPIO::SET_PULL_NONE, LGPIO::LOW)
33
+ LGPIO.gpio_claim_input(input_handle, input_line, LGPIO::SET_PULL_NONE) if input_line
34
+ LGPIO.gpio_claim_output(output_handle, output_line, LGPIO::SET_PULL_NONE, LGPIO::LOW) if output_line
27
35
  end
28
36
 
29
37
  def set_output(level)
30
38
  return if (level == @output_state)
31
- LGPIO.gpio_write(handle, output, @output_state = level)
39
+ LGPIO.gpio_write(output_handle, output_line, @output_state = level)
32
40
  end
33
41
 
34
42
  def transfer_bit(write_bit, reading: false, mode: 0)
35
43
  case mode
36
44
  when 0
37
45
  set_output(write_bit) if write_bit
38
- LGPIO.gpio_write(handle, clock, 1)
39
- read_bit = LGPIO.gpio_read(handle, input) if reading
40
- LGPIO.gpio_write(handle, clock, 0)
46
+ LGPIO.gpio_write(clock_handle, clock_line, 1)
47
+ read_bit = LGPIO.gpio_read(input_handle, input_line) if reading
48
+ LGPIO.gpio_write(clock_handle, clock_line, 0)
41
49
  when 1
42
- LGPIO.gpio_write(handle, clock, 1)
50
+ LGPIO.gpio_write(clock_handle, clock_line, 1)
43
51
  set_output(write_bit) if write_bit
44
- LGPIO.gpio_write(handle, clock, 0)
45
- read_bit = LGPIO.gpio_read(handle, input) if reading
52
+ LGPIO.gpio_write(clock_handle, clock_line, 0)
53
+ read_bit = LGPIO.gpio_read(input_handle, input_line) if reading
46
54
  when 2
47
55
  set_output(write_bit) if write_bit
48
- LGPIO.gpio_write(handle, clock, 0)
49
- read_bit = LGPIO.gpio_read(handle, input) if reading
50
- LGPIO.gpio_write(handle, clock, 1)
56
+ LGPIO.gpio_write(clock_handle, clock_line, 0)
57
+ read_bit = LGPIO.gpio_read(input_handle, input_line) if reading
58
+ LGPIO.gpio_write(clock_handle, clock_line, 1)
51
59
  when 3
52
- LGPIO.gpio_write(handle, clock, 0)
60
+ LGPIO.gpio_write(clock_handle, clock_line, 0)
53
61
  set_output(write_bit) if write_bit
54
- LGPIO.gpio_write(handle, clock, 1)
55
- read_bit = LGPIO.gpio_read(handle, input) if reading
62
+ LGPIO.gpio_write(clock_handle, clock_line, 1)
63
+ read_bit = LGPIO.gpio_read(input_handle, input_line) if reading
56
64
  else
57
65
  raise ArgumentError, "invalid SPI mode: #{mode} given"
58
66
  end
@@ -87,9 +95,9 @@ module LGPIO
87
95
  # Idle clock state depends on SPI mode.
88
96
  case mode
89
97
  when 0..1
90
- LGPIO.gpio_write(handle, clock, 0)
98
+ LGPIO.gpio_write(clock_handle, clock_line, 0)
91
99
  when 2..3
92
- LGPIO.gpio_write(handle, clock, 1)
100
+ LGPIO.gpio_write(clock_handle, clock_line, 1)
93
101
  else
94
102
  raise ArgumentError, "invalid SPI mode: #{mode} given"
95
103
  end
@@ -97,7 +105,7 @@ module LGPIO
97
105
  raise ArgumentError, "invalid Integer for read: #{read}" unless read.kind_of?(Integer)
98
106
 
99
107
  read_bytes = (read > 0) ? [] : nil
100
- LGPIO.gpio_write(handle, select, 0) if select
108
+ LGPIO.gpio_write(select[:handle], select[:line], 0) if select
101
109
 
102
110
  i = 0
103
111
  while (i < read) || (i < write.length)
@@ -106,7 +114,7 @@ module LGPIO
106
114
  i = i + 1
107
115
  end
108
116
 
109
- LGPIO.gpio_write(handle, select, 1) if select
117
+ LGPIO.gpio_write(select[:handle], select[:line], 1) if select
110
118
  read_bytes
111
119
  end
112
120
  end
data/lib/lgpio/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module LGPIO
2
- VERSION = "0.1.9"
2
+ VERSION = "0.1.11"
3
3
  end
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.9
4
+ version: 0.1.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - vickash
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-28 00:00:00.000000000 Z
11
+ date: 2024-10-12 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Use Linux GPIO, I2C, SPI and PWM in Ruby
14
14
  email: mail@vickash.com
@@ -75,7 +75,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
75
75
  requirements:
76
76
  - - ">="
77
77
  - !ruby/object:Gem::Version
78
- version: '0'
78
+ version: '3'
79
79
  required_rubygems_version: !ruby/object:Gem::Requirement
80
80
  requirements:
81
81
  - - ">="
@@ -85,5 +85,5 @@ requirements: []
85
85
  rubygems_version: 3.5.20
86
86
  signing_key:
87
87
  specification_version: 4
88
- summary: Use Linux GPIO, I2C, SPI and PWM in Ruby
88
+ summary: Ruby C extension for Linux GPIO
89
89
  test_files: []