A/D コンバータでデジタル化して SPI 経由で raspberry pi 上から扱いたい。A/Dコンバータも SPI も使ってみたのが初めてだったので、学習もかねてライブラリ化してみた。
require 'spi_mcp'
mcp = MCP3208.new
val = mcp.channel(0)
puts val # 0 ~ 4095
もしくは適当にライブラリ単体で引数つけて実行する。
$ watch -n 1 sudo ruby spi_mcp.rb MCP3208
MCP3002(秋月で180円)を複数買ってしまったけど、特に理由が無ければ MCP3208(320円) の方が分解能高いし、たくさんチャンネル使えるし、繋ぐ必要ある配線もあんまり変わらないのでこちらを買った方が良いと思う。
ちょっとしたハマりどころとしては、Ruby から SPI を扱える pi_piper gem が raspi2 だと Ruby の FFI から呼び出してる libbcm2835.img を最新のを使わなくてはならない。2015/08/16現在、gem install pi_piper
だと古い物が使われてしまう、かつ最新の gem install pi_piper --version 2.0.beta.4
を入れてもうまくいかないため、自前で gem を build して使った方が良い(git 最新の方が libbcm2835.img が新しかったし)。
git clone https://github.com/jwhitehorn/pi_piper.git
cd pi_piper
gem install bundler rake
bundle install
rake gem
sudo gem install pkg/pi_piper-2.0.beta.4.gem
あとあと MISO と MOSI がどっちが out でどっちが in か解らなくなる…。自分用メモ。
# spi_mcp.rb
require 'pi_piper'
class SpiMCP
attr_reader :bit_resolution
attr_reader :channels
def initialize(options = {})
@bit_resolution = options[:bit_resolution] or ArgumentError.new("require :bit_resolution")
@channels = options[:channels] or ArgumentError.new("require :channels")
end
def channel(ch)
raise ArgumentError.new("channel #{ch} is not support.") unless channels.include?(ch)
result = 0
PiPiper::Spi.begin do |spi|
result = convert spi.write(*write_data(ch))
end
result
end
def convert(raw_data)
((raw_data[-2] << 8) + raw_data[-1]) & (2**bit_resolution - 1)
end
end
class MCP3002 < SpiMCP
def initialize
super bit_resolution: 10, channels: 0..1
end
def write_data(ch)
[
0b01101000 | (ch<<4),
0b00000000
]
end
end
class MCP3208 < SpiMCP
def initialize
super bit_resolution: 12, channels: 0..7
end
def write_data(ch)
control_bit = channel_control_bit(ch)
[
0b00000100 | (control_bit>>2), # 0b000001#{SINGLE/DIFF}#{D2}
(control_bit & 0b11) << 6, # 0b#{D1}#{D0}000000 (D1, D0)
0b00000000
]
end
# return 4-bit (SINGLE/DIFF, D2, D1, D0)
def channel_control_bit(ch)
(0b1000 | ch) & 0b1111
end
end
if __FILE__ == $0
begin
mcp_class = Object.const_get(ARGV[0])
rescue NameError => e
abort "usage: $ ruby #{__FILE__} MCP3208"
end
mcp = mcp_class.new
mcp.channels.each do |ch|
puts "ch #{ch}: #{mcp.channel(ch)}"
end
end