Class: ChipGPIO::HardwareSPI

Inherits:
Object
  • Object
show all
Defined in:
lib/chip-gpio/HardwareSpi.rb

Constant Summary collapse

SPI_MAX_CHUNK_BYTES =
64
SPI_IOC_RD_MODE =
0x80016b01
SPI_IOC_WR_MODE =
0x40016b01
SPI_IOC_RD_LSB_FIRST =
0x80016b02
SPI_IOC_WR_LSB_FIRST =
0x40016b02
SPI_IOC_RD_BITS_PER_WORD =
0x80016b03
SPI_IOC_WR_BITS_PER_WORD =
0x40016b03
SPI_IOC_RD_MAX_SPEED_HZ =
0x80046b04
SPI_IOC_WR_MAX_SPEED_HZ =
0x40046b04
SPI_IOC_RD_MODE32 =
0x80046b05
SPI_IOC_WR_MODE32 =
0x40046b05
SPI_IOC_MESSAGE_1 =

can only be used to send one spi_ioc_transfer struct at a time

0x40206b00
SPI_CPHA =
0x01
SPI_CPOL =
0x02

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(polarity: 0, phase: 0, word_size: 8, lsb_first: false) ⇒ HardwareSPI

Returns a new instance of HardwareSPI.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/chip-gpio/HardwareSpi.rb', line 28

def initialize(polarity: 0, phase: 0, word_size: 8, lsb_first: false)
  @polarity = polarity
  @phase = phase
  @word_size = word_size
  @lsb_first = lsb_first
  @speed_hz = 1000000 #TODO Configurable parameter

  @device = open("/dev/spidev32766.0", File::RDWR)      

  @mode = 0
  @mode = @mode | 0x01 if (phase == 1)
  @mode = @mode | 0x02 if (polarity == 1)

  write_u8(SPI_IOC_WR_MODE, @mode)
  write_u8(SPI_IOC_WR_LSB_FIRST, (@lsb_first ? 1 : 0))
  write_u32(SPI_IOC_WR_MAX_SPEED_HZ, @speed_hz) 

end

Instance Attribute Details

#lsb_firstObject (readonly)

Returns the value of attribute lsb_first.



6
7
8
# File 'lib/chip-gpio/HardwareSpi.rb', line 6

def lsb_first
  @lsb_first
end

#modeObject (readonly)

Returns the value of attribute mode.



7
8
9
# File 'lib/chip-gpio/HardwareSpi.rb', line 7

def mode
  @mode
end

#phaseObject (readonly)

Returns the value of attribute phase.



4
5
6
# File 'lib/chip-gpio/HardwareSpi.rb', line 4

def phase
  @phase
end

#polarityObject (readonly)

Returns the value of attribute polarity.



3
4
5
# File 'lib/chip-gpio/HardwareSpi.rb', line 3

def polarity
  @polarity
end

#speed_hzObject (readonly)

Returns the value of attribute speed_hz.



8
9
10
# File 'lib/chip-gpio/HardwareSpi.rb', line 8

def speed_hz
  @speed_hz
end

#word_sizeObject (readonly)

Returns the value of attribute word_size.



5
6
7
# File 'lib/chip-gpio/HardwareSpi.rb', line 5

def word_size
  @word_size
end

Instance Method Details

#break_words_into_nibbles(words: []) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/chip-gpio/HardwareSpi.rb', line 73

def break_words_into_nibbles(words: [])
  nibbles_per_word = @word_size / 4

  #for each word, output each nibble individually 
  #(shifting by 4 each time since a nibble is 4 bits)
  words.each do |w|
    
    #this is basically the reverse of nibbles_per_word.times
    #we reverse it because we want to start at the most-significant nibble
    #which will be shifted the most times
    #
    #since we want to end up at 0, subtract one from nibbles_per_word
    #so we don't get an extra
    (nibbles_per_word - 1).downto(0).each do |i|
      yield w, ((w >> (i * 4)) & 0xf)
    end
  end
end

#closeObject



47
48
49
# File 'lib/chip-gpio/HardwareSpi.rb', line 47

def close()
  @device.close()
end

#pack_words_into_bytes(words: []) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/chip-gpio/HardwareSpi.rb', line 92

def pack_words_into_bytes(words: [])
  nibbles_per_word = @word_size / 4

  bytes = []
  current_byte = 0
  new_byte = true 

  break_words_into_nibbles(words: words) do |current_word, current_nibble|
    if new_byte
      new_byte = false 
      current_byte = current_byte | (current_nibble << 4)
    else
      current_byte = current_byte | current_nibble
      
      bytes << current_byte
      current_byte = 0
      new_byte = true
    end
  end

  return bytes
end

#read_u32(msg) ⇒ Object



62
63
64
65
66
# File 'lib/chip-gpio/HardwareSpi.rb', line 62

def read_u32(msg)
  value_packed = [0].pack("L")
  @device.ioctl(msg, value_packed)
  return value_packed.unpack("L")[0]
end

#read_u8(msg) ⇒ Object



51
52
53
54
55
# File 'lib/chip-gpio/HardwareSpi.rb', line 51

def read_u8(msg)
  value_packed = [0].pack("C")
  @device.ioctl(msg, value_packed)
  return value_packed.unpack("C")[0]
end

#testObject



159
160
161
162
163
164
# File 'lib/chip-gpio/HardwareSpi.rb', line 159

def test()
  words = []
  24.times { |i| words << 0 }

  transfer_data(words: words)
end

#to_sObject



166
167
168
# File 'lib/chip-gpio/HardwareSpi.rb', line 166

def to_s
  return "\#<ChipGPIO:HardwareSPI mode=#{@mode} device=#{@device.path} word_size=#{@word_size} lsb_first=#{@lsb_first}>"
end

#transfer_bytes(bytes: []) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/chip-gpio/HardwareSpi.rb', line 115

def transfer_bytes(bytes: [])
  # http://stackoverflow.com/questions/11949538/pointers-to-buffer-in-ioctl-call
  raise "Too many bytes sent to transfer_bytes" if bytes.size > SPI_MAX_CHUNK_BYTES
  
  #begin spi_ioc_transfer struct (cat /usr/include/linux/spi/spidev.h)
  tx_buff = bytes.pack("C*")        
  rx_buff = (Array.new(bytes.size) { 0 }).pack("C*")       
  
  tx_buff_pointer = [tx_buff].pack("P").unpack("L!")[0]   #u64 (zero-extended pointer)
  rx_buff_pointer = [rx_buff].pack("P").unpack("L!")[0]   #u64 (zero-extended pointer)

  
  buff_len = bytes.size                                   #u32
  speed_hz = @speed_hz                                    #u32

  delay_usecs = 0                                         #u16
  bits_per_word = 8                                       #u8
  cs_change = 0                                           #u8
  tx_nbits = 0                                            #u8
  rx_nbits = 0                                            #u8
  pad = 0                                                 #u16

  struct_array = [tx_buff_pointer, rx_buff_pointer, buff_len, speed_hz, delay_usecs, bits_per_word, cs_change, tx_nbits, rx_nbits, pad]
  struct_packed = struct_array.pack("QQLLSCCCCS")
  #end spi_ioc_transfer struct

  @device.ioctl(SPI_IOC_MESSAGE_1, struct_packed)

  return rx_buff.unpack("C*")
end

#transfer_data(words: []) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/chip-gpio/HardwareSpi.rb', line 146

def transfer_data(words: [])
  
  bytes_to_transfer = pack_words_into_bytes(words: words)

  result = []

  bytes_to_transfer.each_slice(SPI_MAX_CHUNK_BYTES) do |chunk_bytes|
    result = result + transfer_bytes(bytes: chunk_bytes)
  end

  return result
end

#write_u32(msg, value) ⇒ Object



68
69
70
71
# File 'lib/chip-gpio/HardwareSpi.rb', line 68

def write_u32(msg, value)
  value_packed = [value].pack("L")
  @device.ioctl(msg, value_packed)
end

#write_u8(msg, value) ⇒ Object



57
58
59
60
# File 'lib/chip-gpio/HardwareSpi.rb', line 57

def write_u8(msg, value)
  value_packed = [value].pack("C")
  @device.ioctl(msg, value_packed)
end