module Beaglebone::UART

UART

Procedural methods for UART control

Summary

setup is called to initialize a UART device

Constants

SPEEDS

Valid UART speeds

UARTTEMPLATE

dts template for UART3 device

Attributes

uartmutex[RW]
uartstatus[RW]

Public Class Methods

cleanup() click to toggle source

Disable all UART devices

# File lib/beaglebone/uart.rb, line 397
def cleanup
  #reset all UARTs we've used and unload the device tree
  uartstatus.clone.keys.each { |uart| disable(uart)}
end
disable(uart) click to toggle source

Disable a UART device.

@note device trees cannot be unloaded at this time without kernel panic.

@param uart should be a symbol representing the UART device

@example

UART.disable(:UART1)
# File lib/beaglebone/uart.rb, line 376
def disable(uart)
  check_uart_valid(uart)
  check_uart_enabled(uart)

  stop_read_wait(uart)

  disable_uart_pin(UARTS[uart][:rx]) if UARTS[uart][:rx]
  disable_uart_pin(UARTS[uart][:tx]) if UARTS[uart][:tx]

  delete_uart_status(uart)
end
each_char(uart) { |data| ... } click to toggle source

Read a character from a UART device and pass it to the specified block

@param uart should be a symbol representing the UART device

@example

UART.each_char(:UART1) { |x| puts "read: #{x}" }
# File lib/beaglebone/uart.rb, line 233
def each_char(uart)
  loop do
    data = readchars(uart, 1)
    yield data
  end

end
each_chars(uart, chars) { |data| ... } click to toggle source

Read characters from a UART device and pass them to the specified block

@param uart should be a symbol representing the UART device @param chars should be the number of chars to read

@example

UART.each_chars(:UART1, 2) { |x| puts "read: #{x}" }
# File lib/beaglebone/uart.rb, line 248
def each_chars(uart, chars)
  loop do
    data = readchars(uart, chars)
    yield data
  end
end
each_line(uart) { |data| ... } click to toggle source

Read lines from a UART device and pass them to the specified block

@param uart should be a symbol representing the UART device

@example

UART.each_line(:UART1) { |x| puts "read: #{x}" }
# File lib/beaglebone/uart.rb, line 262
def each_line(uart)
  loop do
    data = readline(uart)
    yield data
  end
end
readchar(uart) click to toggle source

Read one character from a UART device

@param uart should be a symbol representing the UART device

@return String the character read from the UART device

@example

UART.readchars(:UART1) => "x"
# File lib/beaglebone/uart.rb, line 164
def readchar(uart)
  readchars(uart, 1)
end
readchars(uart, bytes) click to toggle source

Read characters from a UART device

@param uart should be a symbol representing the UART device @param bytes number of bytes to read

@return String the characters read from the UART device

@example

UART.readchars(:UART1, 2) => "xx"
# File lib/beaglebone/uart.rb, line 177
def readchars(uart, bytes)
  check_uart_enabled(uart)
  ensure_read_lock(uart)

  buffer = ''

  pin_rx = UARTS[uart][:rx]

  Beaglebone::check_valid_pin(pin_rx, :uart)

  fd = Beaglebone::get_pin_status(pin_rx, :fd_uart)

  set_uart_status(uart, :waiting, true)

  while bytes > 0 do
    buffer << fd.readchar
    bytes -= 1
  end
  set_uart_status(uart, :waiting, false)

  buffer
end
readline(uart) click to toggle source

Read a line from a UART device

@param uart should be a symbol representing the UART device

@return String the line read from the UART device

@example

UART.readline(:UART1) => "A line of text"
# File lib/beaglebone/uart.rb, line 208
def readline(uart)
  check_uart_enabled(uart)
  ensure_read_lock(uart)

  pin_rx = UARTS[uart][:rx]

  Beaglebone::check_valid_pin(pin_rx, :uart)

  fd = Beaglebone::get_pin_status(pin_rx, :fd_uart)

  set_uart_status(uart, :waiting, true)

  data = fd.readline.chop

  set_uart_status(uart, :waiting, false)

  data
end
run_on_each_char(callback, uart, repeats=nil) click to toggle source

Convenience method for run_on_each_chars with chars set to 1 @see run_on_each_chars

# File lib/beaglebone/uart.rb, line 364
def run_on_each_char(callback, uart, repeats=nil)
  run_on_each_chars(callback, uart, 1, repeats)
end
run_on_each_chars(callback, uart, chars=1, repeats=nil) click to toggle source

Runs a callback after receiving data from a UART device This creates a new thread that runs in the background

@param callback A method to call when the data is received. This method should take 3 arguments, the UART, the line read, and the counter @param uart should be a symbol representing the UART device @param chars should be the number of chars to read @param repeats is optional and specifies the number of times the callback will be run

@example

callback = lambda { |uart, data, count| puts "[#{uart}:#{count}] #{data} "}
UART.run_on_each_chars(callback, :UART1, 2)
# File lib/beaglebone/uart.rb, line 323
def run_on_each_chars(callback, uart, chars=1, repeats=nil)
  check_uart_enabled(uart)

  raise StandardError, "Already waiting for data on uart: #{uart}" if get_uart_status(uart, :waiting)
  raise StandardError, "Already waiting for data on uart: #{uart}" if get_uart_status(uart, :thread)

  thread = Thread.new(callback, uart, chars, repeats) do |c, u, ch, r|
    begin
      count = 0
      each_chars(u, ch) do |line|

        c.call(u, line, count) if c

        count += 1
        break if r && count >= r
      end
    rescue => ex
      puts ex
      puts ex.backtrace
    ensure
      delete_uart_status(u, :thread)
      set_uart_status(uart, :waiting, false)
    end
  end
  set_uart_status(uart, :thread, thread)
end
run_on_each_line(callback, uart, repeats=nil) click to toggle source

Runs a callback after receiving a line of data from a UART device This creates a new thread that runs in the background

@param callback A method to call when the data is received. This method should take 3 arguments, the UART, the line read, and the counter @param uart should be a symbol representing the UART device @param repeats is optional and specifies the number of times the callback will be run

@example

callback = lambda { |uart, line, count| puts "[#{uart}:#{count}] #{line} "}
UART.run_on_each_line(callback, :UART1)
# File lib/beaglebone/uart.rb, line 279
def run_on_each_line(callback, uart, repeats=nil)
  check_uart_enabled(uart)

  raise StandardError, "Already waiting for data on uart: #{uart}" if get_uart_status(uart, :waiting)
  raise StandardError, "Already waiting for data on uart: #{uart}" if get_uart_status(uart, :thread)

  thread = Thread.new(callback, uart, repeats) do |c, u, r|
    begin
      count = 0
      each_line(u) do |line|

        c.call(u, line, count) if c

        count += 1
        break if r && count >= r
      end
    rescue => ex
      puts ex
      puts ex.backtrace
    ensure
      delete_uart_status(u, :thread)
      set_uart_status(uart, :waiting, false)
    end
  end
  set_uart_status(uart, :thread, thread)
end
run_once_on_each_char(callback, uart) click to toggle source

Convenience method for run_on_each_chars with chars and repeats set to 1 @see run_on_each_chars

# File lib/beaglebone/uart.rb, line 352
def run_once_on_each_char(callback, uart)
  run_once_on_each_chars(callback, uart, 1)
end
run_once_on_each_chars(callback, uart, chars=1) click to toggle source

Convenience method for run_on_each_chars with chars and repeats set to 1 @see run_on_each_chars

# File lib/beaglebone/uart.rb, line 358
def run_once_on_each_chars(callback, uart, chars=1)
  run_on_each_chars(callback, uart, chars, 1)
end
run_once_on_each_line(callback, uart) click to toggle source

Convenience method for run_on_each_line with repeats set to 1 @see run_on_each_line

# File lib/beaglebone/uart.rb, line 308
def run_once_on_each_line(callback, uart)
  run_on_each_line(callback, uart, 1)
end
set_speed(uart, speed) click to toggle source

Set the speed of the UART

@param speed should be an integer thats a valid speed. @see SPEEDS

@example

UART.set_speed(:UART1, 9600)
# File lib/beaglebone/uart.rb, line 109
def set_speed(uart, speed)
  check_uart_valid(uart)
  check_speed_valid(speed)

  uartinfo = UARTS[uart]
  system("stty -F #{uartinfo[:dev]} #{speed}")
end
setup(uart, speed=9600) click to toggle source

Initialize a UART device

@param uart should be a symbol representing the UART device @param speed should be an integer thats a valid speed. @see SPEEDS

@example

UART.setup(:UART1, 9600)
# File lib/beaglebone/uart.rb, line 67
def setup(uart, speed=9600)
  check_uart_valid(uart)
  check_speed_valid(speed)

  #make sure uart not already enabled
  return if get_uart_status(uart)

  uartinfo = UARTS[uart]

  #ensure we have a dtb to load
  create_device_tree(uart)

  #ensure dtb is loaded
  Beaglebone::device_tree_load("#{TREES[:UART][:pin]}#{uartinfo[:id]}")

  #open the uart device
  uart_fd = File.open(uartinfo[:dev], 'r+')

  if uartinfo[:tx]
    Beaglebone::set_pin_status(uartinfo[:tx], :uart, uartinfo[:id])
    Beaglebone::set_pin_status(uartinfo[:tx], :type, :uart)
    Beaglebone::set_pin_status(uartinfo[:tx], :fd_uart, uart_fd)
  end

  if uartinfo[:rx]
    Beaglebone::set_pin_status(uartinfo[:rx], :uart, uartinfo[:id])
    Beaglebone::set_pin_status(uartinfo[:tx], :type, :uart)
    Beaglebone::set_pin_status(uartinfo[:rx], :fd_uart, uart_fd)
  end

  system("stty -F #{uartinfo[:dev]} raw")
  system("stty -F #{uartinfo[:dev]} #{speed}")

  set_uart_status(uart, :fd_uart, uart_fd)
end
stop_read_wait(uart) click to toggle source

Stops any threads waiting for data on the specified UART

# File lib/beaglebone/uart.rb, line 389
def stop_read_wait(uart)
  thread = get_uart_status(uart, :thread)

  thread.exit if thread
  thread.join if thread
end
write(uart, data) click to toggle source

Write data to a UART device

@param uart should be a symbol representing the UART device @param data the data to write

@return Integer the number of bytes written

@example

UART.write(:UART1, "1234") => 4
# File lib/beaglebone/uart.rb, line 126
def write(uart, data)
  check_uart_enabled(uart)

  pin_tx = UARTS[uart][:tx]

  Beaglebone::check_valid_pin(pin_tx, :uart)

  fd = Beaglebone::get_pin_status(pin_tx, :fd_uart)

  ret = fd.write(data)
  fd.flush

  ret
end
writeln(uart, data) click to toggle source

Write a line data to a UART device. This is a convenience method using write @see write

@param uart should be a symbol representing the UART device @param data the data to write

@return Integer the number of bytes written

@example

UART.writeln(:UART1, "1234") => 5
# File lib/beaglebone/uart.rb, line 152
def writeln(uart, data)
  write(uart, data + "\n")
end

Private Class Methods

check_speed_valid(speed) click to toggle source

check to make sure the specified speed is valid

# File lib/beaglebone/uart.rb, line 471
def check_speed_valid(speed)
  raise ArgumentError, "Invalid speed specified: #{speed}" unless SPEEDS.include?(speed)
end
check_uart_enabled(uart) click to toggle source

ensure UART is enabled

# File lib/beaglebone/uart.rb, line 454
def check_uart_enabled(uart)
  raise ArgumentError, "UART not enabled #{uart.to_s}" unless get_uart_status(uart)
end
check_uart_valid(uart) click to toggle source

ensure UART is valid

# File lib/beaglebone/uart.rb, line 435
def check_uart_valid(uart)
  raise ArgumentError, "Invalid UART Specified #{uart.to_s}" unless UARTS[uart]
  uartinfo = UARTS[uart.to_sym]

  if uartinfo[:tx]
    unless [nil,:uart].include?(Beaglebone::get_pin_status(uartinfo[:tx], :type))
      raise StandardError, "TX Pin for #{uart.to_s} in use"
    end
  end

  if uartinfo[:rx]
    unless [nil,:uart].include?(Beaglebone::get_pin_status(uartinfo[:rx], :type))
      raise StandardError, "RX Pin for #{uart.to_s} in use"
    end
  end

end
create_device_tree(uart, force = false) click to toggle source
# File lib/beaglebone/uart.rb, line 490
def create_device_tree(uart, force = false)
  uartinfo = UARTS[uart]

  #ensure valid uart, and a template to create this exists
  return unless uartinfo

  uartnum =  uartinfo[:id]

  return unless uartnum && UARTTEMPLATE[uartnum]

  dts = UARTTEMPLATE[uartinfo[:id]].clone

  filename = "/lib/firmware/#{TREES[:UART][:pin]}#{uartnum}-00A0"
  dts_fn = "#{filename}.dts"
  dtb_fn = "#{filename}.dtbo"

  # if we've already built this file, we don't need to do it again
  return if File.exists?(dtb_fn) && !force

  dts_file = File.open(dts_fn, 'w')
  dts_file.write(dts)
  dts_file.close

  system("dtc -O dtb -o #{dtb_fn} -b 0 -@ #{dts_fn}")

end
delete_uart_status(uart, key = nil) click to toggle source

remove hash data for specified UART

# File lib/beaglebone/uart.rb, line 424
def delete_uart_status(uart, key = nil)
  uartmutex.synchronize do
    if key.nil?
      uartstatus.delete(uart)
    else
      uartstatus[uart].delete(key) if uartstatus[uart]
    end
  end
end
disable_uart_pin(pin) click to toggle source

disable a uart pin

# File lib/beaglebone/uart.rb, line 476
def disable_uart_pin(pin)
  Beaglebone::check_valid_pin(pin, :uart)

  id = Beaglebone::get_pin_status(pin, :uart)

  Beaglebone::delete_pin_status(pin)

  #removing uart tree causes a crash... can't really disable.
  return if true

  Beaglebone::device_tree_unload("#{TREES[:UART][:pin]}#{id}")

end
ensure_read_lock(uart) click to toggle source

ensure we have a read lock for the UART

# File lib/beaglebone/uart.rb, line 459
def ensure_read_lock(uart)
  #ensure we're the only ones reading
  if get_uart_status(uart, :thread) && get_uart_status(uart, :thread) != Thread.current
    raise StandardError, "Already waiting for data on uart: #{uart}"
  end

  if get_uart_status(uart, :waiting) && get_uart_status(uart, :thread) != Thread.current
    raise StandardError, "Already waiting for data on uart: #{uart}"
  end
end
get_uart_status(uart, key = nil) click to toggle source

return hash data for specified UART

# File lib/beaglebone/uart.rb, line 405
def get_uart_status(uart, key = nil)
  uartmutex.synchronize do
    if key
      uartstatus[uart] ? uartstatus[uart][key] : nil
    else
      uartstatus[uart]
    end
  end
end
set_uart_status(uart, key, value) click to toggle source

set hash data for specified UART

# File lib/beaglebone/uart.rb, line 416
def set_uart_status(uart, key, value)
  uartmutex.synchronize do
    uartstatus[uart]    ||= {}
    uartstatus[uart][key] = value
  end
end