raspberry piのSPIで3つ以上のデバイスを接続する
raspberry piではドライバが公開されているので、簡単にSPIでデバイスを接続できます。 しかしraspberry piでは仕様上、3つ以上のデバイスをSPIで接続することができません。 今回はもっとたくさんのデバイスをSPIで接続したかったので、pythonで頑張ってみました。
なお、以下の内容は次の環境を前提としています。
raspberry pi 3B+ Raspbian 9 python3.7 py-spidev 3.4
方針
SPIではひとつのバスに複数のデバイス(スレーブ)を接続できます。 しかしただ同じバスに接続しただけではどのスレーブと通信したいかがわかりません。 そこで、CS(Chip Selector)を各スレーブに個別にマスターと繋がるように配線します。 初期状態ではすべてのスレーブのCSをHighにしておき、通信したいときは通信対象のスレーブのCSだけLowにします。 raspberry pi ではCSが2つしかないので、同時に2つまでのスレーブしか接続できません。
スレーブを3つ以上接続するためにはCSとして利用できるピンを増やせばいいわけです。 CSは単純に通信中にLowに落とすことができればいいので、wiringpiで操作することにします。
またSPIをpythonで利用するために、py-spidevを利用させていただきました。
コード
#!/usr/bin/python import wiringpi from spidev import SpiDev class SPIMassChips(SpiDev): def __init__(self): super().__init__() def set_cs(self, cs_pins): self.cs_pins = cs_pins wiringpi.wiringPiSetup() for self.__i in self.cs_pins: wiringpi.pinMode(self.__i, 1) wiringpi.digitalWrite(self.__i, 1) def open(self, bus, chip): self.bus = bus self.chip = chip if 0 > self.chip or self.chip > (len(self.cs_pins) - 1): raise ValueError("Invalid chip number.") super().open(self.bus, 0) def close(self): super().close() def xfer2(self, args): for self.__i in self.cs_pins: self.__j = 0 while not wiringpi.digitalRead(self.__i): self.__j = self.__j + 1 if self.__j > 100: raise UserWarning("Device is busy.") wiringpi.digitalWrite(self.cs_pins[self.chip], 0) self.__value = super().xfer2(args) wiringpi.digitalWrite(self.cs_pins[self.chip], 1) return self.__value
見ての通り、py-spidevを継承したクラスになっています。
py-spidevでデータを読み書きする直前直後にwiringpi
でCSに見立てたピンの出力を操作することで3つ以上のスレーブに対応できるようになっています。
実際にSPIでデータを読み書きするメソッドはxfer2()
のみ実装しています。
他のメソッドは私の中で需要がなかったので対応していません。
xfer2()
では一応簡単に排他制御っぽいことやっています。
というのもwitingpi
でCSをLowに落としてから継承元のxfer2()
でデータを読み書きするまでの間に、CSに指定したピンの出力が変更されると意図したスレーブと通信できなくなってしまいます。
そこで、通信開始前にCSに指定されているピンの中にLowになっているものがないか確認し、もしLowになっているものがあればHighになるまで待つようにしました。
また待ち時間は適当なところでタイムアウトするようにしました(ループ回数の100は適当です)。
使い方
本家py-spidevとそんなに大きくは変わりません。
spi = SPIMassChips() # CSとしてスレーブに接続しているピンを設定します。 cs_pins = [21, 22, 23, 24] spi.set_cs(cs_pins) # 本家py-spdevと同じようにバスの番号とスレーブの番号を指定します。 # スレーブの番号は`set_cs()`で渡した配列のインデックスになります(ここでは24番ピンに接続されたスレーブが対象になる)。 spi.open(0, 3) # 本家py-spidevと同様にデータをやり取りできます。 send_data = [0, 0, 0] received_data = xfer2(send_data)