module Beaglebone::SPI

SPI

Procedural methods for SPI control

Summary

setup is called to initialize an SPI device

Constants

IOC_DIRBITS
IOC_DIRSHIFT
IOC_NONE

some ioctl defines

IOC_NRBITS
IOC_NRSHIFT
IOC_READ
IOC_SIZEBITS
IOC_SIZESHIFT
IOC_TYPEBITS
IOC_TYPESHIFT
IOC_WRITE
SPI_3WIRE
SPI_CPHA

spi defines

SPI_CPOL
SPI_CS_HIGH
SPI_IOC_MAGIC
SPI_IOC_MESSAGE_1
SPI_IOC_RD_BITS_PER_WORD
SPI_IOC_RD_LSB_FIRST
SPI_IOC_RD_MAX_SPEED_HZ
SPI_IOC_RD_MODE
SPI_IOC_TRANSFER_STRUCT_SIZE
SPI_IOC_WR_BITS_PER_WORD
SPI_IOC_WR_LSB_FIRST
SPI_IOC_WR_MAX_SPEED_HZ
SPI_IOC_WR_MODE
SPI_LOOP
SPI_LSB_FIRST
SPI_MODES
SPI_MODE_0
SPI_MODE_1
SPI_MODE_2
SPI_MODE_3
SPI_NO_CS
SPI_READY

Attributes

spimutex[RW]
spistatus[RW]

Public Class Methods

cleanup() click to toggle source

Disable all active SPI devices

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

Disable the specified SPI device

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

@param spi should be a symbol representing the SPI device

# File lib/beaglebone/spi.rb, line 266
def disable(spi)
  check_spi_valid(spi)
  check_spi_enabled(spi)

  SPIS[spi][:pins].each do |pin|
    disable_spi_pin(pin)
  end

  delete_spi_status(spi)

  #removing spi tree causes a crash... can't really disable.
  #Beaglebone::device_tree_unload("#{SPIS[spi][:devicetree]}") if SPIS[spi][:devicetree]

end
file(spi) click to toggle source

Return the file descriptor to the open SPI device

@param spi should be a symbol representing the SPI device

# File lib/beaglebone/spi.rb, line 184
def file(spi)
  check_spi_enabled(spi)
  get_spi_status(spi, :fd_spi)
end
set_bpw(spi, bpw) click to toggle source

Set the bits per word of the specified SPI device

@param spi should be a symbol representing the SPI device @param bpw should specify the bits per word

# File lib/beaglebone/spi.rb, line 241
def set_bpw(spi, bpw)
  bpw = bpw.to_i
  raise ArgumentError, "BPW (#{bpw.to_s}) must be a positive integer" unless bpw > 0

  check_spi_enabled(spi)
  spi_fd = get_spi_status(spi, :fd_spi)

  spi_fd.ioctl(SPI_IOC_WR_BITS_PER_WORD, [bpw].pack('C'))

  # deal with old versions of ruby that can't handle large number IOCTL
  # https://bugs.ruby-lang.org/issues/6127
  begin
    spi_fd.ioctl(SPI_IOC_RD_BITS_PER_WORD, [bpw].pack('C'))
  rescue
    puts 'Warning, old Ruby detected, cannot set SPI read bits per word'
  end

  set_spi_status(spi, :bpw, bpw)
end
set_mode(spi, mode) click to toggle source

Set the communication speed of the specified SPI device

@param spi should be a symbol representing the SPI device @param mode should be a valid SPI mode, e.g. :SPI_MODE_0 through 3

# File lib/beaglebone/spi.rb, line 217
def set_mode(spi, mode)
  check_spi_enabled(spi)

  #if mode is a symbol, translate it to the appropriate value
  mode = SPI_MODES[mode] if mode.class == Symbol && SPI_MODES[mode]

  raise ArgumentError, "Mode (#{mode.to_s}) is unknown" unless SPI_MODES.values.include?(mode)
  spi_fd = get_spi_status(spi, :fd_spi)

  # deal with old versions of ruby that can't handle large number IOCTL
  # https://bugs.ruby-lang.org/issues/6127
  begin
    spi_fd.ioctl(SPI_IOC_WR_MODE, [mode].pack('C'))
    spi_fd.ioctl(SPI_IOC_RD_MODE, [mode].pack('C'))
  rescue
    puts 'Warning, old Ruby detected, cannot set SPI mode'
  end

end
set_speed(spi, speed) click to toggle source

Set the communication speed of the specified SPI device

@param spi should be a symbol representing the SPI device @param speed communication speed

# File lib/beaglebone/spi.rb, line 193
def set_speed(spi, speed)
  speed = speed.to_i
  raise ArgumentError, "Speed (#{speed.to_s}) must be a positive integer" unless speed > 0

  check_spi_enabled(spi)
  spi_fd = get_spi_status(spi, :fd_spi)

  spi_fd.ioctl(SPI_IOC_WR_MAX_SPEED_HZ, [speed].pack('L'))

  # deal with old versions of ruby that can't handle large number IOCTL
  # https://bugs.ruby-lang.org/issues/6127
  begin
    spi_fd.ioctl(SPI_IOC_RD_MAX_SPEED_HZ, [speed].pack('L'))
  rescue
    puts 'Warning, old Ruby detected, cannot set SPI max read speed'
  end

  set_spi_status(spi, :speed, speed)
end
setup(spi, mode=nil, speed=1000000, bpw=8) click to toggle source

Initialize an SPI device

@param spi should be a symbol representing the SPI device @param mode optional, default 0, specifies the SPI mode :SPI_MODE_0 through 3 @param speed optional, specifies the SPI communication speed @param bpw optional, specifies the bits per word

@example

SPI.setup(:SPI0, SPI_MODE_0)
# File lib/beaglebone/spi.rb, line 81
def setup(spi, mode=nil, speed=1000000, bpw=8)
  check_spi_valid(spi)

  #make sure spi not already enabled
  return if get_spi_status(spi)

  mode = mode || SPI_MODE_0

  spiinfo = SPIS[spi]

  #ensure dtb is loaded
  Beaglebone::device_tree_load("#{spiinfo[:devicetree]}") if spiinfo[:devicetree]

  #open the spi device.
  spi_fd = File.open("#{spiinfo[:dev]}#{SPIS[:counter]}.0", 'r+')

  set_spi_status(spi, :fd_spi, spi_fd)
  set_spi_status(spi, :mutex, Mutex.new)

  set_mode(spi, mode)
  set_bpw(spi, bpw)
  set_speed(spi, speed)

  SPIS[:counter] += 1

  spiinfo[:pins].each do |pin|
    Beaglebone::set_pin_status(pin, :spi, spiinfo[:id])
    Beaglebone::set_pin_status(pin, :type, :spi)
    Beaglebone::set_pin_status(pin, :fd_spi, spi_fd)
  end

end
xfer(spi, tx_data, readbytes=0, speed=nil, delay=nil, bpw=nil) click to toggle source

Transfer data to and from the SPI device

@return String data read from SPI device

@param spi should be a symbol representing the SPI device @param tx_data data to transmit @param readbytes bytes to read, otherwise it sizeof tx_data is used @param speed optional, speed to xfer at @param delay optional delay @param bpw optional bits per word

@example

# communicate with MCP3008
# byte 1: start bit
# byte 2: single(1)/diff(0),3 bites for channel, null pad
# byte 3: don't care
SPI.setup(:SPI0, SPI_MODE_0)
raw = SPI.xfer(:SPI0, [ 0b00000001, 0b10000000, 0].pack("C*"))
data = raw.unpack("C*")
val = ((data[1] & 0b00000011) << 8 ) | data[2]
# File lib/beaglebone/spi.rb, line 134
def xfer(spi, tx_data, readbytes=0, speed=nil, delay=nil, bpw=nil)
  check_spi_enabled(spi)

  speed = speed || get_spi_status(spi, :speed)
  delay = delay || 0
  bpw   = bpw || get_spi_status(spi, :bpw)

  if tx_data.size > readbytes
    readbytes = tx_data.size
  end

  rx_data = ' ' * readbytes

  lock_spi(spi) do
    spi_fd = get_spi_status(spi, :fd_spi)

    ### SPI IOC transfer structure
    # __u64       tx_buf;
    # __u64       rx_buf;
    #
    # __u32       len;
    # __u32       speed_hz;
    #
    # __u16       delay_usecs;
    # __u8        bits_per_word;
    # __u8        cs_change;
    # __u32       pad;
    ###

    msg = [ tx_data, 0,
            rx_data, 0,
            readbytes,
            speed,
            delay,
            bpw,
            0,
            0].pack('pLpLLLSCCL')

    #ioctl call to begin data transfer
    spi_fd.ioctl(SPI_IOC_MESSAGE_1, msg)
    #speedup with defined int
    #spi_fd.ioctl(spi_ioc_message(1), msg)

  end
  rx_data
end

Private Class Methods

check_spi_enabled(spi) click to toggle source

ensure spi is enabled

# File lib/beaglebone/spi.rb, line 322
def check_spi_enabled(spi)
  raise ArgumentError, "spi not enabled #{spi.to_s}" unless get_spi_status(spi)
end
check_spi_valid(spi) click to toggle source

ensure spi is valid

# File lib/beaglebone/spi.rb, line 290
def check_spi_valid(spi)
  raise ArgumentError, "Invalid spi Specified #{spi.to_s}" unless SPIS[spi] && SPIS[spi][:sclk]
  spiinfo = SPIS[spi.to_sym]

  unless spiinfo[:sclk] && [nil,:spi].include?(Beaglebone::get_pin_status(spiinfo[:sclk], :type))
    raise StandardError, "SCLK Pin for #{spi.to_s} in use"
  end

  unless spiinfo[:d0] && [nil,:spi].include?(Beaglebone::get_pin_status(spiinfo[:d0], :type))
    raise StandardError, "D0 Pin for #{spi.to_s} in use"
  end

  unless spiinfo[:d1] && [nil,:spi].include?(Beaglebone::get_pin_status(spiinfo[:d1], :type))
    raise StandardError, "D1 Pin for #{spi.to_s} in use"
  end

  unless spiinfo[:cs0] && [nil,:spi].include?(Beaglebone::get_pin_status(spiinfo[:cs0], :type))
    raise StandardError, "CS0 Pin for #{spi.to_s} in use"
  end
end
delete_spi_status(spi, key = nil) click to toggle source
# File lib/beaglebone/spi.rb, line 374
def delete_spi_status(spi, key = nil)
  spimutex.synchronize do
    if key.nil?
      spistatus.delete(spi)
    else
      spistatus[spi].delete(key) if spistatus[spi]
    end
  end
end
disable_spi_pin(pin) click to toggle source

disable spi pin

# File lib/beaglebone/spi.rb, line 327
def disable_spi_pin(pin)
  Beaglebone::check_valid_pin(pin, :spi)

  Beaglebone::delete_pin_status(pin)
end
get_spi_status(spi, key = nil) click to toggle source
# File lib/beaglebone/spi.rb, line 357
def get_spi_status(spi, key = nil)
  spimutex.synchronize do
    if key
      spistatus[spi] ? spistatus[spi][key] : nil
    else
      spistatus[spi]
    end
  end
end
ioc(dir,type,nr,size) click to toggle source

ports of ioctl definitions

# File lib/beaglebone/spi.rb, line 334
def ioc(dir,type,nr,size)
  (((dir) << IOC_DIRSHIFT) |
      ((type) << IOC_TYPESHIFT) |
      ((nr) << IOC_NRSHIFT) |
      ((size) << IOC_SIZESHIFT))
end
ior(type,nr,size) click to toggle source
# File lib/beaglebone/spi.rb, line 341
def ior(type,nr,size)
  ioc(IOC_READ,(type),(nr),size)
end
iow(type,nr,size) click to toggle source
# File lib/beaglebone/spi.rb, line 345
def iow(type,nr,size)
  ioc(IOC_WRITE,(type),(nr),size)
end
lock_spi(spi) { || ... } click to toggle source

lock spi

# File lib/beaglebone/spi.rb, line 312
def lock_spi(spi)
  check_spi_enabled(spi)
  mutex = get_spi_status(spi, :mutex)

  mutex.synchronize do
    yield
  end
end
set_spi_status(spi, key, value) click to toggle source
# File lib/beaglebone/spi.rb, line 367
def set_spi_status(spi, key, value)
  spimutex.synchronize do
    spistatus[spi]    ||= {}
    spistatus[spi][key] = value
  end
end
spi_ioc_message(n) click to toggle source
# File lib/beaglebone/spi.rb, line 353
def spi_ioc_message(n)
  iow(SPI_IOC_MAGIC, 0, spi_msgsize(n))
end
spi_msgsize(n) click to toggle source
# File lib/beaglebone/spi.rb, line 349
def spi_msgsize(n)
  n*SPI_IOC_TRANSFER_STRUCT_SIZE < 1<<IOC_SIZEBITS ? n*SPI_IOC_TRANSFER_STRUCT_SIZE : 0
end