Ruby Class for Bus Pirate binmode

From wiki.countercaster.com

Jump to: navigation, search

This is my attempt at a simple Ruby library for accessing the Bus Pirate's binmodes.

At the moment it only (partially) supports binmode SPI mode. Error traps are conspicuously absent!

For an example of how to use it, see Scripting the Bus Pirate to talk to a Nokia 3310 LCD.

#
# A start at a Ruby library for accessing the Bus Pirate's binmode SPI feature.
# by blue.zener
# October 2009
#
# Use and adapt as you wish, and at your own risk.
#
 
module BusPirate
  require 'rubygems'
  require 'serialport'
 
  class BinModeSpi
    SpiSpeedMask = {
        30000 => 0b000,
       125000 => 0b001,
       250000 => 0b010,
      1000000 => 0b011,
      2000000 => 0b100,
      2600000 => 0b101,
      4000000 => 0b110,
      8000000 => 0b111
    }
 
 
    def open(port, baud, options = {})
      @serial_port = SerialPort.new(port, baud, 8, 1, SerialPort::NONE)
      @serial_port.flow_control = SerialPort::NONE
      @serial_port.sync = true
      init_binary_mode
      init_spi_mode
      configure_spi(options)
      configure_spi_speed(options)
    end
 
    def set_cs_high
      @serial_port.putc(0b00000011)
      puts "spi_cs_high: 0x%02x" % @serial_port.readchar
    end
 
    def set_cs_low
      @serial_port.putc(0b00000010)
      puts "spi_cs_low: 0x%02x" % @serial_port.readchar
    end
 
    # High level write
    # Write a single byte or array of bytes in "data".
    # to device attached to SPI port.
    #
    # Abstracts away BP bulk transfer limit of 16 bytes.
    # So "data" can be any length. 
    def write(data)
      set_cs_low
      data = arrayify(data)
      # slice into bulk transfer size portions
      transfer_count = 0
      data.to_enum.each_slice(16) do |chunk|
        write_bulk_transfer(chunk, :read => false)
        transfer_count += 1
      end
      sleep(0.05)
      @serial_port.readpartial(transfer_count + data.length)
      set_cs_high
    end
 
 
    def set_peripherals(options = {})
      params = {:power => 0, :pull_ups => 0, :aux => 0, :chip_select => 0}.merge(options)
 
      mask = 0
      mask |= params[:power] << 3
      mask |= params[:pull_ups] << 2
      mask |= params[:aux] << 1
      mask |= params[:chip_select]
 
      @serial_port.putc(0b01000000 | mask)
      puts "set_peripherals: 0x%02x" % @serial_port.readchar
    end
 
    def close
      return_to_binary_mode
      return_to_user_terminal_mode
      @serial_port.close
    end
 
  private
    def init_binary_mode
      20.times do
        @serial_port.putc(0x00)
      end
      response = ""
      5.times do
        response << @serial_port.readchar
      end
      puts "init_binary_mode: #{response}"
    end
 
    def init_spi_mode
      @serial_port.putc(0b00000001)
      response = ""
      4.times do
        response << @serial_port.readchar
      end
      puts "init_spi_mode: #{response}"
    end
 
    def configure_spi(options =  {})
      params =
        {:output_type=>0, :idle=>0, :clock_edge=>0, :sample=>0}.merge(options)
 
      mask = 0
      mask |= params[:output_type] << 3
      mask |= params[:idle] << 2
      mask |= params[:clock_edge] << 1
      mask |= params[:sample]
 
      @serial_port.putc(0b10000000 | mask)
      puts "configure_spi: 0x%02x" % @serial_port.readchar
    end
 
    def configure_spi_speed(options={})
      params = {:speed => 30000}.merge(options)
      puts "configure_spi_speed: Speed: %iHz" % params[:speed]
      puts "configure_spi_speed: Speed mask: %08b" % SpiSpeedMask[params[:speed]]
      @serial_port.putc(0b01100000 | SpiSpeedMask[params[:speed]])
      puts "configure_spi_speed: 0x%02x" % @serial_port.readchar
    end
 
    def start_bulk_transfer(length, options={})
      params = {:read => true}.merge(options)
      @serial_port.putc(0b00010000 | (length-1))
      if (params[:read])
        puts "start_bulk_transfer: 0x%02x" % @serial_port.readchar
      end
    end
 
    def write_bulk_transfer(data, options={})
      params = {:read => true}.merge(options)
      if data.length > 16
        puts "write_bulk_transfer: data exceeds maximum length (16)"
        return
      end
      start_bulk_transfer(data.length, params)
      data.each do |byte|
        @serial_port.putc(byte)
        @serial_port.readchar if params[:read]
        #puts "write_bulk_transfer: 0b%08b" % byte
      end
    end
 
    def return_to_binary_mode
      @serial_port.putc(0b00000000)
      puts "return_to_binary_mode"
    end
 
    def return_to_user_terminal_mode
      @serial_port.putc(0b00001111)
      puts "return_to_user_terminal_mode"
    end
 
    def arrayify(data)
      return data if data.kind_of?(Array)
      [] << data
    end
  end
end
Personal tools