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 +4 -4
- data/Gemfile +4 -0
- data/README.md +1 -1
- data/examples/dht.rb +1 -1
- data/examples/i2c_bb_ssd1306_bench.rb +2 -2
- data/examples/spi_bb_ssd1306_bench.rb +0 -2
- data/examples/spi_hw_loopback.rb +1 -1
- data/ext/lgpio/lgpio.c +140 -27
- data/lgpio.gemspec +2 -1
- data/lib/lgpio/hardware_pwm.rb +31 -7
- data/lib/lgpio/i2c_bitbang.rb +28 -4
- data/lib/lgpio/infrared.rb +11 -1
- data/lib/lgpio/one_wire.rb +7 -5
- data/lib/lgpio/spi_bitbang.rb +1 -1
- data/lib/lgpio/version.rb +1 -1
- metadata +6 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3b3d476891ddae4745580531d10dc7c58de9ac0f7c93f75ad2743597db0fd74
|
4
|
+
data.tar.gz: df0d8c8b767115c07c1d9174cb9cf69797a04278035fb6fbc41be68b4877a585
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: affc87dba05d69d20589acbcffb3988018a5f2759516d9b55a452dc8baa7c00de4c1cc61bc511590503063a06b1a41efadcf589dfcc628a53704cde9eeafe423
|
7
|
+
data.tar.gz: 2ea4c0c0f9ba1f602dcaff830543fd69ca6e1c6c0555fec0961362be3e51482a38210158776e94e54a445c9a17a29198a6d541ade0cdbc8863e80bd5112372e6
|
data/Gemfile
ADDED
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
|
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,
|
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
|
39
|
-
cps = (START_ARRAY.length + ((PATTERN_1.length + PATTERN_2.length) / 2) + 2) *
|
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)
|
data/examples/spi_hw_loopback.rb
CHANGED
@@ -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),
|
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 (
|
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 (
|
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
|
278
|
-
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
|
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<
|
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
|
-
|
384
|
-
|
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(
|
390
|
-
for(int i=0; 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
|
-
|
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
|
418
|
-
currentBit = (currentByte & (1 <<
|
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(
|
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
|
-
|
595
|
-
|
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
|
-
//
|
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
|
-
//
|
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,
|
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 = "
|
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
|
data/lib/lgpio/hardware_pwm.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module LGPIO
|
2
2
|
class HardwarePWM
|
3
|
-
NS_PER_S =
|
4
|
-
NS_PER_US =
|
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
|
-
|
44
|
-
unless (
|
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
|
|
data/lib/lgpio/i2c_bitbang.rb
CHANGED
@@ -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
|
-
|
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
|
|
data/lib/lgpio/infrared.rb
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
module LGPIO
|
2
2
|
class Infrared < HardwarePWM
|
3
|
-
|
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
|
data/lib/lgpio/one_wire.rb
CHANGED
@@ -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,
|
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
|
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
|
-
#
|
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
|
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
|
-
[
|
168
|
+
bytes = []
|
169
|
+
8.times { |i| bytes[i] = address >> (8*i) & 0xFF }
|
170
|
+
bytes
|
169
171
|
end
|
170
172
|
end
|
171
173
|
end
|
data/lib/lgpio/spi_bitbang.rb
CHANGED
data/lib/lgpio/version.rb
CHANGED
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.
|
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:
|
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: '
|
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.
|
86
|
-
signing_key:
|
84
|
+
rubygems_version: 3.6.7
|
87
85
|
specification_version: 4
|
88
|
-
summary:
|
86
|
+
summary: Ruby C extension for Linux GPIO
|
89
87
|
test_files: []
|