WiringPi / WiringPi-Ruby-Legacy

Ruby gem of the Arduino wiring-like C library WiringPi
118 stars 20 forks source link

this is a question related to WiringPi::Spi.new #5

Closed thus1 closed 11 years ago

thus1 commented 11 years ago

I have implemented a small pure-ruby-library(150 lines) for spi-access.

usage:

require "wiringpi/spi" spi = WiringPi::Spi.new("/dev/spidev0.0",speed: 1000000) result = spi.dataRW([1,2,3,0,4],delay: 100,bytedelay: 10) spi.close

The lib requires the package ruby-1.9x and ruby-ffi, both is available in rapbian wheezy

Do you think its worth to integrate it into this project?

con:

pro:

By the way, the same arguments also count for any other functionality of wiringPi ;-))

MarkKropf commented 11 years ago

Do you have this up anywhere? I'd love to use it in lieue of a decision being made here.

thus1 commented 11 years ago

Sorry for the late answer, I didnt recognize your request in my mail-folder. How can I attach the source-code here? Ok I try it inline. I think it would be nice if we could add this single file to the ruby-gem 'wiringPi' for simpification of the install procedure. But for now you can copy the file to any location where your ruby looks to. (ruby -e 'p $:') Decide yourself if you need the subdirectory wiringPi.

#!/usr/bin/ruby
#
# = wiringPi/spi.rb
#
# Object-Oriented WiringPi::Spi Class
#
# Author::        Thomas Husterer 
#
# This module currently  works only with ruby >= 1.9, because some minor 
# 1.9 features are used. e.g. Hash#merge. 
#
# For documentation, use rdoc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
require "pp"
begin
  require "ffi"
rescue LoadError
  raise "\nmissing ffi-package, install with:\nsudo apt-get install ruby-ffi"
end
# This module Ioctl is only a helper module
module Ioctl#:nodoc:
  IOC_NRBITS   =  8
  IOC_TYPEBITS =  8
  IOC_SIZEBITS = 14
  IOC_DIRBITS  =  2
  #dir:
  IOC_NONE     =  0
  IOC_WRITE    =  1
  IOC_READ     =  2
  class << self
    def ioc(dir,type,nr,size)
      dir  < (1 << IOC_DIRBITS)  or raise
      type < (1 << IOC_TYPEBITS) or raise
      nr   < (1 << IOC_NRBITS)   or raise
      size < (1 << IOC_SIZEBITS) or raise
      n = 0
      n = dir  | (n << IOC_DIRBITS)
      n = size | (n << IOC_SIZEBITS)
      n = type | (n << IOC_TYPEBITS)
      n = nr   | (n << IOC_NRBITS)
      n-=2**32 if n >= 2**31
      n
    end
    #/* used to create numbers */
    def ioc_r(type,nr,realsize) #define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
      ioc(IOC_READ,type,nr,realsize)
    end
    def ioc_w(type,nr,realsize)#define _IOW(type,nr,size)   _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
      ioc(IOC_WRITE,type,nr,realsize)
    end
    def ioc_rw(type,nr,realsize)#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
      ioc(IOC_READ|IOC_WRITE,type,nr,realsize)
    end
  end
end
module WiringPi
  # This class WiringPi::Spi is used to generate Interface-Objects 
  # for each spi-channel
  # 
  # Usage:
  # 
  #  require "wiringPi/spi"
  # 
  #  spi    = WiringPi::Spi.new("/dev/spidev0.0",speed: 1000000)
  #  result = spi.dataRW([1,2,3,0,4],delay: 100,bytedelay: 10)
  #  p        result
  #  spi.close
  #
  #
  class Spi
    OPTS ={
      speed: 1000000, #from 10KHz .. 100MHz ?
      bpw:         8, #here only 8 is supported
      delay:       0, #us delay after whole message
      bytedelay:   nil   #us delay after each sent byte
    } #:nodoc:
    SPI_IOC_MAGIC = 107 #?k #"k".chr #:nodoc:
    #struct spi_ioc_transfer see /usr/include/linux/spi/spidev.h
    SPI_IOC_TRANSFER_SIZE = 32 # Spi_ioc_transfer.size #:nodoc:
    SPI_IOC_WR_MSG={}#:nodoc:
    def spi_ioc_message(n)#:nodoc:
      SPI_IOC_WR_MSG[n] ||= Ioctl::ioc_w(SPI_IOC_MAGIC, 0, n*SPI_IOC_TRANSFER_SIZE)
    end
    #/* Read / Write of SPI mode (SPI_MODE_0..SPI_MODE_3) */
    SPI_IOC_RD_MODE          = Ioctl::ioc_r(SPI_IOC_MAGIC, 1, 1)#:nodoc:
    SPI_IOC_WR_MODE          = Ioctl::ioc_w(SPI_IOC_MAGIC, 1, 1)#:nodoc:
    #/* Read / Write SPI bit justification */
    SPI_IOC_RD_LSB_FIRST     = Ioctl::ioc_r(SPI_IOC_MAGIC, 2, 1)#:nodoc:
    SPI_IOC_WR_LSB_FIRST     = Ioctl::ioc_w(SPI_IOC_MAGIC, 2, 1)#:nodoc:
    #/* Read / Write SPI device word length (1..N) */
    SPI_IOC_RD_BITS_PER_WORD = Ioctl::ioc_r(SPI_IOC_MAGIC, 3, 1)#:nodoc:
    SPI_IOC_WR_BITS_PER_WORD = Ioctl::ioc_w(SPI_IOC_MAGIC, 3, 1)#:nodoc:
    #/* Read / Write SPI device default max speed hz */
    SPI_IOC_RD_MAX_SPEED_HZ  = Ioctl::ioc_r(SPI_IOC_MAGIC, 4, 4)#:nodoc:
    SPI_IOC_WR_MAX_SPEED_HZ  = Ioctl::ioc_w(SPI_IOC_MAGIC, 4, 4)#:nodoc:

    private :spi_ioc_message
    # This method sends out some data-bytes and receives the same number 
    # of bytes from the slave-device
    #
    # [data] may be given in three formats
    #        * as String e.g. "hello"
    #        * as Array of positive Integers (0-255) e.g. [1,2,3]
    #        * as FFI::MemoryPointer 
    #
    # [opts] arguments are the same as in the new-method
    #
    # The received Bytes are returned as an Array of positive Interger values
    def dataRW(data,opts={})
      if data.is_a? String
        wbuf = FFI::MemoryPointer.from_string(data)
      elsif data.is_a? FFI::Pointer
        wbuf = data
      elsif data.is_a? Array
        wbuf = FFI::MemoryPointer.new(:uint8,data.length)
        wbuf.write_array_of_uint8(data)
      else
        raise
      end
      rbuf=FFI::MemoryPointer.new(:uint8,wbuf.size)
      FFI::Pointer.size==4 or raise ""
      opts      = @opts.merge(opts)
      speed     = opts[:speed]
      bpw       = opts[:bpw]
      delay     = opts[:delay]
      bytedelay = opts[:bytedelay]
      if bytedelay and bytedelay != 0
        iter = wbuf.size
        len  = 1
      else
        iter = 1
        len  = wbuf.size
      end
      spi       = "" #setup ioctl message buffer
      iter.times{|i| last = i==(iter-1)
        spi+=[
              wbuf.address+i, #  layout :tx_buf ,:pointer,
              0,              #  :tx_buf64      ,:pointer,
              rbuf.address+i, #  :rx_buf        ,:pointer,
              0,              #  :rx_buf64      ,:pointer,
              len,            #  :len           ,:uint32 ,
              speed, #  :speed_hz      ,:uint32 ,
              last ? delay : bytedelay, #  :delay_usecs   ,:uint16 ,
              bpw,   #  :bits_per_word ,:uint8  ,
              last ? 1 : 0,            #  :cs_change     ,:uint8  ,
              0,            #  :pad           ,:uint32
             ].pack("LLLLLLSCCL")
      }
      @fh.ioctl(spi_ioc_message(iter),spi)
      rbuf.read_array_of_uint8(rbuf.size)
    end

    # generate a new Spi-Channel object
    # [chan] may be a number or a device-name-path
    #        any additional arguments are options-arguments
    #
    # [opts] may be:
    #        * speed:  a value from 10KHz .. 100MHz ?
    #        * bpw:    here only 8 is supported
    #        * delay:  a delay after the whole message in us
    #        * bytedelay:  a delay after each sent byte in us
    #
    #
    # Example:
    #  WiringPi::Spi.new("/dev/spidev0.0", speed: 1000000, bytedelay: 50)
    def initialize(chan,opts={})
      dev = chan.is_a?(Integer) ? "/dev/spidev0.#{chan}" : chan
      begin
        @fh = File.open(dev,"w+b")
      rescue Errno::EACCES
        raise "\nPermission denied, maybe its missing:\nsudo modprobe spi_bcm2708\nsudo chown #{Process.uid} #{dev}"
      end
      @opts=OPTS.merge(opts)
      s_Mode = [0].pack("C") #static uint8_t     spiMode   = 0 ;
      @fh.ioctl(SPI_IOC_WR_MODE,s_Mode)
      @fh.ioctl(SPI_IOC_RD_MODE,s_Mode)

      s_BPW  = [OPTS[:bpw]].pack("C") #static uint8_t     spiBPW    = 8 ;
      @fh.ioctl(SPI_IOC_WR_BITS_PER_WORD,s_BPW)
      @fh.ioctl(SPI_IOC_RD_BITS_PER_WORD,s_BPW)
      s_Speed= [OPTS[:speed]].pack("V") #u32
      @fh.ioctl(SPI_IOC_WR_MAX_SPEED_HZ,s_Speed)
      @fh.ioctl(SPI_IOC_RD_MAX_SPEED_HZ,s_Speed)
    end
    # closes this channel after usage
    def close
      @fh.close
    end
  end
end
if $0 == __FILE__
  spi=WiringPi::Spi.new("/dev/spidev0.0",speed: 1000000)
  while 1
    p spi.dataRW([1,2,3,0,4],delay: 100,bytedelay: 10)
    sleep 0.001
  end
  spi.close
end
thus1 commented 11 years ago

sorry, i hit the wrong button 'Close & Comment' how can I undo this step?

MarkKropf commented 11 years ago

Looks like the script got garbled around the Ioctl module

thus1 commented 11 years ago

What is the output in the shell when the script craches