lgpio 0.1.6 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/examples/{i2c_bitbang_search.rb → i2c_bb_search.rb} +2 -3
- data/examples/pwm_hw_bench.rb +20 -0
- data/examples/{spi_bitbang_ssd1306_bench.rb → spi_bb_ssd1306_bench.rb} +1 -1
- data/ext/lgpio/lgpio.c +123 -290
- data/lgpio.gemspec +2 -2
- data/lib/lgpio/spi_bitbang.rb +4 -0
- data/lib/lgpio/version.rb +1 -1
- metadata +28 -29
- data/examples/i2c_bitbang_aht10.rb +0 -40
- data/examples/i2c_bitbang_ssd1306_bench.rb +0 -35
- /data/examples/{bench_in.rb → gpio_bench_in.rb} +0 -0
- /data/examples/{bench_out.rb → gpio_bench_out.rb} +0 -0
- /data/examples/{blink.rb → gpio_blink.rb} +0 -0
- /data/examples/{group_in.rb → gpio_group_in.rb} +0 -0
- /data/examples/{group_out.rb → gpio_group_out.rb} +0 -0
- /data/examples/{momentary.rb → gpio_momentary.rb} +0 -0
- /data/examples/{reports.rb → gpio_reports.rb} +0 -0
- /data/examples/{rotary_encoder.rb → gpio_rotary_encoder.rb} +0 -0
- /data/examples/{rotary_encoder_led.rb → gpio_rotary_encoder_led.rb} +0 -0
- /data/examples/{wave.rb → gpio_wave.rb} +0 -0
- /data/examples/{i2c_bitbang-rb_aht10.rb → i2c_bb_aht10.rb} +0 -0
- /data/examples/{i2c_bitbang-rb_ssd1306_bench.rb → i2c_bb_ssd1306_bench.rb} +0 -0
- /data/examples/{i2c_aht10.rb → i2c_hw_aht10.rb} +0 -0
- /data/examples/{i2c_aht10_zip.rb → i2c_hw_aht10_zip.rb} +0 -0
- /data/examples/{i2c_ssd1306_bench.rb → i2c_hw_ssd1306_bench.rb} +0 -0
- /data/examples/{servo.rb → pwm_hw_servo.rb} +0 -0
- /data/examples/{pwm.rb → pwm_sw.rb} +0 -0
- /data/examples/{spi_bitbang_loopback.rb → spi_bb_loopback.rb} +0 -0
- /data/examples/{spi_bitbang_ssd1306_sim_bench.rb → spi_bb_sim_bench.rb} +0 -0
- /data/examples/{spi_loopback.rb → spi_hw_loopback.rb} +0 -0
- /data/examples/{spi_ws2812.rb → spi_hw_ws2812.rb} +0 -0
- /data/examples/{spi_ws2812_bounce.rb → spi_hw_ws2812_bounce.rb} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3007d43d8623a895efdcfb2089ccc3c9105bde952af5095d904019d2df56eaaf
|
4
|
+
data.tar.gz: 8aa7f8ecf78eff10e04deb2bc5a5732b939cbbaa99f7b4f578dd8320a7634fb7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 93cc1be6fcafd45e98a11272f608241adc2c809f0ff6d71333a63f93453bb34fba4a75155cb973c06407b6cea281fbdd934664970077a1608718f0f218eb49cd
|
7
|
+
data.tar.gz: f94f8859d0b7c3999687263a54979472fd73df50c0a3e09319aeb908c3b4e7de1bac458d97478f0ddeba19e4fe10d061c57e9331fc6910f386101315f4d087d5
|
@@ -5,9 +5,8 @@ SCL_PIN = 228
|
|
5
5
|
SDA_PIN = 270
|
6
6
|
|
7
7
|
chip_handle = LGPIO.chip_open(GPIO_CHIP)
|
8
|
-
LGPIO.
|
9
|
-
|
10
|
-
devices = LGPIO.i2c_bb_search(chip_handle, SCL_PIN, SDA_PIN)
|
8
|
+
i2c_bb = LGPIO::I2CBitBang.new(chip_handle, SCL_PIN, SDA_PIN)
|
9
|
+
devices = i2c_bb.search
|
11
10
|
|
12
11
|
if devices.empty?
|
13
12
|
puts "No devices found on I2C bus"
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'lgpio'
|
2
|
+
#
|
3
|
+
# Writing directly to a hardware PWM channel.
|
4
|
+
# Arguments in order are:
|
5
|
+
# pwmchip index (X in /sys/class/pwm/pwmchipX/)
|
6
|
+
# PWM channel on the chip (Y in /sys/class/pwm/pwmchipX/pwmY)
|
7
|
+
# period: given in nanoseconds
|
8
|
+
# OR frequency: given in Hz
|
9
|
+
#
|
10
|
+
pwm_out = LGPIO::HardwarePWM.new(0, 1, period: 20_000_000)
|
11
|
+
|
12
|
+
RUNS = 250_000
|
13
|
+
start = Time.now
|
14
|
+
RUNS.times do
|
15
|
+
pwm_out.duty_us = 1000
|
16
|
+
end
|
17
|
+
finish = Time.now
|
18
|
+
|
19
|
+
wps = RUNS / (finish - start)
|
20
|
+
puts "Hardware PWM writes per second: #{wps.round(2)}"
|
@@ -43,7 +43,7 @@ LGPIO.chip_close(chip_handle)
|
|
43
43
|
|
44
44
|
fps = FRAME_COUNT / (finish - start)
|
45
45
|
# Also calculate C calls per second, using roughly 20 calls per byte written.
|
46
|
-
data_calls = START_ARRAY.length + (
|
46
|
+
data_calls = (START_ARRAY.length + (PATTERN_1.length + PATTERN_2.length) / 2) * 20
|
47
47
|
# Add DC, SELECT and clock idle calls.
|
48
48
|
total_calls = data_calls + 8
|
49
49
|
cps = ((total_calls * fps) / 1000.0).round
|
data/ext/lgpio/lgpio.c
CHANGED
@@ -3,13 +3,19 @@
|
|
3
3
|
#include <stdio.h>
|
4
4
|
#include <time.h>
|
5
5
|
|
6
|
-
|
6
|
+
/*****************************************************************************/
|
7
|
+
/* GPIO REPORT QUEUE */
|
8
|
+
/*****************************************************************************/
|
7
9
|
static pthread_mutex_t queueLock;
|
10
|
+
// Set up a queue for up to 2**16 GPIO reports.
|
8
11
|
#define QUEUE_LENGTH UINT16_MAX + 1
|
9
12
|
static lgGpioReport_t reportQueue[QUEUE_LENGTH];
|
10
13
|
static uint16_t qWritePos = 1;
|
11
14
|
static uint16_t qReadPos = 0;
|
12
15
|
|
16
|
+
/*****************************************************************************/
|
17
|
+
/* TIMING HELPERS */
|
18
|
+
/*****************************************************************************/
|
13
19
|
static uint64_t nanoDiff(const struct timespec *event2, const struct timespec *event1) {
|
14
20
|
uint64_t event2_ns = (uint64_t)event2->tv_sec * 1000000000LL + event2->tv_nsec;
|
15
21
|
uint64_t event1_ns = (uint64_t)event1->tv_sec * 1000000000LL + event1->tv_nsec;
|
@@ -36,6 +42,9 @@ static void microDelay(uint64_t micros) {
|
|
36
42
|
nanoDelay(micros * 1000);
|
37
43
|
}
|
38
44
|
|
45
|
+
/*****************************************************************************/
|
46
|
+
/* CHIP & GPIO */
|
47
|
+
/*****************************************************************************/
|
39
48
|
static VALUE chip_open(VALUE self, VALUE gpio_dev) {
|
40
49
|
int result = lgGpiochipOpen(NUM2INT(gpio_dev));
|
41
50
|
return INT2NUM(result);
|
@@ -162,103 +171,9 @@ static VALUE gpio_get_report(VALUE self){
|
|
162
171
|
return (popped) ? hash : Qnil;
|
163
172
|
}
|
164
173
|
|
165
|
-
|
166
|
-
|
167
|
-
|
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;
|
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;
|
260
|
-
}
|
261
|
-
|
174
|
+
/*****************************************************************************/
|
175
|
+
/* SOFTWARE PWM & WAVE */
|
176
|
+
/*****************************************************************************/
|
262
177
|
static VALUE tx_busy(VALUE self, VALUE handle, VALUE gpio, VALUE kind) {
|
263
178
|
int result = lgTxBusy(NUM2INT(handle), NUM2INT(gpio), NUM2INT(kind));
|
264
179
|
return INT2NUM(result);
|
@@ -302,6 +217,9 @@ static VALUE tx_wave(VALUE self, VALUE handle, VALUE lead_gpio, VALUE pulses) {
|
|
302
217
|
return INT2NUM(result);
|
303
218
|
}
|
304
219
|
|
220
|
+
/*****************************************************************************/
|
221
|
+
/* HARDWARE PWM OOK WAVE */
|
222
|
+
/*****************************************************************************/
|
305
223
|
static VALUE tx_wave_ook(VALUE self, VALUE dutyPath, VALUE dutyString, VALUE pulses) {
|
306
224
|
// NOTE: This uses hardware PWM, NOT the lgpio software PWM/wave interface.
|
307
225
|
// The Ruby class LGPIO::HardwarePWM should have already set the PWM carrier frequency.
|
@@ -343,6 +261,9 @@ static VALUE tx_wave_ook(VALUE self, VALUE dutyPath, VALUE dutyString, VALUE pul
|
|
343
261
|
fclose(dutyFile);
|
344
262
|
}
|
345
263
|
|
264
|
+
/*****************************************************************************/
|
265
|
+
/* HARDWARE I2C */
|
266
|
+
/*****************************************************************************/
|
346
267
|
static VALUE i2c_open(VALUE self, VALUE i2cDev, VALUE i2cAddr, VALUE i2cFlags){
|
347
268
|
int handle = lgI2cOpen(NUM2INT(i2cDev), NUM2INT(i2cAddr), NUM2INT(i2cFlags));
|
348
269
|
return INT2NUM(handle);
|
@@ -406,6 +327,9 @@ static VALUE i2c_zip(VALUE self, VALUE handle, VALUE txArray, VALUE rb_rxCount){
|
|
406
327
|
return retArray;
|
407
328
|
}
|
408
329
|
|
330
|
+
/*****************************************************************************/
|
331
|
+
/* HARDWARE SPI */
|
332
|
+
/*****************************************************************************/
|
409
333
|
static VALUE spi_open(VALUE self, VALUE spiDev, VALUE spiChan, VALUE spiBaud, VALUE spiFlags){
|
410
334
|
int handle = lgSpiOpen(NUM2INT(spiDev), NUM2INT(spiChan), NUM2INT(spiBaud), NUM2INT(spiFlags));
|
411
335
|
return INT2NUM(handle);
|
@@ -506,32 +430,108 @@ static VALUE spi_ws2812_write(VALUE self, VALUE handle, VALUE pixelArray){
|
|
506
430
|
}
|
507
431
|
|
508
432
|
/*****************************************************************************/
|
509
|
-
/*
|
433
|
+
/* BIT-BANG PULSE INPUT */
|
510
434
|
/*****************************************************************************/
|
511
|
-
static
|
512
|
-
|
513
|
-
|
435
|
+
static VALUE gpio_read_ultrasonic(VALUE self, VALUE rbHandle, VALUE rbTrigger, VALUE rbEcho, VALUE rbTriggerTime) {
|
436
|
+
int handle = NUM2UINT(rbHandle);
|
437
|
+
int trigger = NUM2UINT(rbTrigger);
|
438
|
+
int echo = NUM2UINT(rbEcho);
|
439
|
+
uint32_t triggerTime = NUM2UINT(rbTriggerTime);
|
440
|
+
struct timespec start;
|
441
|
+
struct timespec now;
|
442
|
+
bool echoSeen = false;
|
514
443
|
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
444
|
+
// Pull down avoids false readings if disconnected.
|
445
|
+
lgGpioClaimInput(handle, LG_SET_PULL_DOWN, echo);
|
446
|
+
|
447
|
+
// Initial pulse on the triger pin.
|
448
|
+
lgGpioClaimOutput(handle, LG_SET_PULL_NONE, trigger, 0);
|
449
|
+
microDelay(5);
|
450
|
+
lgGpioWrite(handle, trigger, 1);
|
451
|
+
microDelay(triggerTime);
|
452
|
+
lgGpioWrite(handle, trigger, 0);
|
453
|
+
|
454
|
+
clock_gettime(CLOCK_MONOTONIC, &start);
|
455
|
+
now = start;
|
456
|
+
|
457
|
+
// Wait for echo to go high, up to 25,000 us after trigger.
|
458
|
+
while(nanoDiff(&now, &start) < 25000000){
|
459
|
+
clock_gettime(CLOCK_MONOTONIC, &now);
|
460
|
+
if (lgGpioRead(handle, echo) == 1) {
|
461
|
+
echoSeen = true;
|
462
|
+
start = now;
|
463
|
+
break;
|
464
|
+
}
|
465
|
+
}
|
466
|
+
if (!echoSeen) return Qnil;
|
467
|
+
|
468
|
+
// Wait for echo to go low again, up to 25,000 us after echo start.
|
469
|
+
while(nanoDiff(&now, &start) < 25000000){
|
470
|
+
clock_gettime(CLOCK_MONOTONIC, &now);
|
471
|
+
if (lgGpioRead(handle, echo) == 0) break;
|
520
472
|
}
|
521
|
-
}
|
522
473
|
|
523
|
-
|
524
|
-
return (
|
474
|
+
// High pulse time in microseconds.
|
475
|
+
return INT2NUM(round(nanoDiff(&now, &start) / 1000.0));
|
525
476
|
}
|
526
477
|
|
527
|
-
static
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
478
|
+
static VALUE gpio_read_pulses_us(VALUE self, VALUE rbHandle, VALUE rbGPIO, VALUE rbReset_us, VALUE rbResetLevel, VALUE rbLimit, VALUE rbTimeout_ms) {
|
479
|
+
// C values
|
480
|
+
int handle = NUM2INT(rbHandle);
|
481
|
+
int gpio = NUM2INT(rbGPIO);
|
482
|
+
uint32_t reset_us = NUM2UINT(rbReset_us);
|
483
|
+
uint8_t resetLevel = NUM2UINT(rbResetLevel);
|
484
|
+
uint32_t limit = NUM2UINT(rbLimit);
|
485
|
+
uint64_t timeout_ns = NUM2UINT(rbTimeout_ms) * 1000000;
|
486
|
+
|
487
|
+
// State setup
|
488
|
+
uint64_t pulses_ns[limit];
|
489
|
+
uint32_t pulseIndex = 0;
|
490
|
+
int gpioState;
|
491
|
+
struct timespec start;
|
492
|
+
struct timespec lastPulse;
|
493
|
+
struct timespec now;
|
494
|
+
|
495
|
+
// Perform reset
|
496
|
+
if (reset_us > 0) {
|
497
|
+
int result = lgGpioClaimOutput(handle, LG_SET_PULL_NONE, gpio, resetLevel);
|
498
|
+
if (result < 0) return NUM2INT(result);
|
499
|
+
microDelay(reset_us);
|
500
|
+
}
|
501
|
+
|
502
|
+
// Initialize timing
|
503
|
+
clock_gettime(CLOCK_MONOTONIC, &start);
|
504
|
+
lastPulse = start;
|
505
|
+
now = start;
|
506
|
+
|
507
|
+
// Switch to input and read initial state
|
508
|
+
lgGpioClaimInput(handle, LG_SET_PULL_NONE, gpio);
|
509
|
+
gpioState = lgGpioRead(handle, gpio);
|
510
|
+
|
511
|
+
// Read pulses in nanoseconds
|
512
|
+
while ((nanoDiff(&now, &start) < timeout_ns) && (pulseIndex < limit)) {
|
513
|
+
clock_gettime(CLOCK_MONOTONIC, &now);
|
514
|
+
if (lgGpioRead(handle, gpio) != gpioState) {
|
515
|
+
pulses_ns[pulseIndex] = nanoDiff(&now, &lastPulse);
|
516
|
+
lastPulse = now;
|
517
|
+
gpioState = gpioState ^ 0b1;
|
518
|
+
pulseIndex++;
|
519
|
+
}
|
532
520
|
}
|
521
|
+
|
522
|
+
// Return Ruby array of pulse as microseconds
|
523
|
+
if (pulseIndex == 0) return Qnil;
|
524
|
+
VALUE retArray = rb_ary_new2(pulseIndex);
|
525
|
+
for(int i=0; i<pulseIndex; i++){
|
526
|
+
uint32_t pulse_us = round(pulses_ns[i] / 1000.0);
|
527
|
+
rb_ary_store(retArray, i, UINT2NUM(pulse_us));
|
528
|
+
}
|
529
|
+
return retArray;
|
533
530
|
}
|
534
531
|
|
532
|
+
/*****************************************************************************/
|
533
|
+
/* BIT BANG 1-WIRE HEPERS */
|
534
|
+
/*****************************************************************************/
|
535
535
|
static VALUE one_wire_bit_read(VALUE self, VALUE rbHandle, VALUE rbGPIO) {
|
536
536
|
int handle = NUM2INT(rbHandle);
|
537
537
|
int gpio = NUM2INT(rbGPIO);
|
@@ -598,167 +598,6 @@ static VALUE one_wire_reset(VALUE self, VALUE rbHandle, VALUE rbGPIO) {
|
|
598
598
|
return UINT2NUM(presence);
|
599
599
|
}
|
600
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
601
|
/*****************************************************************************/
|
763
602
|
/* EXTENSION INIT */
|
764
603
|
/*****************************************************************************/
|
@@ -798,10 +637,6 @@ void Init_lgpio(void) {
|
|
798
637
|
rb_define_singleton_method(mLGPIO, "gpio_start_reporting", gpio_start_reporting, 0);
|
799
638
|
rb_define_singleton_method(mLGPIO, "gpio_get_report", gpio_get_report, 0);
|
800
639
|
|
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
640
|
// Soft PWM / Wave
|
806
641
|
rb_define_const(mLGPIO, "TX_PWM", INT2NUM(LG_TX_PWM));
|
807
642
|
rb_define_const(mLGPIO, "TX_WAVE",INT2NUM(LG_TX_WAVE));
|
@@ -813,6 +648,10 @@ void Init_lgpio(void) {
|
|
813
648
|
// Don't use this. Servo will jitter.
|
814
649
|
rb_define_singleton_method(mLGPIO, "tx_servo", tx_servo, 6);
|
815
650
|
|
651
|
+
// Hardware PWM waves for on-off-keying.
|
652
|
+
VALUE cHardwarePWM = rb_define_class_under(mLGPIO, "HardwarePWM", rb_cObject);
|
653
|
+
rb_define_method(cHardwarePWM, "tx_wave_ook", tx_wave_ook, 3);
|
654
|
+
|
816
655
|
// I2C
|
817
656
|
rb_define_singleton_method(mLGPIO, "i2c_open", i2c_open, 3);
|
818
657
|
rb_define_singleton_method(mLGPIO, "i2c_close", i2c_close, 1);
|
@@ -828,18 +667,12 @@ void Init_lgpio(void) {
|
|
828
667
|
rb_define_singleton_method(mLGPIO, "spi_xfer", spi_xfer, 2);
|
829
668
|
rb_define_singleton_method(mLGPIO, "spi_ws2812_write", spi_ws2812_write, 2);
|
830
669
|
|
831
|
-
//
|
832
|
-
|
833
|
-
|
670
|
+
// Bit-Bang Pulse Input
|
671
|
+
rb_define_singleton_method(mLGPIO, "gpio_read_ultrasonic", gpio_read_ultrasonic, 4);
|
672
|
+
rb_define_singleton_method(mLGPIO, "gpio_read_pulses_us", gpio_read_pulses_us, 6);
|
834
673
|
|
835
|
-
// Bit-
|
674
|
+
// Bit-bang 1-Wire Helpers
|
836
675
|
rb_define_singleton_method(mLGPIO, "one_wire_bit_read", one_wire_bit_read, 2);
|
837
676
|
rb_define_singleton_method(mLGPIO, "one_wire_bit_write", one_wire_bit_write, 3);
|
838
677
|
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);
|
845
678
|
}
|
data/lgpio.gemspec
CHANGED
@@ -4,8 +4,8 @@ Gem::Specification.new do |s|
|
|
4
4
|
s.name = 'lgpio'
|
5
5
|
s.version = LGPIO::VERSION
|
6
6
|
s.licenses = ['MIT']
|
7
|
-
s.summary = "
|
8
|
-
s.description = "Use GPIO
|
7
|
+
s.summary = "Use Linux GPIO, I2C, SPI and PWM in Ruby"
|
8
|
+
s.description = "Use Linux GPIO, I2C, SPI and PWM in Ruby"
|
9
9
|
|
10
10
|
s.authors = ["vickash"]
|
11
11
|
s.email = 'mail@vickash.com'
|
data/lib/lgpio/spi_bitbang.rb
CHANGED
@@ -16,6 +16,10 @@ module LGPIO
|
|
16
16
|
initialize_pins
|
17
17
|
end
|
18
18
|
|
19
|
+
def config
|
20
|
+
@config ||= { handle: handle, clock: clock, input: input, output: output }
|
21
|
+
end
|
22
|
+
|
19
23
|
def initialize_pins
|
20
24
|
LGPIO.gpio_claim_output(handle, LGPIO::SET_PULL_NONE, clock, LGPIO::LOW)
|
21
25
|
LGPIO.gpio_claim_input(handle, LGPIO::SET_PULL_NONE, input) if input
|
data/lib/lgpio/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- vickash
|
@@ -10,7 +10,7 @@ bindir: bin
|
|
10
10
|
cert_chain: []
|
11
11
|
date: 2024-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description: Use GPIO
|
13
|
+
description: Use Linux GPIO, I2C, SPI and PWM in Ruby
|
14
14
|
email: mail@vickash.com
|
15
15
|
executables: []
|
16
16
|
extensions:
|
@@ -21,37 +21,36 @@ files:
|
|
21
21
|
- LICENSE
|
22
22
|
- README.md
|
23
23
|
- Rakefile
|
24
|
-
- examples/bench_in.rb
|
25
|
-
- examples/bench_out.rb
|
26
|
-
- examples/blink.rb
|
27
24
|
- examples/dht.rb
|
28
|
-
- examples/
|
29
|
-
- examples/
|
25
|
+
- examples/gpio_bench_in.rb
|
26
|
+
- examples/gpio_bench_out.rb
|
27
|
+
- examples/gpio_blink.rb
|
28
|
+
- examples/gpio_group_in.rb
|
29
|
+
- examples/gpio_group_out.rb
|
30
|
+
- examples/gpio_momentary.rb
|
31
|
+
- examples/gpio_reports.rb
|
32
|
+
- examples/gpio_rotary_encoder.rb
|
33
|
+
- examples/gpio_rotary_encoder_led.rb
|
34
|
+
- examples/gpio_wave.rb
|
30
35
|
- examples/hcsr04.rb
|
31
|
-
- examples/
|
32
|
-
- examples/
|
33
|
-
- examples/
|
34
|
-
- examples/
|
35
|
-
- examples/
|
36
|
-
- examples/
|
37
|
-
- examples/i2c_bitbang_ssd1306_bench.rb
|
38
|
-
- examples/i2c_ssd1306_bench.rb
|
36
|
+
- examples/i2c_bb_aht10.rb
|
37
|
+
- examples/i2c_bb_search.rb
|
38
|
+
- examples/i2c_bb_ssd1306_bench.rb
|
39
|
+
- examples/i2c_hw_aht10.rb
|
40
|
+
- examples/i2c_hw_aht10_zip.rb
|
41
|
+
- examples/i2c_hw_ssd1306_bench.rb
|
39
42
|
- examples/infrared.rb
|
40
|
-
- examples/momentary.rb
|
41
43
|
- examples/one_wire_ds18b20.rb
|
42
44
|
- examples/one_wire_search.rb
|
43
|
-
- examples/
|
44
|
-
- examples/
|
45
|
-
- examples/
|
46
|
-
- examples/
|
47
|
-
- examples/
|
48
|
-
- examples/
|
49
|
-
- examples/
|
50
|
-
- examples/
|
51
|
-
- examples/
|
52
|
-
- examples/spi_ws2812.rb
|
53
|
-
- examples/spi_ws2812_bounce.rb
|
54
|
-
- examples/wave.rb
|
45
|
+
- examples/pwm_hw_bench.rb
|
46
|
+
- examples/pwm_hw_servo.rb
|
47
|
+
- examples/pwm_sw.rb
|
48
|
+
- examples/spi_bb_loopback.rb
|
49
|
+
- examples/spi_bb_sim_bench.rb
|
50
|
+
- examples/spi_bb_ssd1306_bench.rb
|
51
|
+
- examples/spi_hw_loopback.rb
|
52
|
+
- examples/spi_hw_ws2812.rb
|
53
|
+
- examples/spi_hw_ws2812_bounce.rb
|
55
54
|
- ext/lgpio/extconf.rb
|
56
55
|
- ext/lgpio/lgpio.c
|
57
56
|
- lgpio.gemspec
|
@@ -86,5 +85,5 @@ requirements: []
|
|
86
85
|
rubygems_version: 3.5.16
|
87
86
|
signing_key:
|
88
87
|
specification_version: 4
|
89
|
-
summary:
|
88
|
+
summary: Use Linux GPIO, I2C, SPI and PWM in Ruby
|
90
89
|
test_files: []
|
@@ -1,40 +0,0 @@
|
|
1
|
-
require 'lgpio'
|
2
|
-
|
3
|
-
POWER_ON_DELAY = 0.100
|
4
|
-
RESET_DELAY = 0.020
|
5
|
-
COMMAND_DELAY = 0.010
|
6
|
-
MEASURE_DELAY = 0.080
|
7
|
-
DATA_LENGTH = 6
|
8
|
-
SOFT_RESET = [0xBA]
|
9
|
-
INIT_AND_CALIBRATE = [0xE1, 0x08, 0x00]
|
10
|
-
START_MEASUREMENT = [0xAC, 0x33, 0x00]
|
11
|
-
|
12
|
-
GPIO_CHIP = 0
|
13
|
-
SCL_PIN = 228
|
14
|
-
SDA_PIN = 270
|
15
|
-
ADDRESS = 0x38
|
16
|
-
|
17
|
-
chip_handle = LGPIO.chip_open(GPIO_CHIP)
|
18
|
-
LGPIO.i2c_bb_claim(chip_handle, SCL_PIN, SDA_PIN)
|
19
|
-
|
20
|
-
# Startup sequence
|
21
|
-
sleep(POWER_ON_DELAY)
|
22
|
-
LGPIO.i2c_bb_write(chip_handle, SCL_PIN, SDA_PIN, ADDRESS, SOFT_RESET)
|
23
|
-
sleep(RESET_DELAY)
|
24
|
-
LGPIO.i2c_bb_write(chip_handle, SCL_PIN, SDA_PIN, ADDRESS, INIT_AND_CALIBRATE)
|
25
|
-
sleep(COMMAND_DELAY)
|
26
|
-
|
27
|
-
# Read and close
|
28
|
-
LGPIO.i2c_bb_write(chip_handle, SCL_PIN, SDA_PIN, ADDRESS, START_MEASUREMENT)
|
29
|
-
sleep(MEASURE_DELAY)
|
30
|
-
bytes = LGPIO.i2c_bb_read(chip_handle, SCL_PIN, SDA_PIN, ADDRESS, DATA_LENGTH)
|
31
|
-
|
32
|
-
# Humidity uses the upper 4 bits of the shared byte as its lowest 4 bits.
|
33
|
-
h_raw = ((bytes[1] << 16) | (bytes[2] << 8) | (bytes[3])) >> 4
|
34
|
-
humidity = (h_raw.to_f / 2**20) * 100
|
35
|
-
|
36
|
-
# Temperature uses the lower 4 bits of the shared byte as its highest 4 bits.
|
37
|
-
t_raw = ((bytes[3] & 0x0F) << 16) | (bytes[4] << 8) | bytes[5]
|
38
|
-
temperature = (t_raw.to_f / 2**20) * 200 - 50
|
39
|
-
|
40
|
-
puts "#{Time.now.strftime '%Y-%m-%d %H:%M:%S'} - Temperature: #{temperature.round(2)} \xC2\xB0C | Humidity: #{humidity.round(2)} %"
|
@@ -1,35 +0,0 @@
|
|
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 = [64] + Array.new(1024) { 0b00110011 }
|
6
|
-
PATTERN_2 = [64] + Array.new(1024) { 0b11001100 }
|
7
|
-
|
8
|
-
GPIO_CHIP = 0
|
9
|
-
SCL_PIN = 228
|
10
|
-
SDA_PIN = 270
|
11
|
-
ADDRESS = 0x3C
|
12
|
-
|
13
|
-
chip_handle = LGPIO.chip_open(GPIO_CHIP)
|
14
|
-
LGPIO.i2c_bb_claim(chip_handle, SCL_PIN, SDA_PIN)
|
15
|
-
|
16
|
-
LGPIO.i2c_bb_write(chip_handle, SCL_PIN, SDA_PIN, ADDRESS, INIT_ARRAY)
|
17
|
-
FRAME_COUNT = 400
|
18
|
-
|
19
|
-
start = Time.now
|
20
|
-
(FRAME_COUNT / 2).times do
|
21
|
-
LGPIO.i2c_bb_write(chip_handle, SCL_PIN, SDA_PIN, ADDRESS, START_ARRAY)
|
22
|
-
LGPIO.i2c_bb_write(chip_handle, SCL_PIN, SDA_PIN, ADDRESS, PATTERN_1)
|
23
|
-
LGPIO.i2c_bb_write(chip_handle, SCL_PIN, SDA_PIN, ADDRESS, START_ARRAY)
|
24
|
-
LGPIO.i2c_bb_write(chip_handle, SCL_PIN, SDA_PIN, ADDRESS, PATTERN_2)
|
25
|
-
end
|
26
|
-
finish = Time.now
|
27
|
-
|
28
|
-
LGPIO.chip_close(chip_handle)
|
29
|
-
|
30
|
-
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
|
33
|
-
cps = (cps / 1000.0).round
|
34
|
-
|
35
|
-
puts "SSD1306 benchmark result: #{fps.round(2)} fps | #{cps}k C calls/s"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|