denko-piboard 0.13.2 → 0.14.0
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/LICENSE +1 -1
- data/README.md +154 -132
- data/Rakefile +0 -5
- data/board_maps/README.md +59 -0
- data/board_maps/orange_pi_zero_2w.yml +85 -0
- data/board_maps/radxa_zero3.yml +88 -0
- data/board_maps/raspberry_pi.yml +95 -0
- data/denko_piboard.gemspec +6 -7
- data/examples/digital_io/bench_out.rb +22 -0
- data/examples/digital_io/rotary_encoder.rb +31 -0
- data/examples/display/ssd1306.rb +43 -0
- data/examples/i2c/bitbang_aht10.rb +18 -0
- data/examples/i2c/bitbang_search.rb +24 -0
- data/examples/i2c/bitbang_ssd1306_bench.rb +29 -0
- data/examples/i2c/search.rb +24 -0
- data/examples/led/blink.rb +10 -0
- data/examples/led/fade.rb +22 -0
- data/examples/led/ws2812_bounce.rb +36 -0
- data/examples/motor/servo.rb +16 -0
- data/examples/pi_system_monitor.rb +10 -8
- data/examples/pulse_io/buzzer.rb +34 -0
- data/examples/pulse_io/infrared.rb +25 -0
- data/examples/sensor/aht10.rb +17 -0
- data/examples/sensor/dht.rb +24 -0
- data/examples/sensor/ds18b20.rb +59 -0
- data/examples/sensor/hcsr04.rb +16 -0
- data/examples/sensor/neat_tph_readings.rb +32 -0
- data/examples/spi/bb_loopback.rb +31 -0
- data/examples/spi/loopback.rb +37 -0
- data/examples/spi/output_register.rb +38 -0
- data/lib/denko/piboard.rb +11 -2
- data/lib/denko/piboard_base.rb +18 -64
- data/lib/denko/piboard_core.rb +148 -130
- data/lib/denko/piboard_core_optimize_lookup.rb +31 -0
- data/lib/denko/piboard_hardware_pwm.rb +27 -0
- data/lib/denko/piboard_i2c.rb +59 -82
- data/lib/denko/piboard_i2c_bb.rb +48 -0
- data/lib/denko/piboard_infrared.rb +7 -44
- data/lib/denko/piboard_led_array.rb +9 -0
- data/lib/denko/piboard_map.rb +121 -38
- data/lib/denko/piboard_one_wire.rb +42 -0
- data/lib/denko/piboard_pulse.rb +11 -68
- data/lib/denko/piboard_servo.rb +8 -7
- data/lib/denko/piboard_spi.rb +44 -74
- data/lib/denko/piboard_spi_bb.rb +41 -0
- data/lib/denko/piboard_tone.rb +15 -26
- data/lib/denko/piboard_version.rb +1 -1
- data/scripts/99-denko.rules +9 -0
- data/scripts/set_permissions.rb +131 -0
- metadata +45 -17
- data/ext/gpiod/extconf.rb +0 -9
- data/ext/gpiod/gpiod.c +0 -179
- data/lib/gpiod.rb +0 -6
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'denko'
|
2
|
+
require 'denko/piboard'
|
3
|
+
|
4
|
+
CLOCK_PIN = 256
|
5
|
+
INPUT_PIN = 271
|
6
|
+
OUTPUT_PIN = 258
|
7
|
+
SELECT_PIN = 272
|
8
|
+
|
9
|
+
board = Denko::PiBoard.new
|
10
|
+
bus = Denko::SPI::BitBang.new(board: board, pins: { clock: CLOCK_PIN, input: INPUT_PIN, output: OUTPUT_PIN })
|
11
|
+
|
12
|
+
TEST_DATA = [0, 1, 2, 3, 4, 5, 6, 7]
|
13
|
+
|
14
|
+
# Create a simple test component class.
|
15
|
+
class SPITester
|
16
|
+
include Denko::SPI::Peripheral::SinglePin
|
17
|
+
end
|
18
|
+
spi_tester = SPITester.new(bus: bus, pin: SELECT_PIN)
|
19
|
+
spi_tester.add_callback do |rx_bytes|
|
20
|
+
# If MOSI and MISO are connected this should match TEST_DATA.
|
21
|
+
# If not, should be 8 bytes of 255.
|
22
|
+
puts "RX bytes: #{rx_bytes.inspect}"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Send the test data.
|
26
|
+
puts "TX bytes: #{TEST_DATA.inspect}"
|
27
|
+
spi_tester.spi_transfer(write: TEST_DATA, read: 8)
|
28
|
+
|
29
|
+
# Wait for read callback to run.
|
30
|
+
sleep 1
|
31
|
+
board.finish_write
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'denko'
|
2
|
+
require 'denko/piboard'
|
3
|
+
|
4
|
+
# Use board map from ~/.denko_piboard_map.yml
|
5
|
+
board = Denko::PiBoard.new
|
6
|
+
|
7
|
+
# Use the first hardware SPI interface, and its defined :cs0 pin.
|
8
|
+
spi_index = board.map[:spis].keys.first
|
9
|
+
chip_select = board.map[:spis][spi_index][:cs0]
|
10
|
+
bus = Denko::SPI::Bus.new(board: board, index: spi_index)
|
11
|
+
|
12
|
+
TEST_DATA = [0, 1, 2, 3, 4, 5, 6, 7]
|
13
|
+
|
14
|
+
# Create a simple test component class.
|
15
|
+
class SPITester
|
16
|
+
include Denko::SPI::Peripheral::SinglePin
|
17
|
+
end
|
18
|
+
spi_tester = SPITester.new(bus: bus, pin: chip_select)
|
19
|
+
spi_tester.add_callback do |rx_bytes|
|
20
|
+
# If MOSI and MISO are connected this should match TEST_DATA.
|
21
|
+
# If not, should be 8 bytes of 255.
|
22
|
+
puts "Result : #{rx_bytes.inspect}"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Send and receive same data.
|
26
|
+
puts "Tx 8 / Rx 8 : #{TEST_DATA.inspect}"
|
27
|
+
spi_tester.spi_transfer(write: TEST_DATA, read: 8)
|
28
|
+
|
29
|
+
puts "Tx 8 / Rx 12: #{TEST_DATA.inspect}"
|
30
|
+
spi_tester.spi_transfer(write: TEST_DATA, read: 12)
|
31
|
+
|
32
|
+
puts "Tx 8 / Rx 4 : #{TEST_DATA.inspect}"
|
33
|
+
spi_tester.spi_transfer(write: TEST_DATA, read: 4)
|
34
|
+
|
35
|
+
# Wait for read callback to run.
|
36
|
+
sleep 1
|
37
|
+
board.finish_write
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#
|
2
|
+
# Example of LED connected through an output shift register (74HC595).
|
3
|
+
# Can be used over either a bit bang or hardware SPI interface.
|
4
|
+
#
|
5
|
+
require 'denko/piboard'
|
6
|
+
|
7
|
+
LED_PIN = 3 # On the register's parallel outputs
|
8
|
+
|
9
|
+
# Use board map from ~/.denko_piboard_map.yml
|
10
|
+
board = Denko::PiBoard.new
|
11
|
+
|
12
|
+
# Use the first hardware SPI interface, and its defined :cs0 pin.
|
13
|
+
spi_index = board.map[:spis].keys.first
|
14
|
+
chip_select = board.map[:spis][spi_index][:cs0]
|
15
|
+
bus = Denko::SPI::Bus.new(board: board, index: spi_index)
|
16
|
+
|
17
|
+
# OutputRegister needs a bus and pin (chip select).
|
18
|
+
# Other options and their defaults:
|
19
|
+
# bytes: 1 - For daisy-chaining registers
|
20
|
+
# spi_frequency: 1000000 - Only affects hardware SPI interfaces
|
21
|
+
# spi_mode: 0
|
22
|
+
# spi_bit_order: :msbfirst
|
23
|
+
#
|
24
|
+
register = Denko::SPI::OutputRegister.new(bus: bus, pin: chip_select)
|
25
|
+
|
26
|
+
# Turn on the LED by setting the corresponding bit, then writing to the register.
|
27
|
+
register.bit_set(LED_PIN, 1)
|
28
|
+
register.write
|
29
|
+
|
30
|
+
# OutputRegister is a BoardProxy. It has #digital_write, and other methods from Board.
|
31
|
+
register.digital_write(LED_PIN, 0)
|
32
|
+
|
33
|
+
# DigitalOutputs can treat it as a Board.
|
34
|
+
led = Denko::LED.new(board: register, pin: LED_PIN)
|
35
|
+
|
36
|
+
# Blink the LED and sleep the main thread.
|
37
|
+
led.blink 0.5
|
38
|
+
sleep
|
data/lib/denko/piboard.rb
CHANGED
@@ -1,11 +1,20 @@
|
|
1
|
-
require_relative '../gpiod'
|
2
1
|
require_relative 'piboard_version'
|
3
2
|
require_relative 'piboard_map'
|
4
3
|
require_relative 'piboard_base'
|
4
|
+
|
5
5
|
require_relative 'piboard_core'
|
6
|
+
|
6
7
|
require_relative 'piboard_pulse'
|
7
|
-
require_relative 'piboard_servo'
|
8
8
|
require_relative 'piboard_tone'
|
9
|
+
|
10
|
+
require_relative 'piboard_hardware_pwm'
|
11
|
+
require_relative 'piboard_servo'
|
9
12
|
require_relative 'piboard_infrared'
|
13
|
+
|
10
14
|
require_relative 'piboard_i2c'
|
11
15
|
require_relative 'piboard_spi'
|
16
|
+
require_relative 'piboard_led_array'
|
17
|
+
|
18
|
+
require_relative 'piboard_i2c_bb'
|
19
|
+
require_relative 'piboard_spi_bb'
|
20
|
+
require_relative 'piboard_one_wire'
|
data/lib/denko/piboard_base.rb
CHANGED
@@ -1,81 +1,35 @@
|
|
1
1
|
require 'denko'
|
2
|
-
require '
|
2
|
+
require 'lgpio'
|
3
3
|
|
4
4
|
module Denko
|
5
5
|
class PiBoard
|
6
|
-
include
|
7
|
-
|
8
|
-
attr_reader :high, :low, :pwm_high
|
9
|
-
|
10
|
-
def initialize
|
11
|
-
# On 64-bit systems there's a pigpio bug where wavelengths are doubled.
|
12
|
-
@aarch64 = RUBY_PLATFORM.match(/aarch64/)
|
13
|
-
|
14
|
-
# Pin state
|
15
|
-
@pins = []
|
16
|
-
@pwms = []
|
17
|
-
|
18
|
-
# Listener state
|
19
|
-
@pin_listeners = []
|
20
|
-
@listen_mutex = Mutex.new
|
21
|
-
@listen_states = Array.new(32) { 0 }
|
22
|
-
@listen_thread = nil
|
23
|
-
@listen_reading = 0
|
24
|
-
|
25
|
-
# PiGPIO callback state. Unused for now.
|
26
|
-
@pin_callbacks = []
|
6
|
+
include Behaviors::Subcomponents
|
27
7
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
8
|
+
LOW = 0
|
9
|
+
HIGH = 1
|
10
|
+
PWM_HIGH = 100
|
11
|
+
def low; LOW; end
|
12
|
+
def high; HIGH; end
|
13
|
+
def pwm_high; PWM_HIGH; end
|
14
|
+
def analog_write_resolution; 8; end
|
15
|
+
|
16
|
+
def initialize(map_yaml_file=nil)
|
17
|
+
map_yaml_file ||= Dir.home + "/" + DEFAULT_MAP_FILE
|
18
|
+
unless File.exist?(map_yaml_file)
|
19
|
+
raise StandardError, "board map file not given to PiBoard#new, and does not exist at #{map_yaml_file}"
|
20
|
+
end
|
32
21
|
|
33
|
-
|
34
|
-
@pi_handle = Pigpio::IF.pigpio_start
|
35
|
-
exit(-1) if @pi_handle < 0
|
36
|
-
|
37
|
-
# Open the libgpiod interface too.
|
38
|
-
Denko::GPIOD.open_chip
|
22
|
+
parse_map(map_yaml_file)
|
39
23
|
end
|
40
24
|
|
41
25
|
def finish_write
|
42
|
-
|
43
|
-
Denko::GPIOD.close_chip
|
26
|
+
gpio_handles.each { |h| LGPIO.chip_close(h) if h }
|
44
27
|
end
|
45
28
|
|
46
|
-
|
47
|
-
# Use standard Subcomponents behavior.
|
48
|
-
#
|
49
|
-
include Behaviors::Subcomponents
|
50
|
-
|
51
|
-
def update(pin, message, time=nil)
|
29
|
+
def update(pin, message)
|
52
30
|
if single_pin_components[pin]
|
53
31
|
single_pin_components[pin].update(message)
|
54
32
|
end
|
55
33
|
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
attr_reader :pi_handle, :wave
|
60
|
-
|
61
|
-
def get_gpio(pin)
|
62
|
-
@pins[pin] ||= Pigpio::UserGPIO.new(@pi_handle, pin)
|
63
|
-
end
|
64
|
-
|
65
|
-
def pwm_clear(pin)
|
66
|
-
@pwms[pin] = nil
|
67
|
-
end
|
68
|
-
|
69
|
-
def new_wave
|
70
|
-
stop_wave
|
71
|
-
@wave = Pigpio::Wave.new(pi_handle)
|
72
|
-
end
|
73
|
-
|
74
|
-
def stop_wave
|
75
|
-
return unless wave
|
76
|
-
wave.tx_stop
|
77
|
-
wave.clear
|
78
|
-
@wave = nil
|
79
|
-
end
|
80
34
|
end
|
81
35
|
end
|
data/lib/denko/piboard_core.rb
CHANGED
@@ -1,94 +1,120 @@
|
|
1
1
|
module Denko
|
2
2
|
class PiBoard
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
gpio.mode = PI_OUTPUT
|
14
|
-
|
15
|
-
# Use pigpiod for setup, but still open line in libgpiod.
|
16
|
-
Denko::GPIOD.open_line_output(pin)
|
17
|
-
|
18
|
-
# Input
|
19
|
-
else
|
20
|
-
gpio.mode = PI_INPUT
|
21
|
-
|
22
|
-
# State change valid only if steady for this many microseconds.
|
23
|
-
# Only applies to callbacks hooked through pigpiod.
|
24
|
-
if glitch_time
|
25
|
-
gpio.glitch_filter(glitch_time)
|
26
|
-
end
|
3
|
+
REPORT_SLEEP_TIME = 0.001
|
4
|
+
INPUT_MODES = [:input, :input_pullup, :input_pulldown]
|
5
|
+
OUTPUT_MODES = [:output, :output_pwm, :output_open_drain, :output_open_source]
|
6
|
+
PIN_MODES = INPUT_MODES + OUTPUT_MODES
|
7
|
+
|
8
|
+
def set_pin_mode(pin, mode=:input, options={})
|
9
|
+
# Is the mode valid?
|
10
|
+
unless PIN_MODES.include?(mode)
|
11
|
+
raise ArgumentError, "cannot set mode: #{mode}. Should be one of: #{PIN_MODES.inspect}"
|
12
|
+
end
|
27
13
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
14
|
+
# If pin is bound to hardware PWM, allow it to be used as :output_pwm. OR :output.
|
15
|
+
if map[:pwms][pin]
|
16
|
+
if (mode == :output_pwm)
|
17
|
+
return hardware_pwm_from_pin(pin, options)
|
18
|
+
elsif (mode == :output)
|
19
|
+
puts "WARNING: using hardware PWM on pin #{pin} as GPIO. Will be slower than regular GPIO."
|
20
|
+
return hardware_pwm_from_pin(pin, options)
|
33
21
|
else
|
34
|
-
|
22
|
+
raise "Pin #{pin} is bound to hardware PWM. It can only be used as :output or :output_pwm"
|
35
23
|
end
|
36
|
-
|
37
|
-
# Use pigpiod for setup, but still open line in libgpiod.
|
38
|
-
Denko::GPIOD.open_line_input(pin)
|
39
24
|
end
|
25
|
+
|
26
|
+
# Attempt to free the pin.
|
27
|
+
LGPIO.gpio_free(*gpio_tuple(pin))
|
28
|
+
|
29
|
+
# Try to claim the GPIO.
|
30
|
+
if OUTPUT_MODES.include?(mode)
|
31
|
+
flags = LGPIO::SET_PULL_NONE
|
32
|
+
flags = LGPIO::SET_OPEN_DRAIN if mode == :output_open_drain
|
33
|
+
flags = LGPIO::SET_OPEN_SOURCE if mode == :output_open_source
|
34
|
+
result = LGPIO.gpio_claim_output(*gpio_tuple(pin), flags, LOW)
|
35
|
+
else
|
36
|
+
flags = LGPIO::SET_PULL_NONE
|
37
|
+
flags = LGPIO::SET_PULL_UP if mode == :input_pullup
|
38
|
+
flags = LGPIO::SET_PULL_DOWN if mode == :input_pulldown
|
39
|
+
result = LGPIO.gpio_claim_input(*gpio_tuple(pin), flags)
|
40
|
+
end
|
41
|
+
raise "could not claim GPIO for pin #{pin}. lgpio C error: #{result}" if result < 0
|
42
|
+
|
43
|
+
pin_configs[pin] = pin_configs[pin].to_h.merge(mode: mode).merge(options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def set_pin_debounce(pin, debounce_time)
|
47
|
+
return unless debounce_time
|
48
|
+
result = LGPIO.gpio_set_debounce(*gpio_tuple(pin), debounce_time)
|
49
|
+
raise "could not set debounce for pin #{pin}. lgpio C error: #{result}" if result < 0
|
50
|
+
|
51
|
+
pin_configs[pin] = pin_configs[pin].to_h.merge(debounce_time: debounce_time)
|
40
52
|
end
|
41
53
|
|
42
|
-
# CMD = 1
|
43
54
|
def digital_write(pin, value)
|
44
|
-
|
45
|
-
|
55
|
+
if hardware_pwms[pin]
|
56
|
+
hardware_pwms[pin].duty_percent = (value == 0) ? 0 : 100
|
57
|
+
else
|
58
|
+
handle, line = gpio_tuple(pin)
|
59
|
+
LGPIO.gpio_write(handle, line, value)
|
60
|
+
end
|
46
61
|
end
|
47
|
-
|
48
|
-
# CMD = 2
|
62
|
+
|
49
63
|
def digital_read(pin)
|
50
|
-
|
51
|
-
state =
|
52
|
-
|
53
|
-
|
64
|
+
if hardware_pwms[pin]
|
65
|
+
state = hardware_pwms[pin].duty_percent
|
66
|
+
else
|
67
|
+
handle, line = gpio_tuple(pin)
|
68
|
+
state = LGPIO.gpio_read(handle, line)
|
54
69
|
end
|
70
|
+
self.update(pin, state)
|
71
|
+
return state
|
55
72
|
end
|
56
|
-
|
57
|
-
# CMD = 3
|
58
|
-
def pwm_write(pin, value)
|
59
|
-
# Disable servo if necessary.
|
60
|
-
pwm_clear(pin) if @pwms[pin] == :servo
|
61
73
|
|
62
|
-
|
63
|
-
|
64
|
-
|
74
|
+
def pwm_write(pin, duty)
|
75
|
+
if hardware_pwms[pin]
|
76
|
+
hardware_pwms[pin].duty_percent = duty
|
77
|
+
else
|
78
|
+
frequency = pin_configs[pin][:frequency] || 1000
|
79
|
+
handle, line = gpio_tuple(pin)
|
80
|
+
LGPIO.tx_pwm(handle, line, frequency, duty, 0, 0)
|
65
81
|
end
|
66
|
-
@pwms[pin].dutycycle = value
|
67
82
|
end
|
68
83
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
if state == :on && !@pin_callbacks[pin]
|
73
|
-
callback = get_gpio(pin).callback(EITHER_EDGE) do |tick, level, pin_cb|
|
74
|
-
update(pin_cb, level, tick)
|
75
|
-
end
|
76
|
-
@pin_callbacks[pin] = callback
|
84
|
+
def dac_write(pin, value)
|
85
|
+
raise NotImplementedError, "PiBoard#dac_write not implemented"
|
86
|
+
end
|
77
87
|
|
78
|
-
|
79
|
-
|
80
|
-
@pin_callbacks[pin].cancel if @pin_callbacks[pin]
|
81
|
-
@pin_callbacks[pin] = nil
|
82
|
-
end
|
88
|
+
def analog_read(pin, negative_pin=nil, gain=nil, sample_rate=nil)
|
89
|
+
raise NotImplementedError, "PiBoard#analog_read not implemented"
|
83
90
|
end
|
84
91
|
|
85
|
-
# CMD = 6
|
86
92
|
def set_listener(pin, state=:off, options={})
|
87
|
-
#
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
93
|
+
# Validate listener is digital only.
|
94
|
+
options[:mode] ||= :digital
|
95
|
+
unless options[:mode] == :digital
|
96
|
+
raise ArgumentError, "error in mode: #{options[:mode]}. Should be one of: [:digital]"
|
97
|
+
end
|
98
|
+
|
99
|
+
# Validate state.
|
100
|
+
unless (state == :on) || (state == :off)
|
101
|
+
raise ArgumentError, "error in state: #{options[:state]}. Should be one of: [:on, :off]"
|
102
|
+
end
|
103
|
+
|
104
|
+
# Only way to stop getting alerts is to free the GPIO.
|
105
|
+
LGPIO.gpio_free(*gpio_tuple(pin))
|
106
|
+
|
107
|
+
# Reclaim it as input if needed.
|
108
|
+
config = pin_configs[pin]
|
109
|
+
config ||= { mode: :input, debounce_time: nil } if state == :on
|
110
|
+
if config
|
111
|
+
set_pin_mode(pin, config[:mode])
|
112
|
+
set_pin_debounce(pin, config[:debounce_time])
|
113
|
+
end
|
114
|
+
|
115
|
+
if state == :on
|
116
|
+
LGPIO.gpio_claim_alert(*gpio_tuple(pin), 0, LGPIO::BOTH_EDGES)
|
117
|
+
start_alert_thread unless @alert_thread
|
92
118
|
end
|
93
119
|
end
|
94
120
|
|
@@ -96,77 +122,69 @@ module Denko
|
|
96
122
|
set_listener(pin, :on, {})
|
97
123
|
end
|
98
124
|
|
125
|
+
def analog_listen(pin, divider=16)
|
126
|
+
raise NotImplementedError, "PiBoard#analog_read not implemented"
|
127
|
+
end
|
128
|
+
|
99
129
|
def stop_listener(pin)
|
100
130
|
set_listener(pin, :off)
|
101
131
|
end
|
102
132
|
|
103
|
-
def
|
104
|
-
|
105
|
-
@pin_listeners |= [pin]
|
106
|
-
@pin_listeners.sort!
|
107
|
-
@listen_states[pin] = Denko::GPIOD.get_value(pin)
|
108
|
-
end
|
109
|
-
start_listen_thread
|
133
|
+
def halt_resume_check
|
134
|
+
raise NotImplementedError, "PiBoard#halt_resume_check not implemented"
|
110
135
|
end
|
111
|
-
|
112
|
-
def
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
136
|
+
|
137
|
+
def set_register_divider(value)
|
138
|
+
raise NotImplementedError, "PiBoard#set_register_divider not implemented"
|
139
|
+
end
|
140
|
+
|
141
|
+
def set_analog_write_resolution(value)
|
142
|
+
raise NotImplementedError, "PiBoard#set_analog_write_resolution not implemented"
|
143
|
+
end
|
144
|
+
|
145
|
+
def set_analog_read_resolution(value)
|
146
|
+
raise NotImplementedError, "PiBoard#set_analog_read_resolution not implemented"
|
147
|
+
end
|
148
|
+
|
149
|
+
def binary_echo(pin, data=[])
|
150
|
+
raise NotImplementedError, "PiBoard#binary_echo not implemented"
|
151
|
+
end
|
152
|
+
|
153
|
+
def micro_delay(duration)
|
154
|
+
LGPIO.micro_delay(duration)
|
155
|
+
end
|
156
|
+
|
157
|
+
def start_alert_thread
|
158
|
+
start_gpio_reports
|
159
|
+
@alert_thread = Thread.new { loop { get_report } }
|
160
|
+
end
|
161
|
+
|
162
|
+
def stop_alert_thread
|
163
|
+
Thread.kill(@alert_thread) if @alert_thread
|
164
|
+
@alert_thread = nil
|
117
165
|
end
|
118
|
-
|
119
|
-
def
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
# targeting even timing of 1 millisecond between loops.
|
126
|
-
#
|
127
|
-
@listen_count = 0
|
128
|
-
@listen_sleep = 0.001
|
129
|
-
start_time = Time.now
|
130
|
-
|
131
|
-
loop do
|
132
|
-
@listen_mutex.synchronize do
|
133
|
-
@pin_listeners.each do |pin|
|
134
|
-
@listen_reading = Denko::GPIOD.get_value(pin)
|
135
|
-
self.update(pin, @listen_reading) if (@listen_reading != @listen_states[pin])
|
136
|
-
@listen_states[pin] = @listen_reading
|
137
|
-
end
|
166
|
+
|
167
|
+
def get_report
|
168
|
+
report = LGPIO.gpio_get_report
|
169
|
+
if report
|
170
|
+
if chip = alert_lut[report[:chip]]
|
171
|
+
if pin = chip[report[:gpio]]
|
172
|
+
update(pin, report[:level])
|
138
173
|
end
|
139
|
-
@listen_count += 1
|
140
|
-
sleep(@listen_sleep)
|
141
174
|
end
|
175
|
+
else
|
176
|
+
sleep 0.001
|
142
177
|
end
|
178
|
+
end
|
143
179
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
count1 = @listen_count
|
149
|
-
sleep(5)
|
150
|
-
time2 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
151
|
-
count2 = @listen_count
|
152
|
-
|
153
|
-
# Quick maths.
|
154
|
-
loops = count2 - count1
|
155
|
-
time = time2 - time1
|
156
|
-
active_time_per_loop = (time - (loops * @listen_sleep)) / loops
|
157
|
-
|
158
|
-
# Target 1 millisecond.
|
159
|
-
@listen_sleep = 0.001 - active_time_per_loop
|
160
|
-
@listen_sleep = 0 if @listen_sleep < 0
|
161
|
-
end
|
162
|
-
end
|
180
|
+
def start_gpio_reports
|
181
|
+
return if @reporting_started
|
182
|
+
LGPIO.gpio_start_reporting
|
183
|
+
@reporting_started = true
|
163
184
|
end
|
164
|
-
|
165
|
-
def
|
166
|
-
@
|
167
|
-
@listen_monitor_thread = nil
|
168
|
-
@listen_thread.kill
|
169
|
-
@listen_thread = nil
|
185
|
+
|
186
|
+
def pin_configs
|
187
|
+
@pin_configs ||= []
|
170
188
|
end
|
171
189
|
end
|
172
190
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#
|
2
|
+
# DO NOT REQUIRE THIS FILE. It is evaluated at runtime, if applicable.
|
3
|
+
#
|
4
|
+
# Optimized method overrides when all GPIO pins are on one gpiochip.
|
5
|
+
#
|
6
|
+
def digital_write(pin, value)
|
7
|
+
if hardware_pwms[pin]
|
8
|
+
hardware_pwms[pin].duty_percent = (value == 0) ? 0 : 100
|
9
|
+
else
|
10
|
+
LGPIO.gpio_write(__GPIOCHIP_SINGLE_HANDLE__, pin, value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def digital_read(pin)
|
15
|
+
if hardware_pwms[pin]
|
16
|
+
state = hardware_pwms[pin].duty_percent
|
17
|
+
else
|
18
|
+
state = LGPIO.gpio_read(__GPIOCHIP_SINGLE_HANDLE__, pin)
|
19
|
+
end
|
20
|
+
self.update(pin, state)
|
21
|
+
return state
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_report
|
25
|
+
report = LGPIO.gpio_get_report
|
26
|
+
if report
|
27
|
+
update(report[:gpio], report[:level])
|
28
|
+
else
|
29
|
+
sleep REPORT_SLEEP_TIME
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Denko
|
2
|
+
class PiBoard
|
3
|
+
def hardware_pwms
|
4
|
+
@hardware_pwms ||= []
|
5
|
+
end
|
6
|
+
|
7
|
+
def hardware_pwm_from_pin(pin, options={})
|
8
|
+
# Find existing hardware PWM, change the frequency if needed, then return it.
|
9
|
+
frequency = options[:frequency]
|
10
|
+
pwm = hardware_pwms[pin]
|
11
|
+
if pwm
|
12
|
+
pwm.frequency = frequency if (frequency && pwm.frequency != frequency)
|
13
|
+
return pwm
|
14
|
+
end
|
15
|
+
|
16
|
+
# Make sure it's in the board map before trying to use it.
|
17
|
+
raise StandardError, "no hardware PWM in board map for pin #{pin}" unless map[:pwms][pin]
|
18
|
+
|
19
|
+
# Make a new hardware PWM.
|
20
|
+
pwmchip = map[:pwms][pin][:pwmchip]
|
21
|
+
channel = map[:pwms][pin][:channel]
|
22
|
+
frequency ||= 1000
|
23
|
+
pwm = LGPIO::HardwarePWM.new(pwmchip, channel, frequency: frequency)
|
24
|
+
hardware_pwms[pin] = pwm
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|