lgpio 0.1.10 → 0.1.12

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: 4da526bc3f239adf23035ca2c8923463667f979038b01fe0f8b63f0ad017a0e1
4
- data.tar.gz: 6beafa4839875a6aac25f78a0b4b3e4fa25a28569269254ec4dcea27bfc06572
3
+ metadata.gz: e3b3d476891ddae4745580531d10dc7c58de9ac0f7c93f75ad2743597db0fd74
4
+ data.tar.gz: df0d8c8b767115c07c1d9174cb9cf69797a04278035fb6fbc41be68b4877a585
5
5
  SHA512:
6
- metadata.gz: af9365d7c43fc0f7d5bab7c360e5a2e9325f2c3f7b7015ffff39d98ee3c4c2041123dfb81354d5d0113a06daab31910d18ed3fcee91dd75ded80c13b87543cba
7
- data.tar.gz: aef0e2f50840a3b601627283f15df63f1f6ab5d065288f05275c07d04e0ad32def28d055acedfbba807c16f16b041d2df3bd5444f093bbb55c604aac2026c4ac
6
+ metadata.gz: affc87dba05d69d20589acbcffb3988018a5f2759516d9b55a452dc8baa7c00de4c1cc61bc511590503063a06b1a41efadcf589dfcc628a53704cde9eeafe423
7
+ data.tar.gz: 2ea4c0c0f9ba1f602dcaff830543fd69ca6e1c6c0555fec0961362be3e51482a38210158776e94e54a445c9a17a29198a6d541ade0cdbc8863e80bd5112372e6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in denko.gemspec
4
+ gemspec
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.):
data/examples/dht.rb CHANGED
@@ -13,7 +13,7 @@ chip_handle = LGPIO.chip_open(GPIO_CHIP)
13
13
  # Maximum number of pulses to read
14
14
  # Timeout in milliseconds
15
15
  #
16
- data = LGPIO.gpio_read_pulses_us(chip_handle, DHT_PIN, 20_000, 0, 84, 100)
16
+ data = LGPIO.gpio_read_pulses_us(chip_handle, DHT_PIN, 10_000, 0, 84, 100)
17
17
 
18
18
  # Handle errors.
19
19
  raise "error: DHT sensor not connected" unless data
@@ -35,8 +35,8 @@ LGPIO.chip_close(scl_handle)
35
35
  LGPIO.chip_close(sda_handle) unless (scl_handle == sda_handle)
36
36
 
37
37
  fps = FRAME_COUNT / (finish - start)
38
- # Also calculate C calls per second, using roughly 23 calls per byte written.
39
- 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
40
40
  cps = (cps / 1000.0).round
41
41
 
42
42
  puts "SSD1306 benchmark result: #{fps.round(2)} fps | #{cps}k C calls/s"
@@ -25,8 +25,6 @@ pins.each_value do |hash|
25
25
  end
26
26
  end
27
27
 
28
- puts pins.inspect
29
-
30
28
  spi_bb = LGPIO::SPIBitBang.new(clock: pins[:clock], output: pins[:output])
31
29
  LGPIO.gpio_claim_output(pins[:reset][:handle], pins[:reset][:line], LGPIO::SET_PULL_NONE, LGPIO::LOW)
32
30
  LGPIO.gpio_claim_output(pins[:dc][:handle], pins[:dc][:line], LGPIO::SET_PULL_NONE, LGPIO::LOW)
@@ -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;
@@ -414,8 +438,8 @@ static VALUE spi_ws2812_write(VALUE self, VALUE handle, VALUE pixelArray){
414
438
  Check_Type(currentByte_rb, T_FIXNUM);
415
439
  currentByte = NUM2CHR(currentByte_rb);
416
440
 
417
- for (int i=7; i>=0; i--) {
418
- currentBit = (currentByte & (1 << i));
441
+ for (int j=7; j>=0; j--) {
442
+ currentBit = (currentByte & (1 << j));
419
443
  temp = temp << 3;
420
444
  temp = (currentBit == 0) ? (temp | 0b100) : (temp | 0b110);
421
445
  }
@@ -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
  }
@@ -581,6 +605,7 @@ static VALUE one_wire_reset(VALUE self, VALUE rbHandle, VALUE rbGPIO) {
581
605
  int handle = NUM2INT(rbHandle);
582
606
  int gpio = NUM2INT(rbGPIO);
583
607
  struct timespec start;
608
+ struct timespec now;
584
609
  uint8_t presence = 1;
585
610
 
586
611
  // Hold low for 500us to reset, then go high.
@@ -591,13 +616,92 @@ static VALUE one_wire_reset(VALUE self, VALUE rbHandle, VALUE rbGPIO) {
591
616
 
592
617
  // Poll for 250us. If a device pulls the line low, return 0 (device present).
593
618
  clock_gettime(CLOCK_MONOTONIC, &start);
594
- while(nanosSince(&start) < 250000){
595
- if (lgGpioRead(handle, gpio) == 0) presence = 0;
619
+ now = start;
620
+ while(nanoDiff(&now, &start) < 250000){
621
+ if (digitalRead(gpio) == 0) presence = 0;
622
+ clock_gettime(CLOCK_MONOTONIC, &now);
596
623
  }
597
624
 
598
625
  return UINT2NUM(presence);
599
626
  }
600
627
 
628
+ /*****************************************************************************/
629
+ /* Bit-Bang I2C Helpers */
630
+ /*****************************************************************************/
631
+ static uint8_t bitReadU8(uint8_t* b, uint8_t i) {
632
+ return (*b >> i) & 0b1;
633
+ }
634
+
635
+ static void bitWriteU8(uint8_t* b, uint8_t i, uint8_t v) {
636
+ if (v == 0) {
637
+ *b &= ~(1 << i);
638
+ } else {
639
+ *b |= (1 << i);
640
+ }
641
+ }
642
+
643
+ static void i2c_bb_set_sda(int handle, int gpio, uint8_t* state, uint8_t level) {
644
+ if (level == *state) return;
645
+ lgGpioWrite(handle, gpio, level);
646
+ *state = level;
647
+ }
648
+
649
+ static VALUE i2c_bb_read_byte(VALUE self, VALUE rbSCL_H, VALUE rbSCL_L, VALUE rbSDA_H, VALUE rbSDA_L, VALUE rbACK) {
650
+ int scl_H = NUM2INT(rbSCL_H);
651
+ int scl_L = NUM2INT(rbSCL_L);
652
+ int sda_H = NUM2INT(rbSDA_H);
653
+ int sda_L = NUM2INT(rbSDA_L);
654
+ uint8_t ack = RTEST(rbACK);
655
+
656
+ // Prevent caching by setting opposite to first state SDA will take.
657
+ uint8_t sdaState = 0;
658
+ uint8_t b;
659
+ uint8_t bit;
660
+
661
+ // Receive MSB first.
662
+ for (int i=7; i>=0; i--) {
663
+ i2c_bb_set_sda(sda_H, sda_L, &sdaState, 1);
664
+ lgGpioWrite(scl_H, scl_L, 1);
665
+ bit = lgGpioRead(sda_H, sda_L);
666
+ lgGpioWrite(scl_H, scl_L, 0);
667
+
668
+ bitWriteU8(&b, i, bit);
669
+ }
670
+
671
+ // Send ACK or NACK.
672
+ i2c_bb_set_sda(sda_H, sda_L, &sdaState, ack ^ 0b1);
673
+ lgGpioWrite(scl_H, scl_L, 1);
674
+ lgGpioWrite(scl_H, scl_L, 0);
675
+
676
+ return UINT2NUM(b);
677
+ }
678
+
679
+ static VALUE i2c_bb_write_byte(VALUE self, VALUE rbSCL_H, VALUE rbSCL_L, VALUE rbSDA_H, VALUE rbSDA_L, VALUE rbByte) {
680
+ int scl_H = NUM2INT(rbSCL_H);
681
+ int scl_L = NUM2INT(rbSCL_L);
682
+ int sda_H = NUM2INT(rbSDA_H);
683
+ int sda_L = NUM2INT(rbSDA_L);
684
+ uint8_t b = NUM2UINT(rbByte);
685
+
686
+ // Prevent caching by setting opposite to first state SDA will take.
687
+ uint8_t sdaState = bitReadU8(&b, 7) ^ 0b1;
688
+ uint8_t ack;
689
+
690
+ // Write MSBFIRST.
691
+ for (int i=7; i>=0; i--) {
692
+ i2c_bb_set_sda(sda_H, sda_L, &sdaState, bitReadU8(&b, i));
693
+ lgGpioWrite(scl_H, scl_L, 1);
694
+ lgGpioWrite(scl_H, scl_L, 0);
695
+ }
696
+
697
+ // Read and return ACK.
698
+ i2c_bb_set_sda(sda_H, sda_L, &sdaState, 1);
699
+ lgGpioWrite(scl_H, scl_L, 1);
700
+ ack = lgGpioRead(sda_H, sda_L);
701
+ lgGpioWrite(scl_H, scl_L, 0);
702
+ return (ack == 0) ? Qtrue : Qfalse;
703
+ }
704
+
601
705
  /*****************************************************************************/
602
706
  /* EXTENSION INIT */
603
707
  /*****************************************************************************/
@@ -605,7 +709,7 @@ void Init_lgpio(void) {
605
709
  // Modules
606
710
  VALUE mLGPIO = rb_define_module("LGPIO");
607
711
 
608
- // Basics
712
+ // Constants
609
713
  rb_define_const(mLGPIO, "SET_ACTIVE_LOW", INT2NUM(LG_SET_ACTIVE_LOW));
610
714
  rb_define_const(mLGPIO, "SET_OPEN_DRAIN", INT2NUM(LG_SET_OPEN_DRAIN));
611
715
  rb_define_const(mLGPIO, "SET_OPEN_SOURCE", INT2NUM(LG_SET_OPEN_SOURCE));
@@ -615,6 +719,11 @@ void Init_lgpio(void) {
615
719
  rb_define_const(mLGPIO, "RISING_EDGE", INT2NUM(LG_RISING_EDGE));
616
720
  rb_define_const(mLGPIO, "FALLING_EDGE", INT2NUM(LG_FALLING_EDGE));
617
721
  rb_define_const(mLGPIO, "BOTH_EDGES", INT2NUM(LG_BOTH_EDGES));
722
+
723
+ // Generic Helpers
724
+ rb_define_singleton_method(mLGPIO, "micro_delay", rbMicroDelay, 1);
725
+
726
+ // Basic GPIO
618
727
  rb_define_singleton_method(mLGPIO, "chip_open", chip_open, 1);
619
728
  rb_define_singleton_method(mLGPIO, "chip_close", chip_close, 1);
620
729
  rb_define_singleton_method(mLGPIO, "gpio_get_mode", gpio_get_mode, 2);
@@ -648,23 +757,23 @@ void Init_lgpio(void) {
648
757
  // Don't use this. Servo will jitter.
649
758
  rb_define_singleton_method(mLGPIO, "tx_servo", tx_servo, 6);
650
759
 
651
- // Hardware PWM waves for on-off-keying.
760
+ // HW PWM waves, on-off-keying only.
652
761
  VALUE cHardwarePWM = rb_define_class_under(mLGPIO, "HardwarePWM", rb_cObject);
653
762
  rb_define_method(cHardwarePWM, "tx_wave_ook", tx_wave_ook, 3);
654
763
 
655
- // I2C
764
+ // HW I2C
656
765
  rb_define_singleton_method(mLGPIO, "i2c_open", i2c_open, 3);
657
766
  rb_define_singleton_method(mLGPIO, "i2c_close", i2c_close, 1);
658
767
  rb_define_singleton_method(mLGPIO, "i2c_write_device", i2c_write_device, 2);
659
768
  rb_define_singleton_method(mLGPIO, "i2c_read_device", i2c_read_device, 2);
660
769
  rb_define_singleton_method(mLGPIO, "i2c_zip", i2c_zip, 3);
661
770
 
662
- // SPI
771
+ // HW SPI
663
772
  rb_define_singleton_method(mLGPIO, "spi_open", spi_open, 4);
664
773
  rb_define_singleton_method(mLGPIO, "spi_close", spi_close, 1);
665
774
  rb_define_singleton_method(mLGPIO, "spi_read", spi_read, 2);
666
775
  rb_define_singleton_method(mLGPIO, "spi_write", spi_write, 2);
667
- rb_define_singleton_method(mLGPIO, "spi_xfer", spi_xfer, 2);
776
+ rb_define_singleton_method(mLGPIO, "spi_xfer", spi_xfer, 3);
668
777
  rb_define_singleton_method(mLGPIO, "spi_ws2812_write", spi_ws2812_write, 2);
669
778
 
670
779
  // Bit-Bang Pulse Input
@@ -675,4 +784,8 @@ void Init_lgpio(void) {
675
784
  rb_define_singleton_method(mLGPIO, "one_wire_bit_read", one_wire_bit_read, 2);
676
785
  rb_define_singleton_method(mLGPIO, "one_wire_bit_write", one_wire_bit_write, 3);
677
786
  rb_define_singleton_method(mLGPIO, "one_wire_reset", one_wire_reset, 2);
787
+
788
+ // Bit-bang I2C Helpers
789
+ rb_define_singleton_method(mLGPIO, "i2c_bb_write_byte", i2c_bb_write_byte, 5);
790
+ rb_define_singleton_method(mLGPIO, "i2c_bb_read_byte", i2c_bb_read_byte, 5);
678
791
  }
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
@@ -1,10 +1,10 @@
1
1
  module LGPIO
2
2
  class HardwarePWM
3
- NS_PER_S = 1_000_000_000
4
- NS_PER_US = 1_000
3
+ NS_PER_S = 10**9
4
+ NS_PER_US = 10**3
5
5
  SYS_FS_PWM_PATH = "/sys/class/pwm/"
6
6
 
7
- attr_reader :period, :duty, :enabled
7
+ attr_reader :period, :duty, :polarity, :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,16 +44,31 @@ 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 ||= (NS_PER_S / period.to_f).round
40
65
  end
41
66
 
42
67
  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
68
+ old_duty = duty || File.read(duty_path).strip.to_i
69
+ duty = 0 unless (old_duty == 0)
47
70
  File.open(period_path, 'w') { |f| f.write(p) }
71
+ @frequency = nil
48
72
  @period = p
49
73
  end
50
74
 
@@ -20,6 +20,10 @@ module LGPIO
20
20
  initialize_pins
21
21
  end
22
22
 
23
+ def config
24
+ @config ||= [scl_handle, scl_line, sda_handle, scl_line]
25
+ end
26
+
23
27
  def initialize_pins
24
28
  LGPIO.gpio_claim_output(scl_handle, scl_line, LGPIO::SET_PULL_NONE, LGPIO::HIGH)
25
29
  LGPIO.gpio_claim_output(sda_handle, sda_line, LGPIO::SET_OPEN_DRAIN | LGPIO::SET_PULL_UP, LGPIO::HIGH)
@@ -84,6 +88,14 @@ module LGPIO
84
88
  (read_bit == 0)
85
89
  end
86
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
+
87
99
  def read(address, length)
88
100
  raise ArgumentError, "invalid I2C address: #{address}. Range is 0x08..0x77" unless VALID_ADDRESSES.include?(address)
89
101
  raise ArgumentError, "invalid Integer for read length: #{length}" unless length.kind_of?(Integer)
@@ -94,10 +106,16 @@ module LGPIO
94
106
 
95
107
  # Read length bytes, and ACK for all but the last one.
96
108
  bytes = []
97
- (length-1).times { bytes << read_byte(true) }
98
- bytes << read_byte(false)
99
- stop
100
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
101
119
  bytes
102
120
  end
103
121
 
@@ -107,7 +125,13 @@ module LGPIO
107
125
 
108
126
  start
109
127
  write_byte(write_form(address))
110
- 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
+
111
135
  stop
112
136
  end
113
137
 
@@ -1,6 +1,16 @@
1
1
  module LGPIO
2
2
  class Infrared < HardwarePWM
3
- def transmit(pulses, duty: 33.333)
3
+ FREQUENCY = 38_000
4
+ DUTY = 33.333
5
+
6
+ def initialize(*args, **kwargs)
7
+ new_kwargs = {frequency: FREQUENCY}.merge(kwargs)
8
+ super(*args, **new_kwargs)
9
+ # Avoid needing to call #enable and #disable, before and after each #transmit call.
10
+ # disable
11
+ end
12
+
13
+ def transmit(pulses, duty: DUTY)
4
14
  self.duty_percent = duty
5
15
  tx_wave_ook(duty_path, @duty.to_s, pulses)
6
16
  end
@@ -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
@@ -123,12 +123,12 @@ module LGPIO
123
123
  new_discrepancies = address ^ complement
124
124
 
125
125
  high_discrepancy = -1
126
- (0..63).each { |i| high_discrepancy = i if new_discrepancies[i] == 0 }
126
+ (0..63).each { |i| high_discrepancy = i if ((new_discrepancies >> i) & 0b1 == 0) }
127
127
 
128
128
  [address, high_discrepancy]
129
129
  end
130
130
 
131
- # Convert interleaved address/complement bytes to 64-bit numbers.
131
+ # Result is 16 bytes, 8 byte address and complement interleaved LSByte first.
132
132
  def split_search_result(data)
133
133
  address = 0
134
134
  complement = 0
@@ -155,7 +155,7 @@ module LGPIO
155
155
  crc = 0b00000000
156
156
  bytes.take(bytes.length - 1).each do |byte|
157
157
  for bit in (0..7)
158
- xor = byte[bit] ^ crc[0]
158
+ xor = ((byte >> bit) & 0b1) ^ (crc & 0b1)
159
159
  crc = crc ^ ((xor * (2 ** 3)) | (xor * (2 ** 4)))
160
160
  crc = crc >> 1
161
161
  crc = crc | (xor * (2 ** 7))
@@ -165,7 +165,9 @@ module LGPIO
165
165
  end
166
166
 
167
167
  def self.address_to_bytes(address)
168
- [address].pack('Q<').split("").map(&:ord)
168
+ bytes = []
169
+ 8.times { |i| bytes[i] = address >> (8*i) & 0xFF }
170
+ bytes
169
171
  end
170
172
  end
171
173
  end
@@ -25,7 +25,7 @@ module LGPIO
25
25
  end
26
26
 
27
27
  def config
28
- @config ||= { handle: handle, clock: clock, input: input, output: output }
28
+ @config ||= [clock_handle, clock_line, input_handle, input_line, output_handle, output_line]
29
29
  end
30
30
 
31
31
  def initialize_pins
data/lib/lgpio/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module LGPIO
2
- VERSION = "0.1.10"
2
+ VERSION = "0.1.12"
3
3
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lgpio
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.10
4
+ version: 0.1.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - vickash
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-09-28 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies: []
13
12
  description: Use Linux GPIO, I2C, SPI and PWM in Ruby
14
13
  email: mail@vickash.com
@@ -18,6 +17,7 @@ extensions:
18
17
  extra_rdoc_files: []
19
18
  files:
20
19
  - ".gitignore"
20
+ - Gemfile
21
21
  - LICENSE
22
22
  - README.md
23
23
  - Rakefile
@@ -67,7 +67,6 @@ licenses:
67
67
  - MIT
68
68
  metadata:
69
69
  source_code_uri: https://github.com/denko-rb/lgpio
70
- post_install_message:
71
70
  rdoc_options: []
72
71
  require_paths:
73
72
  - lib
@@ -75,15 +74,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
75
74
  requirements:
76
75
  - - ">="
77
76
  - !ruby/object:Gem::Version
78
- version: '0'
77
+ version: '3'
79
78
  required_rubygems_version: !ruby/object:Gem::Requirement
80
79
  requirements:
81
80
  - - ">="
82
81
  - !ruby/object:Gem::Version
83
82
  version: '0'
84
83
  requirements: []
85
- rubygems_version: 3.5.20
86
- signing_key:
84
+ rubygems_version: 3.6.7
87
85
  specification_version: 4
88
- summary: Use Linux GPIO, I2C, SPI and PWM in Ruby
86
+ summary: Ruby C extension for Linux GPIO
89
87
  test_files: []