lgpio 0.1.5 → 0.1.7
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/README.md +42 -28
- data/examples/dht.rb +47 -0
- data/examples/{bench_in.rb → gpio_bench_in.rb} +1 -1
- data/examples/{bench_out.rb → gpio_bench_out.rb} +1 -1
- data/examples/{blink.rb → gpio_blink.rb} +1 -1
- data/examples/{group_in.rb → gpio_group_in.rb} +2 -2
- data/examples/{group_out.rb → gpio_group_out.rb} +1 -1
- data/examples/{momentary.rb → gpio_momentary.rb} +2 -2
- data/examples/{reports.rb → gpio_reports.rb} +1 -1
- data/examples/{rotary_encoder.rb → gpio_rotary_encoder.rb} +3 -3
- data/examples/{rotary_encoder_led.rb → gpio_rotary_encoder_led.rb} +4 -4
- data/examples/{wave.rb → gpio_wave.rb} +1 -1
- data/examples/hcsr04.rb +32 -0
- data/examples/i2c_bb_aht10.rb +40 -0
- data/examples/i2c_bb_search.rb +19 -0
- data/examples/i2c_bb_ssd1306_bench.rb +35 -0
- data/examples/{i2c_aht10.rb → i2c_hw_aht10.rb} +2 -1
- data/examples/{i2c_aht10_zip.rb → i2c_hw_aht10_zip.rb} +2 -1
- data/examples/{i2c_ssd1306_bench.rb → i2c_hw_ssd1306_bench.rb} +6 -5
- data/examples/infrared.rb +22 -0
- data/examples/one_wire_ds18b20.rb +30 -0
- data/examples/one_wire_search.rb +15 -0
- data/examples/pwm_hw_bench.rb +20 -0
- data/examples/{pwm.rb → pwm_sw.rb} +1 -1
- data/examples/spi_bb_loopback.rb +25 -0
- data/examples/spi_bb_sim_bench.rb +48 -0
- data/examples/spi_bb_ssd1306_bench.rb +51 -0
- data/ext/lgpio/lgpio.c +289 -11
- data/lib/lgpio/hardware_pwm.rb +18 -5
- data/lib/lgpio/i2c_bitbang.rb +117 -0
- data/lib/lgpio/infrared.rb +8 -0
- data/lib/lgpio/one_wire.rb +171 -0
- data/lib/lgpio/spi_bitbang.rb +109 -0
- data/lib/lgpio/version.rb +1 -1
- data/lib/lgpio.rb +6 -0
- metadata +36 -21
- data/examples/spi_read.rb +0 -14
- /data/examples/{servo.rb → pwm_hw_servo.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: 175af29acc6f0bb362dc017458e1ca9948f5d0c73ac0693f4a06b2e4e64f2842
|
4
|
+
data.tar.gz: c7eebe2b025a40e98c3e7fc7574eb1f7572e7c147a88a32d7b4a9bfdb0cb0882
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3f5bfc169dfeb319fdfc06c4de1058682e9bee1f547a9eebecc1eb9ad2d50403bda912a67aef171e66b828d22740b04925b83c0e3494204c73c0b32dbbe7931
|
7
|
+
data.tar.gz: f3d17767bf96c7af4631945ac0a7812628698cb483634fc0c1a45513a8ac5107bc613799be32b95b2c4b251e51a4c95c253f107691767d5351b8b1e073f5d965
|
data/README.md
CHANGED
@@ -1,39 +1,52 @@
|
|
1
1
|
# lgpio
|
2
2
|
|
3
|
-
Ruby bindings for the [lgpio (lg)](https://github.com/joan2937/lg)
|
3
|
+
Ruby gem with bindings for the [lgpio (lg)](https://github.com/joan2937/lg) C library. This is for single-board-computers (SBCs) running Linux, such as Orange Pi, Raspberry Pi, etc. It provides low-level access to the GPIO, I2C, SPI, and PWM subsytems.
|
4
|
+
|
5
|
+
## Standard LGPIO Features
|
4
6
|
|
5
|
-
## Mapped LGPIO Features
|
6
7
|
- [x] GPIO Read/Write
|
7
8
|
- [x] GPIO Group Read/Write
|
8
|
-
- [x] GPIO Alerts
|
9
|
-
-
|
10
|
-
-
|
11
|
-
|
12
|
-
-
|
9
|
+
- [x] GPIO Alerts
|
10
|
+
- Alerts are generated by a separate thread, and can be read from a queue in Ruby.
|
11
|
+
- No real "callback" functionality.
|
12
|
+
- [x] Software PWM Output
|
13
|
+
- Timing not 100% precise, but works on any GPIO.
|
14
|
+
- Not recommended for servo motors, will jitter.
|
13
15
|
- [x] Wave
|
14
|
-
- Software timed on any pin, as with PWM.
|
15
|
-
- [x] I2C
|
16
|
-
- [x] SPI
|
16
|
+
- Software timed on any pin(s), as with PWM.
|
17
|
+
- [x] Hardware I2C
|
18
|
+
- [x] Hardware SPI
|
19
|
+
|
20
|
+
## Extra Features (Built on LGPIO)
|
21
|
+
|
22
|
+
- [x] `LGPIO.gpio_read_ultrasonic` sends a pulse on a `trigger` pin, then measures a single pulse on a separate `echo` pin. Used for HC-SR04 or similar. See `examples/hcsr04.rb`.
|
23
|
+
- [x] `LGPIO.gpio_read_pulses_us` outputs a reset pulse on a pin, then polls for a sequence of input pulses. Used for DHT enviro sensors or similar. See `examples/dht.rb`.
|
24
|
+
- [x] Bit Bang I2C
|
25
|
+
- [x] Bit Bang SPI
|
26
|
+
- [x] Bit Bang 1-Wire
|
27
|
+
- [x] WS2812 addressable LEDs over hardware SPI
|
28
|
+
- Outputs on MOSI/PICO pin
|
29
|
+
- Must be able to set SPI clock frequency to 2.4 MHz
|
30
|
+
|
31
|
+
## Hardware PWM Features
|
32
|
+
|
33
|
+
These use the sysfs PWM interface, not lgpio C, but are a good fit for this gem.
|
17
34
|
|
18
|
-
|
19
|
-
- [x]
|
20
|
-
|
21
|
-
-
|
22
|
-
-
|
35
|
+
- [x] Hardware PWM Output
|
36
|
+
- [x] Servo (based on hardware PWM)
|
37
|
+
- [x] On-off Keying (OOK) Modulated Waves
|
38
|
+
- Carrier generated by hardware PWM. Software modulated with monotonic clock timing.
|
39
|
+
- Useful for sending infrared signals at 38kHz, for example.
|
23
40
|
|
24
|
-
|
25
|
-
- [x] Hardware PWM Out (specific pins per chip)
|
26
|
-
- [x] Servo
|
41
|
+
**Note:** Once hardware PWM is used on a pin, it stays PWM until reboot. The associated GPIO won't work.
|
27
42
|
|
28
43
|
## Installation
|
29
44
|
On Debian-based Linuxes (RaspberryPi OS, Armbian, DietPi etc.):
|
30
45
|
```bash
|
31
46
|
sudo apt install swig python3-dev python3-setuptools
|
32
47
|
|
33
|
-
#
|
34
|
-
|
35
|
-
wget https://github.com/joan2937/lg/archive/master.zip
|
36
|
-
# wget https://github.com/vickash/lg/archive/refs/heads/master.zip
|
48
|
+
# Temporary fork of: wget https://github.com/joan2937/lg/archive/master.zip
|
49
|
+
wget https://github.com/vickash/lg/archive/refs/heads/master.zip
|
37
50
|
|
38
51
|
unzip master.zip
|
39
52
|
cd lg-master
|
@@ -44,17 +57,18 @@ gem install lgpio
|
|
44
57
|
```
|
45
58
|
|
46
59
|
## Enabling Hardware & Permissions
|
47
|
-
Depending on your SBC and Linux distro/version, you may need to manually enable I2C and
|
60
|
+
Depending on your SBC and Linux distro/version, you may need to manually enable hardware I2C, SPI, and PWM. You should use the config tool that came with your distro for that, if possible.
|
48
61
|
|
49
|
-
|
62
|
+
Even when these are enabled, you may not have permission to access them. To run without `sudo`, you need read+write permission to some or all of the following:
|
50
63
|
```
|
51
|
-
/dev/gpiochip*
|
52
|
-
/dev/i2c-*
|
53
|
-
/dev/spidev*
|
64
|
+
/dev/gpiochip* (For GPIO, example: /dev/gpiochip0)
|
65
|
+
/dev/i2c-* (For I2C, example: /dev/i2c-1)
|
66
|
+
/dev/spidev* (For SPI, example: /dev/spidev0.1)
|
67
|
+
/sys/class/pwm/pwmchip* (For PWM, example: /sys/class/pwm/pwmchip0)
|
54
68
|
```
|
55
69
|
|
56
70
|
## Documentation
|
57
|
-
- See examples folder for demos of
|
71
|
+
- See examples folder for demos of everything implemented.
|
58
72
|
- Development was done on an Orange Pi Zero 2W. Your GPIO numbers may be different, so change them.
|
59
73
|
- For more info, see the [lgpio C API docs.](https://abyz.me.uk/lg/lgpio.html)
|
60
74
|
- As much as possible, the Ruby methods closely follow the C API functions, except:
|
data/examples/dht.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'lgpio'
|
2
|
+
|
3
|
+
GPIO_CHIP = 0
|
4
|
+
DHT_PIN = 267
|
5
|
+
chip_handle = LGPIO.chip_open(GPIO_CHIP)
|
6
|
+
#
|
7
|
+
# Read a series of pulses input to a GPIO, with an (optional) reset output pulse at start.
|
8
|
+
# Arguments in order are:
|
9
|
+
# gpiochip handle
|
10
|
+
# GPIO number
|
11
|
+
# Starting (reset) output pulse time in microseconds (0 for no reset)
|
12
|
+
# Reset pulse level (0 or 1)
|
13
|
+
# Maximum number of pulses to read
|
14
|
+
# Timeout in milliseconds
|
15
|
+
#
|
16
|
+
data = LGPIO.gpio_read_pulses_us(chip_handle, DHT_PIN, 20_000, 0, 84, 100)
|
17
|
+
|
18
|
+
# Handle errors.
|
19
|
+
raise "error: DHT sensor not connected" unless data
|
20
|
+
raise "LGPIO error: #{data}" if data.class == Integer
|
21
|
+
|
22
|
+
# Discard unneeded pulses
|
23
|
+
data = data.last(81)
|
24
|
+
raise "error: incomplete DHT data" unless data.length == 81
|
25
|
+
data = data.first(80)
|
26
|
+
|
27
|
+
# Convert to bytes
|
28
|
+
bytes = []
|
29
|
+
data.each_slice(16) do |b|
|
30
|
+
byte = 0b00000000
|
31
|
+
b.each_slice(2) do |x,y|
|
32
|
+
bit = (y<x) ? 0 : 1
|
33
|
+
byte = (byte << 1) | bit
|
34
|
+
end
|
35
|
+
bytes << byte
|
36
|
+
end
|
37
|
+
|
38
|
+
# CRC
|
39
|
+
crc = bytes[0..3].reduce(0, :+) & 0xFF == bytes[4]
|
40
|
+
raise "error: DHT CRC check failed" unless crc
|
41
|
+
|
42
|
+
# Convert and display
|
43
|
+
temperature = ((bytes[2] << 8) | bytes[3]).to_f / 10
|
44
|
+
humidity = ((bytes[0] << 8) | bytes[1]).to_f / 10
|
45
|
+
|
46
|
+
puts "DHT Temperature: #{temperature} \xC2\xB0C"
|
47
|
+
puts "DHT Humidity: #{humidity} %"
|
@@ -5,10 +5,10 @@
|
|
5
5
|
require 'lgpio'
|
6
6
|
|
7
7
|
GPIO_CHIP = 0
|
8
|
-
PIN_A =
|
9
|
-
PIN_B =
|
8
|
+
PIN_A = 260
|
9
|
+
PIN_B = 76
|
10
10
|
|
11
|
-
PIN_LED =
|
11
|
+
PIN_LED = 272
|
12
12
|
PWM_FREQ = 500
|
13
13
|
PWM_OFFSET = 0
|
14
14
|
PWM_CYCLES = 0 # 0 = infinite
|
@@ -44,7 +44,7 @@ loop do
|
|
44
44
|
elsif report[:gpio] == PIN_B
|
45
45
|
delta = (report[:level] == state_a) ? -1 : 1
|
46
46
|
state_b = report[:level]
|
47
|
-
|
47
|
+
|
48
48
|
led_duty += delta
|
49
49
|
led_duty = 0 if led_duty < 0
|
50
50
|
led_duty = 100 if led_duty > 100
|
data/examples/hcsr04.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#
|
2
|
+
# Example showing an HC-S04 ultrasonic distance sensor.
|
3
|
+
#
|
4
|
+
# NOTE: Some versions of this sensor require 5V power to function properly.
|
5
|
+
# If using one of these, use a 5V to 3.3V level shifter between your board and
|
6
|
+
# the sensor, at least on the echo pin.
|
7
|
+
#
|
8
|
+
require 'lgpio'
|
9
|
+
|
10
|
+
GPIO_CHIP = 0
|
11
|
+
ECHO_PIN = 228
|
12
|
+
TRIGGER_PIN = 270
|
13
|
+
SPEED_OF_SOUND = 343.0
|
14
|
+
|
15
|
+
chip_handle = LGPIO.chip_open(GPIO_CHIP)
|
16
|
+
|
17
|
+
loop do
|
18
|
+
# Arguments in order are:
|
19
|
+
# chip handle, trigger pin, echo pin, trigger time (us)
|
20
|
+
#
|
21
|
+
# HC-SR04 uses 10 microseconds for trigger. Some others use 20us.
|
22
|
+
#
|
23
|
+
microseconds = LGPIO.gpio_read_ultrasonic(chip_handle, TRIGGER_PIN, ECHO_PIN, 10)
|
24
|
+
|
25
|
+
if microseconds
|
26
|
+
mm = (microseconds / 2000.0) * SPEED_OF_SOUND
|
27
|
+
puts "Distance: #{mm.round} mm"
|
28
|
+
else
|
29
|
+
puts "Cound not read HC-SR04 sensor"
|
30
|
+
end
|
31
|
+
sleep 0.5
|
32
|
+
end
|
@@ -0,0 +1,40 @@
|
|
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
|
+
i2c_bb = LGPIO::I2CBitBang.new(chip_handle, SCL_PIN, SDA_PIN)
|
19
|
+
|
20
|
+
# Startup sequence
|
21
|
+
sleep(POWER_ON_DELAY)
|
22
|
+
i2c_bb.write(ADDRESS, SOFT_RESET)
|
23
|
+
sleep(RESET_DELAY)
|
24
|
+
i2c_bb.write(ADDRESS, INIT_AND_CALIBRATE)
|
25
|
+
sleep(COMMAND_DELAY)
|
26
|
+
|
27
|
+
# Read and close
|
28
|
+
i2c_bb.write(ADDRESS, START_MEASUREMENT)
|
29
|
+
sleep(MEASURE_DELAY)
|
30
|
+
bytes = i2c_bb.read(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)} %"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'lgpio'
|
2
|
+
|
3
|
+
GPIO_CHIP = 0
|
4
|
+
SCL_PIN = 228
|
5
|
+
SDA_PIN = 270
|
6
|
+
|
7
|
+
chip_handle = LGPIO.chip_open(GPIO_CHIP)
|
8
|
+
i2c_bb = LGPIO::I2CBitBang.new(chip_handle, SCL_PIN, SDA_PIN)
|
9
|
+
devices = i2c_bb.search
|
10
|
+
|
11
|
+
if devices.empty?
|
12
|
+
puts "No devices found on I2C bus"
|
13
|
+
else
|
14
|
+
puts "I2C device addresses found:"
|
15
|
+
devices.each do |address|
|
16
|
+
# Print as hexadecimal.
|
17
|
+
puts "0x#{address.to_s(16).upcase}"
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,35 @@
|
|
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
|
+
i2c_bb = LGPIO::I2CBitBang.new(chip_handle, SCL_PIN, SDA_PIN)
|
15
|
+
|
16
|
+
i2c_bb.write(ADDRESS, INIT_ARRAY)
|
17
|
+
FRAME_COUNT = 400
|
18
|
+
|
19
|
+
start = Time.now
|
20
|
+
(FRAME_COUNT / 2).times do
|
21
|
+
i2c_bb.write(ADDRESS, START_ARRAY)
|
22
|
+
i2c_bb.write(ADDRESS, PATTERN_1)
|
23
|
+
i2c_bb.write(ADDRESS, START_ARRAY)
|
24
|
+
i2c_bb.write(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"
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'lgpio'
|
2
2
|
|
3
3
|
I2C_DEV = 3
|
4
|
+
ADDRESS = 0x38
|
4
5
|
POWER_ON_DELAY = 0.100
|
5
6
|
RESET_DELAY = 0.020
|
6
7
|
COMMAND_DELAY = 0.010
|
@@ -10,7 +11,7 @@ SOFT_RESET = [0xBA]
|
|
10
11
|
INIT_AND_CALIBRATE = [0xE1, 0x08, 0x00]
|
11
12
|
START_MEASUREMENT = [0xAC, 0x33, 0x00]
|
12
13
|
|
13
|
-
aht10_handle = LGPIO.i2c_open(I2C_DEV,
|
14
|
+
aht10_handle = LGPIO.i2c_open(I2C_DEV, ADDRESS, 0)
|
14
15
|
|
15
16
|
# Startup sequence
|
16
17
|
sleep(POWER_ON_DELAY)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'lgpio'
|
2
2
|
|
3
3
|
I2C_DEV = 3
|
4
|
+
ADDRESS = 0x38
|
4
5
|
POWER_ON_DELAY = 0.100
|
5
6
|
RESET_DELAY = 0.020
|
6
7
|
COMMAND_DELAY = 0.010
|
@@ -10,7 +11,7 @@ INIT_AND_CALIBRATE = [5, 3, 0xE1, 0x08, 0x00, 0]
|
|
10
11
|
START_MEASUREMENT = [5, 3, 0xAC, 0x33, 0x00, 0]
|
11
12
|
READ_SIX = [4, 6, 0]
|
12
13
|
|
13
|
-
aht10_handle = LGPIO.i2c_open(I2C_DEV,
|
14
|
+
aht10_handle = LGPIO.i2c_open(I2C_DEV, ADDRESS, 0)
|
14
15
|
|
15
16
|
# Startup sequence
|
16
17
|
sleep(POWER_ON_DELAY)
|
@@ -1,21 +1,22 @@
|
|
1
1
|
require 'lgpio'
|
2
2
|
|
3
3
|
I2C_DEV = 3
|
4
|
+
ADDRESS = 0x3C
|
4
5
|
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]
|
5
6
|
START_ARRAY = [0, 33, 0, 127, 34, 0, 7]
|
6
|
-
|
7
|
-
|
7
|
+
PATTERN_1 = [64] + Array.new(1024) { 0b00110011 }
|
8
|
+
PATTERN_2 = [64] + Array.new(1024) { 0b11001100 }
|
8
9
|
|
9
|
-
ssd1306_handle = LGPIO.i2c_open(I2C_DEV,
|
10
|
+
ssd1306_handle = LGPIO.i2c_open(I2C_DEV, ADDRESS, 0)
|
10
11
|
LGPIO.i2c_write_device(ssd1306_handle, INIT_ARRAY)
|
11
12
|
FRAME_COUNT = 100
|
12
13
|
|
13
14
|
start = Time.now
|
14
15
|
(FRAME_COUNT / 2).times do
|
15
16
|
LGPIO.i2c_write_device(ssd1306_handle, START_ARRAY)
|
16
|
-
LGPIO.i2c_write_device(ssd1306_handle,
|
17
|
+
LGPIO.i2c_write_device(ssd1306_handle, PATTERN_1)
|
17
18
|
LGPIO.i2c_write_device(ssd1306_handle, START_ARRAY)
|
18
|
-
LGPIO.i2c_write_device(ssd1306_handle,
|
19
|
+
LGPIO.i2c_write_device(ssd1306_handle, PATTERN_2)
|
19
20
|
end
|
20
21
|
finish = Time.now
|
21
22
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'lgpio'
|
2
|
+
|
3
|
+
FREQUENCY = 38_000
|
4
|
+
|
5
|
+
# NEC Raw-Data=0xF708FB04. LSBFIRST, so the binary for each hex digit below is backward.
|
6
|
+
CODE = [ 9000, 500, # Start bit
|
7
|
+
560, 560, 560, 560, 560, 1690, 560, 560, # 0010 0x4 command
|
8
|
+
560, 560, 560, 560, 560, 560, 560, 560, # 0000 0x0 command
|
9
|
+
560, 1690, 560, 1690, 560,560, 560, 1690, # 1101 0xB command inverted
|
10
|
+
560, 1690, 560, 1690, 560, 1690, 560, 1690, # 1111 0xF command inverted
|
11
|
+
560, 560, 560, 560, 560, 560, 560, 1690, # 0001 0x8 address
|
12
|
+
560, 560, 560, 560, 560, 560, 560, 560, # 0000 0x0 address
|
13
|
+
560, 1690, 560, 1690, 560, 1690, 560, 560, # 1110 0x7 address inverted
|
14
|
+
560, 1690, 560, 1690, 560, 1690, 560, 1690, # 1111 0xF address inverted
|
15
|
+
560] # Stop bit
|
16
|
+
|
17
|
+
infrared = LGPIO::Infrared.new(0, 2, frequency: FREQUENCY)
|
18
|
+
|
19
|
+
loop do
|
20
|
+
infrared.transmit(CODE)
|
21
|
+
sleep 1
|
22
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'lgpio'
|
2
|
+
|
3
|
+
GPIO_CHIP = 0
|
4
|
+
PIN = 256
|
5
|
+
PARASITE = false
|
6
|
+
|
7
|
+
chip_handle = LGPIO.chip_open(GPIO_CHIP)
|
8
|
+
one_wire = LGPIO::OneWire.new(chip_handle, PIN)
|
9
|
+
|
10
|
+
one_wire.reset
|
11
|
+
# Skip ROM
|
12
|
+
one_wire.write([LGPIO::OneWire::SKIP_ROM], parasite: PARASITE)
|
13
|
+
# Start conversion
|
14
|
+
one_wire.write([LGPIO::OneWire::CONVERT_T], parasite: PARASITE)
|
15
|
+
# Wait for conversion
|
16
|
+
sleep(1)
|
17
|
+
# Reset
|
18
|
+
one_wire.reset
|
19
|
+
# Skip ROM
|
20
|
+
one_wire.write([LGPIO::OneWire::SKIP_ROM], parasite: PARASITE)
|
21
|
+
# Read 9 bytes from scratchpad
|
22
|
+
one_wire.write([LGPIO::OneWire::READ_SCRATCH], parasite: PARASITE)
|
23
|
+
bytes = one_wire.read(9)
|
24
|
+
|
25
|
+
# Temperature is the first 16 bits (2 bytes of 9 read).
|
26
|
+
# It's a signed, 2's complement, little-endian decimal. LSB = 2 ^ -4.
|
27
|
+
#
|
28
|
+
temperature = bytes[0..1].pack('C*').unpack('s<')[0] * (2.0 ** -4)
|
29
|
+
|
30
|
+
puts "DS18B20 reading: #{temperature} \xC2\xB0C"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'lgpio'
|
2
|
+
|
3
|
+
GPIO_CHIP = 0
|
4
|
+
PIN = 256
|
5
|
+
|
6
|
+
chip_handle = LGPIO.chip_open(GPIO_CHIP)
|
7
|
+
one_wire = LGPIO::OneWire.new(chip_handle, PIN)
|
8
|
+
one_wire.search
|
9
|
+
|
10
|
+
puts; puts "Found these 1-wire addresss (HEX) on the bus:"; puts
|
11
|
+
|
12
|
+
one_wire.found_addresses.each do |address|
|
13
|
+
puts address.to_s(16)
|
14
|
+
end
|
15
|
+
puts
|
@@ -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)}"
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'lgpio'
|
2
|
+
|
3
|
+
GPIO_CHIP = 0
|
4
|
+
CLOCK_PIN = 22
|
5
|
+
INPUT_PIN = 17
|
6
|
+
OUTPUT_PIN = 27
|
7
|
+
|
8
|
+
MODES = [0, 1, 2, 3]
|
9
|
+
ORDERS = [:msbfirst, :lsbfirst]
|
10
|
+
TX_BYTES = [0, 1, 2, 3, 4, 5, 6, 7]
|
11
|
+
|
12
|
+
chip_handle = LGPIO.chip_open(GPIO_CHIP)
|
13
|
+
spi_bb = LGPIO::SPIBitBang.new(handle: chip_handle, clock: CLOCK_PIN, input: INPUT_PIN, output: OUTPUT_PIN)
|
14
|
+
|
15
|
+
puts "TX bytes => #{TX_BYTES.inspect}"
|
16
|
+
|
17
|
+
# Connect (loop back) INPUT_PIN to OUTPUT_PIN to see received bytes.
|
18
|
+
ORDERS.each do |order|
|
19
|
+
MODES.each do |mode|
|
20
|
+
rx_bytes = spi_bb.transfer(write: TX_BYTES, read: TX_BYTES.length, order: order, mode: mode)
|
21
|
+
puts "RX (order: #{order}, mode: #{mode}) => #{rx_bytes.inspect}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
LGPIO.chip_close(chip_handle)
|
@@ -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"
|