spi 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +19 -0
- data/README.md +67 -0
- data/Rakefile +2 -0
- data/lib/spi.rb +31 -0
- data/lib/spi/driver.rb +12 -0
- data/lib/spi/driver/spidev.rb +177 -0
- data/lib/spi/version.rb +3 -0
- data/spi.gemspec +24 -0
- metadata +83 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b7194309444e270662ed8d5420ac8ce84cb0e175
|
4
|
+
data.tar.gz: 1b636b4d38ad56b04650405e158e5eca8726daa7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 970ec16d5eed918ecaccbd732be353836f6ab394e96f2925044f82f4aa80c0c3000cfab629d38675060268604230b20dce72237f66a2c37d4bdecbf8c6abe0be
|
7
|
+
data.tar.gz: 0794eb9034851daaeb91ebce74866cd582e8c603eaffa3d035d88ecea222a686c8aaf92242734ed0e62aed7eaae98cd6effb7a529b79dae3d8c4a6b1a61d115a
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2017 Mike Axford
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# SPI
|
2
|
+
|
3
|
+
This gem aims to provide a Unified method of talking to SPI devices on a variety of hardware. This is mainly aimed at a range of Small Board Computers (SBCs) such as the Raspberry Pi and C.H.I.P. computers.
|
4
|
+
|
5
|
+
Currently only the Linux spidev kernel interface is implimented however the code is designed to make adding other drivers a simple process. It should then be possible to choose the correct driver for the hardware without having to re-write code.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'spi'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install spi
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
You may need to enable the SPI device on your hardware and/or load the kernel module.
|
26
|
+
On the Raspberry Pi SPI can be enabled through raspi-cofig
|
27
|
+
On the C.H.I.P. SPI can be loaded by importing the SPI DTB
|
28
|
+
|
29
|
+
After doing this you should find one or more devices in /dev
|
30
|
+
```bash
|
31
|
+
user@device:~ $ ls /dev/spi*
|
32
|
+
/dev/spidev32766.0
|
33
|
+
```
|
34
|
+
|
35
|
+
To use the default (SPIdev) driver you can specify
|
36
|
+
```ruby
|
37
|
+
require "bundler/setup"
|
38
|
+
require "spi"
|
39
|
+
s=SPI.new(device: '/dev/spidev32766.0')
|
40
|
+
s.speed=500000
|
41
|
+
s.xfer(txdata: [0x10,0x00])
|
42
|
+
```
|
43
|
+
Currently SPIdev is the only driver.
|
44
|
+
|
45
|
+
|
46
|
+
Simple Testing of just the SPIdev Driver with an RFM69
|
47
|
+
```ruby
|
48
|
+
require "bundler/setup"
|
49
|
+
require "spi"
|
50
|
+
require 'spi/driver/spidev'
|
51
|
+
s=SPI::Driver::SPIdev.new(device: '/dev/spidev32766.0')
|
52
|
+
s.speed=500000
|
53
|
+
s.xfer(txdata: [0x10,0x00])
|
54
|
+
```
|
55
|
+
This should result in a 2 byte array with the 2nd value reporting 36 (0x24)
|
56
|
+
|
57
|
+
|
58
|
+
## Development
|
59
|
+
|
60
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
61
|
+
|
62
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
63
|
+
|
64
|
+
## Contributing
|
65
|
+
|
66
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/spi.
|
67
|
+
|
data/Rakefile
ADDED
data/lib/spi.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require "spi/version"
|
2
|
+
require "spi/driver"
|
3
|
+
|
4
|
+
class SPI
|
5
|
+
|
6
|
+
class SPIException < Exception; end
|
7
|
+
|
8
|
+
attr_reader :driver
|
9
|
+
|
10
|
+
def initialize (args={})
|
11
|
+
|
12
|
+
if args[:driver].nil?
|
13
|
+
require "spi/driver/spidev"
|
14
|
+
@device = args[:device] or raise SPIException, "args[:device] required"
|
15
|
+
@driver=SPI::Driver::SPIdev.new(device: '/dev/spidev32766.0')
|
16
|
+
end
|
17
|
+
|
18
|
+
def speed
|
19
|
+
@driver.speed
|
20
|
+
end
|
21
|
+
|
22
|
+
def speed=(speed)
|
23
|
+
@driver.speed=speed
|
24
|
+
end
|
25
|
+
|
26
|
+
def xfer(txdata: [], length: 0)
|
27
|
+
@driver.xfer(txdata: txdata, length: length)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
data/lib/spi/driver.rb
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'spi'
|
2
|
+
|
3
|
+
class SPI
|
4
|
+
module Driver
|
5
|
+
class SPIdev < SPI::Driver::Base
|
6
|
+
attr_reader :mode
|
7
|
+
attr_reader :bits
|
8
|
+
attr_reader :speed
|
9
|
+
|
10
|
+
def initialize(args={})
|
11
|
+
raise SPIException, "No Device specified" if args[:device].nil?
|
12
|
+
raise SPIException, "Device #{args[:device]} not found" unless File.exists?(args[:device])
|
13
|
+
puts "Using Device #{args[:device]}"
|
14
|
+
@device = File.open(args[:device])
|
15
|
+
@mode=getMode
|
16
|
+
@bits=getBits
|
17
|
+
@speed=getSpeed
|
18
|
+
end
|
19
|
+
|
20
|
+
def mode=(mode)
|
21
|
+
@device.ioctl(SPI_IOC_WR_MODE(), mode.pack('C'));
|
22
|
+
@mode=getMode
|
23
|
+
end
|
24
|
+
|
25
|
+
def bits=(bits)
|
26
|
+
@device.ioctl(SPI_IOC_WR_BITS_PER_WORD(), bits.pack('C'))
|
27
|
+
@bits=getBits
|
28
|
+
end
|
29
|
+
|
30
|
+
def speed=(speed)
|
31
|
+
@device.ioctl(SPI_IOC_WR_MAX_SPEED_HZ(),[speed].pack('L'))
|
32
|
+
@speed=getSpeed
|
33
|
+
end
|
34
|
+
|
35
|
+
# TODO accessors for SPI_IOC_WR_LSB_FIRST and SPI_IOC_WR_MODE32
|
36
|
+
|
37
|
+
|
38
|
+
# By the length used will always be the greater of txdata.size and length.
|
39
|
+
# rxdata will be sized according to that maximum and txdata grown if needed
|
40
|
+
def xfer(txdata: [], length: 0)
|
41
|
+
# Ensure tx and rx buffers are the same size
|
42
|
+
length = txdata.size if txdata.size > length
|
43
|
+
txdata += (Array.new(length-txdata.size) {0}) if length > txdata.size
|
44
|
+
rxdata = (Array.new(length) { 0 })
|
45
|
+
|
46
|
+
# TODO Use the bitstruct gem ?
|
47
|
+
# https://rubygems.org/gems/bit-struct/versions/0.15.0
|
48
|
+
# Or create a class to wrap access and pack/unpack
|
49
|
+
|
50
|
+
txdata_pack = txdata.pack('C*') # __u64 (pointer to data)
|
51
|
+
rxdata_pack = rxdata.pack('C*') # __u64 (pointer to data)
|
52
|
+
|
53
|
+
# length # __u32
|
54
|
+
speed = 0 # __u32
|
55
|
+
|
56
|
+
delay_usecs = 0 # __u16
|
57
|
+
bits_per_word = 0 # __u8
|
58
|
+
cs_change = 0 # __u8
|
59
|
+
tx_nbits = 0 # __u8
|
60
|
+
rx_nbits = 0 # __u8
|
61
|
+
pad = 0 # __u16
|
62
|
+
|
63
|
+
txdata_p = [txdata_pack].pack('P').unpack('L!')[0] # 64bit pointer to txdata
|
64
|
+
rxdata_p = [rxdata_pack].pack('P').unpack('L!')[0] # 64bit pointer to rxdata
|
65
|
+
|
66
|
+
# We might need to do something special with the txdata and rxdata pointers to make them 64 bit
|
67
|
+
data = [txdata_p, rxdata_p, length, speed, delay_usecs, bits_per_word, cs_change, tx_nbits, rx_nbits, pad].pack('QQLLSCCCCS')
|
68
|
+
|
69
|
+
# We're only going to handle one message at a time for now
|
70
|
+
@device.ioctl(SPI_IOC_MESSAGE(1),data)
|
71
|
+
|
72
|
+
return rxdata_pack.unpack('C*')
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
# TODO It might be good to generalize these functions, Maybe make a IOCTL class?
|
78
|
+
def getMode
|
79
|
+
data=[0].pack('C')
|
80
|
+
@device.ioctl(SPI_IOC_RD_MODE(), data);
|
81
|
+
return data.unpack('C')[0]
|
82
|
+
end
|
83
|
+
|
84
|
+
def getBits
|
85
|
+
data=[0].pack('C')
|
86
|
+
@device.ioctl(SPI_IOC_RD_BITS_PER_WORD(), data);
|
87
|
+
return data.unpack('C')[0]
|
88
|
+
end
|
89
|
+
|
90
|
+
def getSpeed
|
91
|
+
data=[0].pack('L')
|
92
|
+
@device.ioctl(SPI_IOC_RD_MAX_SPEED_HZ(),data)
|
93
|
+
return data.unpack('L')[0]
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
# Functions and Constants based on /usr/include/asm-generic/ioctl.h
|
98
|
+
IOC_NONE = 0x00
|
99
|
+
IOC_WRITE = 0x01
|
100
|
+
IOC_READ = 0x02
|
101
|
+
|
102
|
+
def _IOC(dir, type, nr, size)
|
103
|
+
out=0
|
104
|
+
out += dir << (14 + 8 + 8) # 2 bits
|
105
|
+
out += size << ( 8 + 8) # 14 bits
|
106
|
+
out += type << ( 8) # 8 bits
|
107
|
+
out += nr # 8 bits
|
108
|
+
return out
|
109
|
+
end
|
110
|
+
|
111
|
+
def _IO(type,nr); _IOC(IOC_NONE, type, nr, 0); end
|
112
|
+
def _IOR(type,nr,size); _IOC(IOC_READ, type, nr, size); end
|
113
|
+
def _IOW(type,nr,size); _IOC(IOC_WRITE, type, nr, size); end
|
114
|
+
def _IOWR(type,nr,size); _IOC(IOC_READ |IOC_WRITE, type, nr, size); end
|
115
|
+
|
116
|
+
# Functions and Constants based on /usr/include/linux/spi/spidev.h
|
117
|
+
SPI_CPHA = 0x01
|
118
|
+
SPI_CPOL = 0x02
|
119
|
+
|
120
|
+
MODE0 = (0|0)
|
121
|
+
MODE1 = (0|SPI_CPHA)
|
122
|
+
MODE2 = (SPI_CPOL|0)
|
123
|
+
MODE3 = (SPI_CPOL|SPI_CPHA)
|
124
|
+
|
125
|
+
SPI_CS_HIGH = 0x04
|
126
|
+
SPI_LSB_FIRST = 0x08
|
127
|
+
SPI_3WIRE = 0x10
|
128
|
+
SPI_LOOP = 0x20
|
129
|
+
SPI_NO_CS = 0x40
|
130
|
+
SPI_READY = 0x80
|
131
|
+
SPI_TX_DUAL = 0x100
|
132
|
+
SPI_TX_QUAD = 0x200
|
133
|
+
SPI_RX_DUAL = 0x400
|
134
|
+
SPI_RX_QUAD = 0x800
|
135
|
+
|
136
|
+
SPI_IOC_MAGIC = 'k'.ord
|
137
|
+
|
138
|
+
# SPI Definitions based on /usr/include/linux/spi/spidev.h
|
139
|
+
# TODO we could also determine these as constants
|
140
|
+
# /* Read / Write of SPI mode (SPI_MODE_0..SPI_MODE_3) (limited to 8 bits) */
|
141
|
+
def SPI_IOC_RD_MODE; _IOR(SPI_IOC_MAGIC, 1, 1); end
|
142
|
+
def SPI_IOC_WR_MODE; _IOW(SPI_IOC_MAGIC, 1, 1); end
|
143
|
+
|
144
|
+
# /* Read / Write SPI bit justification */
|
145
|
+
def SPI_IOC_RD_LSB_FIRST; _IOR(SPI_IOC_MAGIC, 2, 1); end
|
146
|
+
def SPI_IOC_WR_LSB_FIRST; _IOW(SPI_IOC_MAGIC, 2, 1); end
|
147
|
+
|
148
|
+
# /* Read / Write SPI device word length (1..N) */
|
149
|
+
def SPI_IOC_RD_BITS_PER_WORD; _IOR(SPI_IOC_MAGIC, 3, 1); end
|
150
|
+
def SPI_IOC_WR_BITS_PER_WORD; _IOW(SPI_IOC_MAGIC, 3, 1); end
|
151
|
+
|
152
|
+
# /* Read / Write SPI device default max speed hz */
|
153
|
+
def SPI_IOC_RD_MAX_SPEED_HZ; _IOR(SPI_IOC_MAGIC, 4, 4); end
|
154
|
+
def SPI_IOC_WR_MAX_SPEED_HZ; _IOW(SPI_IOC_MAGIC, 4, 4); end
|
155
|
+
|
156
|
+
# /* Read / Write of the SPI mode field */
|
157
|
+
def SPI_IOC_RD_MODE32; _IOR(SPI_IOC_MAGIC, 5, 4); end
|
158
|
+
def SPI_IOC_WR_MODE32; _IOW(SPI_IOC_MAGIC, 5, 4); end
|
159
|
+
|
160
|
+
# If n * sizeof(spi_ioc_transfer) > 1 << _IOC_SIZEBITS we should return 0 instead
|
161
|
+
# In other words, I think the limit is the number of 32byte array we can fit in a 14 bit number
|
162
|
+
# See the relevant #define
|
163
|
+
def SPI_IOC_MESSAGE(n); _IOW(SPI_IOC_MAGIC, 0, n*32); end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
__END__
|
170
|
+
class SPIException < Exception ; end
|
171
|
+
class SPINoDevice < SPIException ; end
|
172
|
+
|
173
|
+
|
174
|
+
NB: Before 3.15 __u32 and tx_nbits/rx_nbits didn't exist. Some distros seem to have the old header even on newer kernels
|
175
|
+
|
176
|
+
|
177
|
+
|
data/lib/spi/version.rb
ADDED
data/spi.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'spi/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "spi"
|
8
|
+
spec.version = SPI::VERSION
|
9
|
+
spec.authors = ["Mike Axford"]
|
10
|
+
spec.email = ["mike@m1ari.co.uk"]
|
11
|
+
|
12
|
+
spec.summary = %q{SPI access library }
|
13
|
+
spec.description = %q{Module for configuring and communicating with devices on an SPI device, useful for various Small Board Computers such as RaspberryPi and C.H.I.P.}
|
14
|
+
spec.homepage = "https://github.com/m1ari/ruby-spi"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.11"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: spi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mike Axford
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-03-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.11'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.11'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description: Module for configuring and communicating with devices on an SPI device,
|
42
|
+
useful for various Small Board Computers such as RaspberryPi and C.H.I.P.
|
43
|
+
email:
|
44
|
+
- mike@m1ari.co.uk
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".gitignore"
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE.txt
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- lib/spi.rb
|
55
|
+
- lib/spi/driver.rb
|
56
|
+
- lib/spi/driver/spidev.rb
|
57
|
+
- lib/spi/version.rb
|
58
|
+
- spi.gemspec
|
59
|
+
homepage: https://github.com/m1ari/ruby-spi
|
60
|
+
licenses:
|
61
|
+
- MIT
|
62
|
+
metadata: {}
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements: []
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 2.2.2
|
80
|
+
signing_key:
|
81
|
+
specification_version: 4
|
82
|
+
summary: SPI access library
|
83
|
+
test_files: []
|