Class: Denko::SPI::InputRegister

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

Instance Attribute Summary

Attributes inherited from BaseRegister

#bytes

Attributes included from Peripheral

#spi_bit_order, #spi_frequency, #spi_mode

Attributes included from Behaviors::BusPeripheral

#address

Attributes included from Behaviors::Component

#board

Attributes included from Behaviors::Callbacks

#callback_mutex

Attributes included from Behaviors::SinglePin

#mode, #pin

Instance Method Summary collapse

Methods included from Behaviors::BoardProxy

#convert_pin, #high, #low, #set_pin_mode, #start_read

Methods included from Behaviors::Subcomponents

#add_component, #components, #remove_component, #single_pin_components

Methods included from Peripheral

#spi_listen, #spi_read, #spi_stop, #spi_transfer, #spi_write

Methods included from Behaviors::BusPeripheral

#atomically

Methods included from Behaviors::Component

#initialize, #micro_delay

Methods included from Behaviors::State

#initialize, #state

Methods included from Behaviors::Callbacks

#add_callback, #callbacks, #initialize, #pre_callback_filter, #remove_callback

Instance Method Details

#after_initialize(options = {}) ⇒ Object



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

def after_initialize(options={})
  super(options)
  enable_proxy
end

#any_listeningObject



54
55
56
57
# File 'lib/denko/spi/input_register.rb', line 54

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

#before_initialize(options = {}) ⇒ Object



5
6
7
8
9
10
11
12
13
# File 'lib/denko/spi/input_register.rb', line 5

def before_initialize(options={})
  super(options)
  #
  # Keep track of whether anything is listening or reading a specific pin.
  # This might be separate from whether a component is attached or not.
  #
  @reading_pins   = Array.new(@bytes*8) { false }
  @listening_pins = Array.new(@bytes*8) { 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.



114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/denko/spi/input_register.rb', line 114

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



44
45
46
47
# File 'lib/denko/spi/input_register.rb', line 44

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

#digital_read(pin) ⇒ Object

BoardProxy interface



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

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).



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

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



24
25
26
# File 'lib/denko/spi/input_register.rb', line 24

def listen
  spi_listen(@bytes)
end

#readObject



20
21
22
# File 'lib/denko/spi/input_register.rb', line 20

def read
  spi_read(@bytes)
end

#stopObject



28
29
30
# File 'lib/denko/spi/input_register.rb', line 28

def stop
  spi_stop
end

#stop_listener(pin) ⇒ Object



49
50
51
52
# File 'lib/denko/spi/input_register.rb', line 49

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.



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

def update(message)
  bits = byte_array_to_bit_array(message.split(","))

  @callback_mutex.synchronize {
    #
    # The Arduino code does not de-duplicate repeated state.
    # Do it here, but if a :force_update callback exists, run anyway.
    #
    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)
  }
  @state = bits
end

#update_component(part, pin, value) ⇒ Object



100
101
102
103
104
105
106
107
108
# File 'lib/denko/spi/input_register.rb', line 100

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.
  elsif @reading_pins[pin] && @callbacks[:force_update]
    part.update(value)
  end
end