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
Public Class Methods
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 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
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 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 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
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
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
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
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
# 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
# File lib/beaglebone/spi.rb, line 327 def disable_spi_pin(pin) Beaglebone::check_valid_pin(pin, :spi) Beaglebone::delete_pin_status(pin) end
# 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
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
# File lib/beaglebone/spi.rb, line 341 def ior(type,nr,size) ioc(IOC_READ,(type),(nr),size) end
# File lib/beaglebone/spi.rb, line 345 def iow(type,nr,size) ioc(IOC_WRITE,(type),(nr),size) end
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
# 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
# File lib/beaglebone/spi.rb, line 353 def spi_ioc_message(n) iow(SPI_IOC_MAGIC, 0, spi_msgsize(n)) end
# 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