lgpio 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,51 @@
1
+ require 'lgpio'
2
+
3
+ INIT_ARRAY = [0, 168, 63, 211, 0, 64, 161, 200, 218, 18, 164, 166, 213, 128, 219, 32, 217, 241, 141, 20, 32, 0, 175]
4
+ START_ARRAY = [0, 33, 0, 127, 34, 0, 7]
5
+ PATTERN_1 = Array.new(1024) { 0b00110011 }
6
+ PATTERN_2 = Array.new(1024) { 0b11001100 }
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, LGPIO::SET_PULL_NONE, SELECT_PIN, LGPIO::HIGH)
19
+ LGPIO.gpio_claim_output(chip_handle, LGPIO::SET_PULL_NONE, RESET_PIN, LGPIO::LOW)
20
+ LGPIO.gpio_claim_output(chip_handle, LGPIO::SET_PULL_NONE, DC_PIN, LGPIO::LOW)
21
+
22
+ # 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)
26
+
27
+ FRAME_COUNT = 400
28
+
29
+ start = Time.now
30
+ (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)
39
+ end
40
+ finish = Time.now
41
+
42
+ LGPIO.chip_close(chip_handle)
43
+
44
+ fps = FRAME_COUNT / (finish - start)
45
+ # Also calculate C calls per second, using roughly 20 calls per byte written.
46
+ data_calls = START_ARRAY.length + ((PATTERN_1.length + PATTERN_2.length) / 2) * 20
47
+ # Add DC, SELECT and clock idle calls.
48
+ total_calls = data_calls + 8
49
+ cps = ((total_calls * fps) / 1000.0).round
50
+
51
+ puts "SSD1306 benchmark result: #{fps.round(2)} fps | #{cps}k C calls/s"
@@ -0,0 +1,48 @@
1
+ require 'lgpio'
2
+
3
+ GPIO_CHIP = 0
4
+ CLOCK_PIN = 17
5
+ INPUT_PIN = 22
6
+ OUTPUT_PIN = 27
7
+
8
+ # Emulate data sent over I2C to a SSD1306 OLED, prepending its write address.
9
+ START_ARRAY = [0x3C << 1] + [0, 33, 0, 127, 34, 0, 7]
10
+ PATTERN_1 = [0x3C << 1] + [64] + Array.new(1179) { 0b00110011 }
11
+ PATTERN_2 = [0x3C << 1] + [64] + Array.new(1179) { 0b11001100 }
12
+ FRAME_COUNT = 400
13
+
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)
16
+
17
+ start = Time.now
18
+ (FRAME_COUNT / 2).times do
19
+ spi_bb.transfer(write: START_ARRAY)
20
+ spi_bb.transfer(write: PATTERN_1)
21
+ spi_bb.transfer(write: START_ARRAY)
22
+ spi_bb.transfer(write: PATTERN_2)
23
+ end
24
+ finish = Time.now
25
+
26
+ LGPIO.chip_close(chip_handle)
27
+
28
+ fps = FRAME_COUNT / (finish - start)
29
+ #
30
+ # We want to calculate how many C calls were made per second, to compare with I2C:
31
+ # - SPI bytes are 8 bits on the wire, while I2C are 9 (8 data bits + ACK bit).
32
+ # - The I2C ACK should do 4 C API calls, while data bits on both buses need 3 calls.
33
+ # - This isn't always true. Because of the "lazy" optimization when setting the data pin
34
+ # in both protocols, and the line pattern being used, where the first bit of
35
+ # each byte is the inverse of the last bit of the previous byte, two things change:
36
+ # - Each I2C ACK is effecitvely 3 C calls. The data bit either before or after is always 1,
37
+ # eliminating a write to SDA.
38
+ # - For both buses, the "2 on, 2 off" patern means 8 data bits are only 20 C calls total.
39
+ # - So SPI is 20 for all pixel bytes, and I2C is 23 for all pixel bytes.
40
+ # - Ignores bit changes in the address and start bytes.
41
+ # - Ignore 10 calls per frame for I2C start/stop, and 2 calls per frame for SPI clock idle.
42
+ # - These are only 1% of total calls, so should be negligible.
43
+ # - Changing the line pattern WILL break this calculation.
44
+ #
45
+ cps = (START_ARRAY.length + ((PATTERN_1.length + PATTERN_2.length) / 2)) * 20 * fps
46
+ cps = (cps / 1000.0).round
47
+
48
+ puts "SSD1306 sim result: #{fps.round(2)} fps | #{cps}k C calls/s"
data/examples/wave.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'lgpio'
2
2
 
3
3
  GPIO_CHIP = 0
4
- LEDS = [260, 267]
4
+ LEDS = [272, 258]
5
5
  INIT_STATE = [0, 0]
6
6
  INTERVAL = 250_000 # 250ms
7
7
  TIMES = 10
data/ext/lgpio/lgpio.c CHANGED
@@ -1,5 +1,7 @@
1
1
  #include <lgpio.h>
2
2
  #include <ruby.h>
3
+ #include <stdio.h>
4
+ #include <time.h>
3
5
 
4
6
  // Set up a queue for up to 2**16 GPIO reports.
5
7
  static pthread_mutex_t queueLock;
@@ -8,6 +10,32 @@ static lgGpioReport_t reportQueue[QUEUE_LENGTH];
8
10
  static uint16_t qWritePos = 1;
9
11
  static uint16_t qReadPos = 0;
10
12
 
13
+ static uint64_t nanoDiff(const struct timespec *event2, const struct timespec *event1) {
14
+ uint64_t event2_ns = (uint64_t)event2->tv_sec * 1000000000LL + event2->tv_nsec;
15
+ uint64_t event1_ns = (uint64_t)event1->tv_sec * 1000000000LL + event1->tv_nsec;
16
+ return event2_ns - event1_ns;
17
+ }
18
+
19
+ static uint64_t nanosSince(const struct timespec *event) {
20
+ struct timespec now;
21
+ clock_gettime(CLOCK_MONOTONIC, &now);
22
+ return nanoDiff(&now, event);
23
+ }
24
+
25
+ static void nanoDelay(uint64_t nanos) {
26
+ struct timespec refTime;
27
+ struct timespec now;
28
+ clock_gettime(CLOCK_MONOTONIC, &refTime);
29
+ now = refTime;
30
+ while(nanoDiff(&now, &refTime) < nanos) {
31
+ clock_gettime(CLOCK_MONOTONIC, &now);
32
+ }
33
+ }
34
+
35
+ static void microDelay(uint64_t micros) {
36
+ nanoDelay(micros * 1000);
37
+ }
38
+
11
39
  static VALUE chip_open(VALUE self, VALUE gpio_dev) {
12
40
  int result = lgGpiochipOpen(NUM2INT(gpio_dev));
13
41
  return INT2NUM(result);
@@ -18,6 +46,11 @@ static VALUE chip_close(VALUE self, VALUE handle) {
18
46
  return INT2NUM(result);
19
47
  }
20
48
 
49
+ static VALUE gpio_get_mode(VALUE self, VALUE handle, VALUE gpio) {
50
+ int result = lgGpioGetMode(NUM2INT(handle), NUM2INT(gpio));
51
+ return INT2NUM(result);
52
+ }
53
+
21
54
  static VALUE gpio_claim_output(VALUE self, VALUE handle, VALUE flags, VALUE gpio, VALUE level) {
22
55
  int result = lgGpioClaimOutput(NUM2INT(handle), NUM2INT(flags), NUM2INT(gpio), NUM2INT(level));
23
56
  return INT2NUM(result);
@@ -93,13 +126,13 @@ static VALUE gpio_claim_alert(VALUE self, VALUE handle, VALUE flags, VALUE eFlag
93
126
  return INT2NUM(result);
94
127
  }
95
128
 
96
- void queue_gpio_reports(int count, lgGpioAlert_p events, void *data){
129
+ static void queue_gpio_reports(int count, lgGpioAlert_p events, void *data){
97
130
  pthread_mutex_lock(&queueLock);
98
131
  for(int i=0; i<count; i++) {
99
132
  memcpy(&reportQueue[qWritePos], &events[i].report, sizeof(lgGpioReport_t));
100
- qWritePos += 1;
133
+ qWritePos++;
101
134
  // qReadPos is the LAST report read. If passing by 1, increment it too. Lose oldest data first.
102
- if (qWritePos - qReadPos == 1) qReadPos += 1;
135
+ if (qWritePos - qReadPos == 1) qReadPos++;
103
136
  }
104
137
  pthread_mutex_unlock(&queueLock);
105
138
  }
@@ -126,11 +159,104 @@ static VALUE gpio_get_report(VALUE self){
126
159
  }
127
160
  pthread_mutex_unlock(&queueLock);
128
161
 
129
- if (popped){
130
- return hash;
131
- } else {
132
- return Qnil;
162
+ return (popped) ? hash : Qnil;
163
+ }
164
+
165
+ static VALUE gpio_read_ultrasonic(VALUE self, VALUE rbHandle, VALUE rbTrigger, VALUE rbEcho, VALUE rbTriggerTime) {
166
+ int handle = NUM2UINT(rbHandle);
167
+ int trigger = NUM2UINT(rbTrigger);
168
+ int echo = NUM2UINT(rbEcho);
169
+ uint32_t triggerTime = NUM2UINT(rbTriggerTime);
170
+ struct timespec start;
171
+ struct timespec now;
172
+ bool echoSeen = false;
173
+
174
+ // Pull down avoids false readings if disconnected.
175
+ lgGpioClaimInput(handle, LG_SET_PULL_DOWN, echo);
176
+
177
+ // Initial pulse on the triger pin.
178
+ lgGpioClaimOutput(handle, LG_SET_PULL_NONE, trigger, 0);
179
+ microDelay(5);
180
+ lgGpioWrite(handle, trigger, 1);
181
+ microDelay(triggerTime);
182
+ lgGpioWrite(handle, trigger, 0);
183
+
184
+ clock_gettime(CLOCK_MONOTONIC, &start);
185
+ now = start;
186
+
187
+ // Wait for echo to go high, up to 25,000 us after trigger.
188
+ while(nanoDiff(&now, &start) < 25000000){
189
+ clock_gettime(CLOCK_MONOTONIC, &now);
190
+ if (lgGpioRead(handle, echo) == 1) {
191
+ echoSeen = true;
192
+ start = now;
193
+ break;
194
+ }
195
+ }
196
+ if (!echoSeen) return Qnil;
197
+
198
+ // Wait for echo to go low again, up to 25,000 us after echo start.
199
+ while(nanoDiff(&now, &start) < 25000000){
200
+ clock_gettime(CLOCK_MONOTONIC, &now);
201
+ if (lgGpioRead(handle, echo) == 0) break;
133
202
  }
203
+
204
+ // High pulse time in microseconds.
205
+ return INT2NUM(round(nanoDiff(&now, &start) / 1000.0));
206
+ }
207
+
208
+ static VALUE gpio_read_pulses_us(VALUE self, VALUE rbHandle, VALUE rbGPIO, VALUE rbReset_us, VALUE rbResetLevel, VALUE rbLimit, VALUE rbTimeout_ms) {
209
+ // C values
210
+ int handle = NUM2INT(rbHandle);
211
+ int gpio = NUM2INT(rbGPIO);
212
+ uint32_t reset_us = NUM2UINT(rbReset_us);
213
+ uint8_t resetLevel = NUM2UINT(rbResetLevel);
214
+ uint32_t limit = NUM2UINT(rbLimit);
215
+ uint64_t timeout_ns = NUM2UINT(rbTimeout_ms) * 1000000;
216
+
217
+ // State setup
218
+ uint64_t pulses_ns[limit];
219
+ uint32_t pulseIndex = 0;
220
+ int gpioState;
221
+ struct timespec start;
222
+ struct timespec lastPulse;
223
+ struct timespec now;
224
+
225
+ // Perform reset
226
+ if (reset_us > 0) {
227
+ int result = lgGpioClaimOutput(handle, LG_SET_PULL_NONE, gpio, resetLevel);
228
+ if (result < 0) return NUM2INT(result);
229
+ microDelay(reset_us);
230
+ }
231
+
232
+ // Initialize timing
233
+ clock_gettime(CLOCK_MONOTONIC, &start);
234
+ lastPulse = start;
235
+ now = start;
236
+
237
+ // Switch to input and read initial state
238
+ lgGpioClaimInput(handle, LG_SET_PULL_NONE, gpio);
239
+ gpioState = lgGpioRead(handle, gpio);
240
+
241
+ // Read pulses in nanoseconds
242
+ while ((nanoDiff(&now, &start) < timeout_ns) && (pulseIndex < limit)) {
243
+ clock_gettime(CLOCK_MONOTONIC, &now);
244
+ if (lgGpioRead(handle, gpio) != gpioState) {
245
+ pulses_ns[pulseIndex] = nanoDiff(&now, &lastPulse);
246
+ lastPulse = now;
247
+ gpioState = gpioState ^ 0b1;
248
+ pulseIndex++;
249
+ }
250
+ }
251
+
252
+ // Return Ruby array of pulse as microseconds
253
+ if (pulseIndex == 0) return Qnil;
254
+ VALUE retArray = rb_ary_new2(pulseIndex);
255
+ for(int i=0; i<pulseIndex; i++){
256
+ uint32_t pulse_us = round(pulses_ns[i] / 1000.0);
257
+ rb_ary_store(retArray, i, UINT2NUM(pulse_us));
258
+ }
259
+ return retArray;
134
260
  }
135
261
 
136
262
  static VALUE tx_busy(VALUE self, VALUE handle, VALUE gpio, VALUE kind) {
@@ -176,6 +302,47 @@ static VALUE tx_wave(VALUE self, VALUE handle, VALUE lead_gpio, VALUE pulses) {
176
302
  return INT2NUM(result);
177
303
  }
178
304
 
305
+ static VALUE tx_wave_ook(VALUE self, VALUE dutyPath, VALUE dutyString, VALUE pulses) {
306
+ // NOTE: This uses hardware PWM, NOT the lgpio software PWM/wave interface.
307
+ // The Ruby class LGPIO::HardwarePWM should have already set the PWM carrier frequency.
308
+ //
309
+ // Convert pulses from microseconds to nanoseconds.
310
+ uint32_t pulseCount = rb_array_len(pulses);
311
+ uint64_t nanoPulses[pulseCount];
312
+ for (int i=0; i<pulseCount; i++) {
313
+ nanoPulses[i] = NUM2UINT(rb_ary_entry(pulses, i)) * 1000;
314
+ }
315
+
316
+ // Prepare to write duty cycle.
317
+ const char *filePath = StringValueCStr(dutyPath);
318
+ FILE *dutyFile = fopen(filePath, "w");
319
+ if (dutyFile == NULL) {
320
+ VALUE errorMessage = rb_sprintf("Could not open PWM duty_cycle file: %s", filePath);
321
+ rb_raise(rb_eRuntimeError, "%s", StringValueCStr(errorMessage));
322
+ }
323
+ fclose(dutyFile);
324
+ const char *cDuty = StringValueCStr(dutyString);
325
+
326
+ // Toggle duty cycle between given value and 0, to modulate the PWM carrier.
327
+ for (int i=0; i<pulseCount; i++) {
328
+ if (i % 2 == 0) {
329
+ dutyFile = fopen(filePath, "w");
330
+ fputs(cDuty, dutyFile);
331
+ fclose(dutyFile);
332
+ } else {
333
+ dutyFile = fopen(filePath, "w");
334
+ fputs("0", dutyFile);
335
+ fclose(dutyFile);
336
+ }
337
+ // Wait for pulse time.
338
+ nanoDelay(nanoPulses[i]);
339
+ }
340
+ // Leave the pin low.
341
+ dutyFile = fopen(filePath, "w");
342
+ fputs("0", dutyFile);
343
+ fclose(dutyFile);
344
+ }
345
+
179
346
  static VALUE i2c_open(VALUE self, VALUE i2cDev, VALUE i2cAddr, VALUE i2cFlags){
180
347
  int handle = lgI2cOpen(NUM2INT(i2cDev), NUM2INT(i2cAddr), NUM2INT(i2cFlags));
181
348
  return INT2NUM(handle);
@@ -338,6 +505,263 @@ static VALUE spi_ws2812_write(VALUE self, VALUE handle, VALUE pixelArray){
338
505
  return INT2NUM(result);
339
506
  }
340
507
 
508
+ /*****************************************************************************/
509
+ /* ONE WIRE */
510
+ /*****************************************************************************/
511
+ static uint8_t bitReadU64(uint64_t* b, uint8_t i) {
512
+ return ((*b >> i) & 0b1);
513
+ }
514
+
515
+ static void bitWriteU64(uint64_t* b, uint8_t i, uint8_t v) {
516
+ if (v == 0) {
517
+ *b &= ~(1ULL << i);
518
+ } else {
519
+ *b |= (1ULL << i);
520
+ }
521
+ }
522
+
523
+ static uint8_t bitReadU8(uint8_t* b, uint8_t i) {
524
+ return (*b >> i) & 0b1;
525
+ }
526
+
527
+ static void bitWriteU8(uint8_t* b, uint8_t i, uint8_t v) {
528
+ if (v == 0) {
529
+ *b &= ~(1 << i);
530
+ } else {
531
+ *b |= (1 << i);
532
+ }
533
+ }
534
+
535
+ static VALUE one_wire_bit_read(VALUE self, VALUE rbHandle, VALUE rbGPIO) {
536
+ int handle = NUM2INT(rbHandle);
537
+ int gpio = NUM2INT(rbGPIO);
538
+ uint8_t bit = 1;
539
+ struct timespec start;
540
+ struct timespec now;
541
+
542
+ // Start the read slot.
543
+ lgGpioWrite(handle, gpio, 0);
544
+ microDelay(1);
545
+ lgGpioWrite(handle, gpio, 1);
546
+
547
+ // Poll for 60us to see if pin goes low.
548
+ clock_gettime(CLOCK_MONOTONIC, &start);
549
+ now = start;
550
+ while(nanoDiff(&now, &start) < 60000){
551
+ if (lgGpioRead(handle, gpio) == 0) bit = 0;
552
+ clock_gettime(CLOCK_MONOTONIC, &now);
553
+ }
554
+ return UINT2NUM(bit);
555
+ }
556
+
557
+ static VALUE one_wire_bit_write(VALUE self, VALUE rbHandle, VALUE rbGPIO, VALUE rbBit) {
558
+ int handle = NUM2INT(rbHandle);
559
+ int gpio = NUM2INT(rbGPIO);
560
+ uint8_t bit = NUM2CHR(rbBit);
561
+
562
+ // Write slot starts by going low for at least 1us.
563
+ lgGpioWrite(handle, gpio, 0);
564
+ microDelay(1);
565
+
566
+ // If 0, keep it low for the rest of the 60us write slot, then release.
567
+ if (bit == 0) {
568
+ microDelay(59);
569
+ lgGpioWrite(handle, gpio, 1);
570
+ // If 1, release first, then wait the rest of the 60us slot.
571
+ } else {
572
+ lgGpioWrite(handle, gpio, 1);
573
+ microDelay(59);
574
+ }
575
+ // Minimum 1us recovery time after each slot.
576
+ microDelay(1);
577
+ return Qnil;
578
+ }
579
+
580
+ static VALUE one_wire_reset(VALUE self, VALUE rbHandle, VALUE rbGPIO) {
581
+ int handle = NUM2INT(rbHandle);
582
+ int gpio = NUM2INT(rbGPIO);
583
+ struct timespec start;
584
+ uint8_t presence = 1;
585
+
586
+ // Hold low for 500us to reset, then go high.
587
+ lgGpioFree(handle, gpio);
588
+ lgGpioClaimOutput(handle, LG_SET_OPEN_DRAIN | LG_SET_PULL_UP, gpio, 0);
589
+ microDelay(500);
590
+ lgGpioWrite(handle, gpio, 1);
591
+
592
+ // Poll for 250us. If a device pulls the line low, return 0 (device present).
593
+ clock_gettime(CLOCK_MONOTONIC, &start);
594
+ while(nanosSince(&start) < 250000){
595
+ if (lgGpioRead(handle, gpio) == 0) presence = 0;
596
+ }
597
+
598
+ return UINT2NUM(presence);
599
+ }
600
+
601
+ /*****************************************************************************/
602
+ /* BIT-BANG I2C */
603
+ /*****************************************************************************/
604
+ static uint8_t sdaState = 1;
605
+
606
+ static void i2c_bb_set_sda(int handle, int sda, uint8_t level) {
607
+ if (level == sdaState) return;
608
+ lgGpioWrite(handle, sda, level);
609
+ sdaState = level;
610
+ }
611
+
612
+ // Start condition is SDA then SCL going low, from both high.
613
+ static void i2c_bb_start(int handle, int scl, int sda) {
614
+ lgGpioWrite(handle, sda, 0);
615
+ lgGpioWrite(handle, scl, 0);
616
+ }
617
+
618
+ // Stop condition is SDA going high, while SCL is also high.
619
+ static void i2c_bb_stop(int handle, int scl, int sda) {
620
+ lgGpioWrite(handle, sda, 0);
621
+ lgGpioWrite(handle, scl, 1);
622
+ lgGpioWrite(handle, sda, 1);
623
+ }
624
+
625
+ static uint8_t i2c_bb_read_bit(int handle, int scl, int sda) {
626
+ uint8_t bit;
627
+ // Ensure SDA high before we pull SCL high.
628
+ i2c_bb_set_sda(handle, sda, 1);
629
+ lgGpioWrite(handle, scl, 1);
630
+ bit = lgGpioRead(handle, sda);
631
+ lgGpioWrite(handle, scl, 0);
632
+ return bit;
633
+ }
634
+
635
+ static void i2c_bb_write_bit(int handle, int scl, int sda, uint8_t bit) {
636
+ // Set SDA while SCL is low.
637
+ i2c_bb_set_sda(handle, sda, bit);
638
+ lgGpioWrite(handle, scl, 1);
639
+ lgGpioWrite(handle, scl, 0);
640
+ }
641
+
642
+ static uint8_t i2c_bb_read_byte(int handle, int scl, int sda, bool ack) {
643
+ uint8_t b;
644
+
645
+ // Receive MSB first.
646
+ for (int i=7; i>=0; i--) bitWriteU8(&b, i, i2c_bb_read_bit(handle, scl, sda));
647
+
648
+ // Send ACK or NACK and return byte.
649
+ if (ack) {
650
+ i2c_bb_write_bit(handle, scl, sda, 0);
651
+ } else {
652
+ i2c_bb_write_bit(handle, scl, sda, 1);
653
+ }
654
+ return b;
655
+ }
656
+
657
+ static int i2c_bb_write_byte(int handle, int scl, int sda, uint8_t b) {
658
+ // Send MSB first.
659
+ for (int i=7; i>=0; i--) i2c_bb_write_bit(handle, scl, sda, bitReadU8(&b, i));
660
+
661
+ // Return -1 for NACK, 0 for ACK.
662
+ return (i2c_bb_read_bit(handle, scl, sda) == 0) ? 0 : -1;
663
+ }
664
+
665
+ static VALUE i2c_bb_claim(VALUE self, VALUE rbHandle, VALUE rbSCL, VALUE rbSDA) {
666
+ int handle = NUM2INT(rbHandle);
667
+ int scl = NUM2INT(rbSCL);
668
+ int sda = NUM2INT(rbSDA);
669
+
670
+ // SCL is a driven output. SDA is open drain with pullup enabled.
671
+ lgGpioClaimOutput(handle, LG_SET_PULL_NONE, scl, 1);
672
+ lgGpioClaimOutput(handle, LG_SET_OPEN_DRAIN | LG_SET_PULL_UP, sda, 1);
673
+ }
674
+
675
+ static VALUE i2c_bb_search(VALUE self, VALUE rbHandle, VALUE rbSCL, VALUE rbSDA) {
676
+ int handle = NUM2INT(rbHandle);
677
+ int scl = NUM2INT(rbSCL);
678
+ int sda = NUM2INT(rbSDA);
679
+ int ack;
680
+ uint8_t present[128];
681
+ uint8_t presentCount = 0;
682
+ sdaState = 1;
683
+
684
+ // Only addresses from 0x08 to 0x77 are usable (8 to 127).
685
+ for (uint8_t addr = 0x08; addr < 0x78; addr++) {
686
+ i2c_bb_start(handle, scl, sda);
687
+ ack = i2c_bb_write_byte(handle, scl, sda, ((addr << 1) & 0b11111110));
688
+ i2c_bb_stop(handle, scl, sda);
689
+ if (ack == 0){
690
+ present[addr] = 1;
691
+ presentCount++;
692
+ } else {
693
+ present[addr] = 0;
694
+ }
695
+ }
696
+ if (presentCount == 0) return Qnil;
697
+
698
+ VALUE retArray = rb_ary_new2(presentCount);
699
+ uint8_t i = 0;
700
+ for (uint8_t addr = 0x08; addr < 0x78; addr++) {
701
+ if (present[addr] == 1) {
702
+ rb_ary_store(retArray, i, UINT2NUM(addr));
703
+ i++;
704
+ }
705
+ }
706
+ return retArray;
707
+ }
708
+
709
+ static VALUE i2c_bb_write(VALUE self, VALUE rbHandle, VALUE rbSCL, VALUE rbSDA, VALUE rbAddress, VALUE txArray) {
710
+ int handle = NUM2INT(rbHandle);
711
+ int scl = NUM2INT(rbSCL);
712
+ int sda = NUM2INT(rbSDA);
713
+ uint8_t address = NUM2CHR(rbAddress);
714
+ uint8_t writeAddress = (address << 1);
715
+ sdaState = 1;
716
+
717
+ int count = RARRAY_LEN(txArray);
718
+ uint8_t txBuf[count];
719
+ VALUE currentByte;
720
+ for(int i=0; i<count; i++){
721
+ currentByte = rb_ary_entry(txArray, i);
722
+ Check_Type(currentByte, T_FIXNUM);
723
+ txBuf[i] = NUM2CHR(currentByte);
724
+ }
725
+
726
+ i2c_bb_start(handle, scl, sda);
727
+ i2c_bb_write_byte(handle, scl, sda, writeAddress);
728
+ for (int i=0; i<count; i++) i2c_bb_write_byte(handle, scl, sda, txBuf[i]);
729
+ i2c_bb_stop(handle, scl, sda);
730
+ }
731
+
732
+ static VALUE i2c_bb_read(VALUE self, VALUE rbHandle, VALUE rbSCL, VALUE rbSDA, VALUE rbAddress, VALUE rbCount) {
733
+ int handle = NUM2INT(rbHandle);
734
+ int scl = NUM2INT(rbSCL);
735
+ int sda = NUM2INT(rbSDA);
736
+ uint8_t address = NUM2CHR(rbAddress);
737
+ uint8_t readAddress = (address << 1) | 0b00000001;
738
+ sdaState = 1;
739
+
740
+ int count = NUM2INT(rbCount);
741
+ uint8_t rxBuf[count];
742
+
743
+ i2c_bb_start(handle, scl, sda);
744
+ int ack = i2c_bb_write_byte(handle, scl, sda, readAddress);
745
+ // Device with this address not present on the bus.
746
+ if (ack != 0) return Qnil;
747
+
748
+ // Read and ACK for all but the last byte.
749
+ int pos = 0;
750
+ while(pos < count-1) {
751
+ rxBuf[pos] = i2c_bb_read_byte(handle, scl, sda, true);
752
+ pos++;
753
+ }
754
+ rxBuf[pos] = i2c_bb_read_byte(handle, scl, sda, false);
755
+ i2c_bb_stop(handle, scl, sda);
756
+
757
+ VALUE retArray = rb_ary_new2(count);
758
+ for(int i=0; i<count; i++) rb_ary_store(retArray, i, UINT2NUM(rxBuf[i]));
759
+ return retArray;
760
+ }
761
+
762
+ /*****************************************************************************/
763
+ /* EXTENSION INIT */
764
+ /*****************************************************************************/
341
765
  void Init_lgpio(void) {
342
766
  // Modules
343
767
  VALUE mLGPIO = rb_define_module("LGPIO");
@@ -354,6 +778,7 @@ void Init_lgpio(void) {
354
778
  rb_define_const(mLGPIO, "BOTH_EDGES", INT2NUM(LG_BOTH_EDGES));
355
779
  rb_define_singleton_method(mLGPIO, "chip_open", chip_open, 1);
356
780
  rb_define_singleton_method(mLGPIO, "chip_close", chip_close, 1);
781
+ rb_define_singleton_method(mLGPIO, "gpio_get_mode", gpio_get_mode, 2);
357
782
  rb_define_singleton_method(mLGPIO, "gpio_free", gpio_free, 2);
358
783
  rb_define_singleton_method(mLGPIO, "gpio_claim_input", gpio_claim_input, 3);
359
784
  rb_define_singleton_method(mLGPIO, "gpio_claim_output", gpio_claim_output, 4);
@@ -373,15 +798,20 @@ void Init_lgpio(void) {
373
798
  rb_define_singleton_method(mLGPIO, "gpio_start_reporting", gpio_start_reporting, 0);
374
799
  rb_define_singleton_method(mLGPIO, "gpio_get_report", gpio_get_report, 0);
375
800
 
376
- // PWM / Servo / Wave
801
+ // Pulse Input
802
+ rb_define_singleton_method(mLGPIO, "gpio_read_ultrasonic", gpio_read_ultrasonic, 4);
803
+ rb_define_singleton_method(mLGPIO, "gpio_read_pulses_us", gpio_read_pulses_us, 6);
804
+
805
+ // Soft PWM / Wave
377
806
  rb_define_const(mLGPIO, "TX_PWM", INT2NUM(LG_TX_PWM));
378
807
  rb_define_const(mLGPIO, "TX_WAVE",INT2NUM(LG_TX_WAVE));
379
808
  rb_define_singleton_method(mLGPIO, "tx_busy", tx_busy, 3);
380
809
  rb_define_singleton_method(mLGPIO, "tx_room", tx_room, 3);
381
810
  rb_define_singleton_method(mLGPIO, "tx_pulse", tx_pulse, 6);
382
811
  rb_define_singleton_method(mLGPIO, "tx_pwm", tx_pwm, 6);
383
- rb_define_singleton_method(mLGPIO, "tx_servo", tx_servo, 6);
384
812
  rb_define_singleton_method(mLGPIO, "tx_wave", tx_wave, 3);
813
+ // Don't use this. Servo will jitter.
814
+ rb_define_singleton_method(mLGPIO, "tx_servo", tx_servo, 6);
385
815
 
386
816
  // I2C
387
817
  rb_define_singleton_method(mLGPIO, "i2c_open", i2c_open, 3);
@@ -397,4 +827,19 @@ void Init_lgpio(void) {
397
827
  rb_define_singleton_method(mLGPIO, "spi_write", spi_write, 2);
398
828
  rb_define_singleton_method(mLGPIO, "spi_xfer", spi_xfer, 2);
399
829
  rb_define_singleton_method(mLGPIO, "spi_ws2812_write", spi_ws2812_write, 2);
830
+
831
+ // Hardware PWM waves for on-off-keying.
832
+ VALUE cHardwarePWM = rb_define_class_under(mLGPIO, "HardwarePWM", rb_cObject);
833
+ rb_define_method(cHardwarePWM, "tx_wave_ook", tx_wave_ook, 3);
834
+
835
+ // Bit-banged 1-Wire
836
+ rb_define_singleton_method(mLGPIO, "one_wire_bit_read", one_wire_bit_read, 2);
837
+ rb_define_singleton_method(mLGPIO, "one_wire_bit_write", one_wire_bit_write, 3);
838
+ rb_define_singleton_method(mLGPIO, "one_wire_reset", one_wire_reset, 2);
839
+
840
+ // Bit-banged I2C
841
+ rb_define_singleton_method(mLGPIO, "i2c_bb_claim", i2c_bb_claim, 3);
842
+ rb_define_singleton_method(mLGPIO, "i2c_bb_search", i2c_bb_search, 3);
843
+ rb_define_singleton_method(mLGPIO, "i2c_bb_write", i2c_bb_write, 5);
844
+ rb_define_singleton_method(mLGPIO, "i2c_bb_read", i2c_bb_read, 5);
400
845
  }
@@ -23,6 +23,18 @@ module LGPIO
23
23
  @path ||= "#{SYS_FS_PWM_PATH}pwmchip#{@chip}/pwm#{@channel}/"
24
24
  end
25
25
 
26
+ def period_path
27
+ @period_path ||= "#{path}period"
28
+ end
29
+
30
+ def duty_path
31
+ @duty_path ||= "#{path}duty_cycle"
32
+ end
33
+
34
+ def enable_path
35
+ @enable_path ||= "#{path}enable"
36
+ end
37
+
26
38
  def frequency=(freq)
27
39
  self.period = (NS_PER_S / freq.to_f).round
28
40
  end
@@ -30,13 +42,14 @@ module LGPIO
30
42
  def period=(p)
31
43
  old_period = File.read("#{path}period").strip.to_i
32
44
  unless (old_period == 0)
33
- File.open("#{path}duty_cycle", 'w') { |f| f.write("0") }
45
+ File.open(duty_path, 'w') { |f| f.write("0") }
34
46
  end
35
- File.open("#{path}period", 'w') { |f| f.write(p) }
47
+ File.open(period_path, 'w') { |f| f.write(p) }
36
48
  @period = p
37
49
  end
38
50
 
39
51
  def duty_percent
52
+ return 0.0 if (!duty || !period) || (duty == 0)
40
53
  (duty / period.to_f) * 100.0
41
54
  end
42
55
 
@@ -53,17 +66,17 @@ module LGPIO
53
66
 
54
67
  def duty=(d_ns)
55
68
  raise "duty cycle: #{d_ns} ns cannot be longer than period: #{period} ns" if d_ns > period
56
- File.open("#{path}duty_cycle", 'w') { |f| f.write(d_ns) }
69
+ File.open(duty_path, 'w') { |f| f.write(d_ns) }
57
70
  @duty = d_ns
58
71
  end
59
72
 
60
73
  def disable
61
- File.open("#{path}enable", 'w') { |f| f.write("0") }
74
+ File.open(enable_path, 'w') { |f| f.write("0") }
62
75
  @enabled = false
63
76
  end
64
77
 
65
78
  def enable
66
- File.open("#{path}enable", 'w') { |f| f.write("1") }
79
+ File.open(enable_path, 'w') { |f| f.write("1") }
67
80
  @enabled = true
68
81
  end
69
82
  end