Class: Denko::SPI::InputRegister

Inherits:
BaseRegister show all
Includes:
Behaviors::Lifecycle
Defined in:
lib/denko/spi/input_register.rb

Constant Summary

Constants included from Behaviors::Lifecycle

Behaviors::Lifecycle::CALLBACK_METHODS

Instance Attribute Summary

Attributes inherited from BaseRegister

#bytes

Attributes included from Peripheral

#spi_bit_order, #spi_frequency, #spi_mode

Attributes included from Behaviors::State

#state

Attributes included from Behaviors::Component

#board, #params

Attributes included from Behaviors::MultiPin

#pin, #pins, #proxies

Instance Method Summary collapse

Methods included from Behaviors::Lifecycle

included

Methods inherited from BaseRegister

#is_a_register?, #platform, #state

Methods included from Behaviors::BoardProxy

#analog_read_high, #analog_write_high, #convert_pin, #high, #low, #set_pin_mode, #start_read

Methods included from Behaviors::Subcomponents

#add_component, #add_hw_i2c, #add_hw_spi, #add_single_pin, #components, #hw_i2c_comps, #hw_spi_comps, #remove_component, #remove_hw_i2c, #remove_hw_spi, #remove_single_pin, #single_pin_components

Methods included from Peripheral

#ensure_byte_array, #initialize_pins, #proxy_pin, #spi_listen, #spi_read, #spi_stop, #spi_transfer, #spi_write

Methods included from Behaviors::Callbacks

#add_callback, #callbacks, #pre_callback_filter, #remove_callback

Methods included from Behaviors::State

#update_state

Methods included from Behaviors::BusPeripheral

#atomically

Methods included from Behaviors::Component

#initialize, #micro_delay

Methods included from Behaviors::MultiPin

#convert_pins, #proxy_pin, #proxy_states, #require_pin, #require_pins

Instance Method Details

#any_listeningObject



56
57
58
59
# File 'lib/denko/spi/input_register.rb', line 56

def any_listening
  listening_pins.each { |p| return true if p }
  false
end

#byte_array_to_bit_array(byte_array) ⇒ Object

Convert array of bytes coming from the register into an array of bits to update self state and give to component callbacks.



120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/denko/spi/input_register.rb', line 120

def byte_array_to_bit_array(byte_array)
  #
  # For each array element (1 byte):
  # decimal number as string -> integer -> padded string of binary digits
  # -> reverse digits from reading order to array indexing order
  # -> join digits of all bytes into one string
  #
  binary_string = byte_array.map do |byte|
    byte.to_i.to_s(2).rjust(8, "0").reverse
  end.join

  # Split the digits out of the string into individual integers.
  binary_string.split("").map { |bit| bit.to_i }
end

#digital_listen(pin, divider) ⇒ Object



46
47
48
49
# File 'lib/denko/spi/input_register.rb', line 46

def digital_listen(pin, divider)
  listen unless any_listening
  listening_pins[pin] = true
end

#digital_read(pin) ⇒ Object

BoardProxy interface



37
38
39
40
41
42
43
44
# File 'lib/denko/spi/input_register.rb', line 37

def digital_read(pin)
  # Remember what pin was read and force callbacks to run next update.
  add_callback(:force_update) { Proc.new{} }
  reading_pins[pin] = true

  # Don't actually call #read if already listening.
  read unless any_listening
end

#enable_proxyObject

Mimic Board#update, but in a callback fired through #update. This doesn’t interfere with using the register directly, and doesn’t fire if the board isn’t acting as a proxy (no components).



66
67
68
69
70
71
72
73
74
75
# File 'lib/denko/spi/input_register.rb', line 66

def enable_proxy
  self.add_callback(:board_proxy) do |bit_array|
    bit_array.each_with_index do |value, pin|
      components.each do |part|
        update_component(part, pin, value) if pin == part.pin
      end
      reading_pins[pin] = false
    end
  end
end

#listenObject



26
27
28
# File 'lib/denko/spi/input_register.rb', line 26

def listen
  spi_listen(bytes)
end

#listening_pinsObject



14
15
16
# File 'lib/denko/spi/input_register.rb', line 14

def listening_pins
  @listening_pins ||= Array.new(bytes*8) { false }
end

#readObject



22
23
24
# File 'lib/denko/spi/input_register.rb', line 22

def read
  spi_read(bytes)
end

#reading_pinsObject

Keep track of whether anything is listening or reading a specific pin. This might be separate from whether a component is attached or not.



10
11
12
# File 'lib/denko/spi/input_register.rb', line 10

def reading_pins
  @reading_pins ||= Array.new(bytes*8) { false }
end

#stopObject



30
31
32
# File 'lib/denko/spi/input_register.rb', line 30

def stop
  spi_stop
end

#stop_listener(pin) ⇒ Object



51
52
53
54
# File 'lib/denko/spi/input_register.rb', line 51

def stop_listener(pin)
  listening_pins[pin] = false
  stop unless any_listening
end

#update(message) ⇒ Object

Override Callbacks#update to make sure we handle :force_update within the main mutex lock.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/denko/spi/input_register.rb', line 81

def update(message)
  # Since overriding update, make sure this is a byte array first.
  byte_array = ensure_byte_array(message)

  bits = byte_array_to_bit_array(byte_array)

  @callback_mutex.lock
  if @callbacks && !@callbacks.empty?
    # Arduino doesn't de-duplicate state. Do it, but honor :force_update callbacks.
    if (bits != state) || @callbacks[:force_update]
      @callbacks.each_value do |array|
        array.each { |callback| callback.call(bits) }
      end
    end

    # Remove both :read and :force update while inside the lock.
    @callbacks.delete(:read)
    @callbacks.delete(:force_update)
  end
  @callback_mutex.unlock

  self.state = bits
end

#update_component(part, pin, value) ⇒ Object



105
106
107
108
109
110
111
112
113
114
# File 'lib/denko/spi/input_register.rb', line 105

def update_component(part, pin, value)
  # Update if component is listening and value has changed.
  if listening_pins[pin] && (value != state[pin])
    part.update(value)
  # Also update if the component forced a read.
  # Always called inside @callback_mutex, so @callbacks, not callbacks
  elsif reading_pins[pin] && @callbacks[:force_update]
    part.update(value)
  end
end