class BitWizard::Board

Attributes

address[R]
bus[R]
features[R]
logger[RW]
type[R]
version[R]

Public Class Methods

detect(options) click to toggle source

Detects the type of board on the given address and creates the correct handler class for it.

@param [Number] address The address to check.
@param [optional, Hash] options A Hash of options.
@option options [Symbol] :bus The type of bus the board is connected on. (:spi or :i2c)
@option options [Logger] :logger A logger you want to attach to the board.
# File lib/bitwizard.rb, line 13
def Board.detect(options)
        options = {
                address: -1,
                bus: :spi,
                logger: NullLogger.new
        }.merge(options).merge({
                type: :auto_detect,
                skip_check: false
        })

        options[:logger] = NullLogger.new unless options[:logger]

        temp = BitWizard::Board.new options
        correct = temp.instance_variable_get(:@constructor).call(options.merge({skip_check: true})) if temp.valid?

        correct.instance_variable_set(:@type, temp.type)
        correct.instance_variable_set(:@version, temp.version)
        correct.instance_variable_set(:@features, temp.features)

        correct
end
new(options) click to toggle source

Creates a generic board handle for reading and writing directly.

@param [Hash] options A hash of options.
@option options [Fixnum] :address The address of the board. (0x00..0xff)
@option options [Symbol] :type The board type, defaults to auto detecting. (identifier)
@option options [Symbol] :bus The bus it's connected to. (:spi or :i2c)
@option options [Boolean] :skip_check Skip the self check that runs on creation.
@option options [Number] :clock The clockrate you want to run the communication with.
@option options [Logger] :logger Add a logger here to log data that's sent and received.
# File lib/bitwizard.rb, line 47
def initialize(options)
        options = {
                address: -1,
                type: :auto_detect,
                bus: :spi,
                skip_check: false,
                clock: 45000,
                logger: NullLogger.new
        }.merge(options)

        raise ArgumentError.new "Bus must be :spi or :i2c." unless options[:bus] == :spi or options[:bus] == :i2c

        @logger = options[:logger]
        @address = options[:address]
        @type = options[:type]
        @bus = options[:bus]

        self_check! unless options[:skip_check]
end

Public Instance Methods

address=(new_address) click to toggle source

Changes the boards address

The new address needs to follow these criteria;
  * Must be a 8-bit number between 0x00 and 0xff
  * Must not have it's least significant bit set

@param [Number] new_address The new address of the board
# File lib/bitwizard.rb, line 104
def address=(new_address)
        raise ArgumentError.new "#{new_address} is not a valid address" unless new_address.is_a? Fixnum and (0..255).include? new_address and new_address|1 != new_address

        # Run a quick check so that there isn't a board on the other address.
        old_address = @address
        @address = new_address
        identifier = read(0x01, 20).pack("C*").split("\0")[0]
        @address = old_address

        Known_Boards.each do |name, data|
                if name =~ identifier then
                        raise ArgumentError.new "Another board (#{identifier}) already exists on #{new_address}!"
                end
        end

        # Unlock the change register
        write 0xf1, 0x55 
        write 0xf2, 0xaa

        # Perform the change
        write 0xf0, new_address

        @address = new_address
end
read(reg, count) click to toggle source

Reads a value from the board

@param [Number] reg The registry address to read from
@param [Number] count The number of bytes to read
# File lib/bitwizard.rb, line 90
def read(reg, count)
        raise ArgumentError.new "#{reg} is not a valid register, must be a number between 0x00..0xff" unless reg.is_a? Fixnum and (0..255).include? reg

        return spi_read(reg, count) if @bus == :spi
        return i2c_read(reg, count) if @bus == :i2c
end
valid?() click to toggle source

Returns if the board has a valid communication address

# File lib/bitwizard.rb, line 68
def valid?
        return false if @address == -1 or @type == :auto_detect
        true
end
write(reg, value) click to toggle source

Writes a value to the board, either a single byte or several in the form of a string

@param [Number] reg The registry address to write to
@param [Number|String] value The data to write to the board
# File lib/bitwizard.rb, line 77
def write(reg, value)
        raise ArgumentError.new "#{reg} is not a valid register, must be a number between 0x00..0xff" unless reg.is_a? Fixnum and (0..255).include? reg
        raise ArgumentError.new "#{value} is not a valid value, must be a single byte or a string" unless (value.is_a? Fixnum and (0..255).include? value) or (value.is_a? String)

        value = value.unpack("C*") if value.is_a? String
        return spi_write(reg, value) if @bus == :spi
        return i2c_write(reg, value) if @bus == :i2c
end

Private Instance Methods

i2c_read(reg, count) click to toggle source
# File lib/bitwizard.rb, line 211
def i2c_read(reg, count)
        PiPiper::I2C.begin do |i2c|
                i2c.write({ to: @address | 1, data: [reg] })
        end
        data = PiPiper::I2C.begin do |i2c|
                PiPiper::Platform.driver.i2c_set_address(@address | 1)
                i2c.read count
        end
        @logger.debug("I2C [0x#{@address.to_s(16)}] --> 0x#{reg.to_s(16)}: #{data.pack("C*").inspect}")
        data
end
i2c_write(reg, value) click to toggle source
# File lib/bitwizard.rb, line 199
def i2c_write(reg, value)
        @logger.debug("I2C [0x#{@address.to_s(16)}] <-- 0x#{reg.to_s(16)}: #{value.is_a? Array and value.pack("C*").inspect or value.inspect}")
        PiPiper::I2C.begin do |i2c|
                data = [reg]

                data << value unless value.is_a? Array
                data += value if value.is_a? Array

                i2c.write({ to: @address, data: data })
        end
end
self_check!() click to toggle source

Performs a self check of the board

This includes contacting the board and checking that it's of the correct type.
# File lib/bitwizard.rb, line 138
def self_check!
        found_board = nil
        Known_Boards.each do |name, data|
                if name =~ @type then
                        @address = data[:default_address] unless (0..255).include? @address
                        found_board = {
                                name: name,
                                data: data
                        }
                        break
                end
        end
        raise ArgumentError.new "Don't know what board '#{@type}' is." if not found_board and @type != :auto_detect
        raise ArgumentError.new "Board type is 'auto_detect', but invalid address #{@address} given." if @type == :auto_detect and not (0..255).include? @address

        identifier = read(0x01, 20).pack("C*").split("\0")[0]
        raise ArgumentError.new "No response from board" if not identifier or identifier.empty?

        if @type == :auto_detect then
                Known_Boards.each do |name, data|
                        if name =~ identifier then
                                @type, @version = *identifier.split
                                @type = @type.to_sym
                                @constructor = data[:constructor]
                                @features = data[:features]
                                break
                        end
                end

                raise ArgumentError.new "No known board of type '#{identifier}'." if @type == :auto_detect and not identifier.empty?
        else
                Known_Boards.each do |name, data|
                        if name =~ identifier then
                                @version = identifier.split[1]
                                @features = data[:features]
                                raise ArgumentError.new "Board reports type #{real_name}, which does not match #{@type}" unless found_board[:data] == data
                                break
                        end
                end
        end

        true
end
spi_read(reg, count) click to toggle source
# File lib/bitwizard.rb, line 190
def spi_read(reg, count)
        data = PiPiper::Spi.begin do |spi|
                spi.write @address | 1, reg, *Array.new(count, 0)
        end[2..-1]
        
        @logger.debug("SPI [0x#{@address.to_s(16)}] --> 0x#{reg.to_s(16)}: #{data.pack("C*").inspect}")
        data
end
spi_write(reg, value) click to toggle source
# File lib/bitwizard.rb, line 182
def spi_write(reg, value)
        @logger.debug("SPI [0x#{@address.to_s(16)}] <-- 0x#{reg.to_s(16)}: #{value.is_a? Array and value.pack("C*").inspect or value.inspect}")
        PiPiper::Spi.begin do |spi|
                spi.write @address, reg, *value if value.is_a? Array
                spi.write @address, reg, value unless value.is_a? Array
        end
end