2012/11/11からのアクセス回数 41304
センサーのインタフェースには、RS232Cのシリアル通信を使ったものや先に接続を確認したI2Cの他に、 高速な通信をサポートするSPI接続があります。
今回は、Raspberry Piのドライバーを使ってSPIデバイスと接続する方法をArduinoを使いながら説明します。
液晶ディスプレイやデジタル・シリアル変換(DAC)などは、RS232CやI2C以上に高速な通信を必要とします。 そこで、Raspberry PiのSPIドライバーを使ってArduinoと接続することで、SPI接続の手順を確認してみます。
SPIと言ってもこれまで接続したことがないデバイスとつなぐのも大変ですし、いきなりカラー液晶と接続するのも無謀です。
そこで、Raspberry Pi+Arduino + SPI を手持ちのArduinoを使って動作を確認した後に、mbed互換ライブラリに追加してみることにします。
Raspberry PiとArduinoとの接続は、*1
の通りに接続します。
Raspberry PI | Arduiono |
p10 MOSI | 11 |
p11 MISO | 12 |
p12 SCKL | 13 |
p23 GND | GND |
Raspberry Pi+Arduino + SPI
の説明通り、arduino_spi_slave.pde のスケッチをArduino IDEで作成し、Arduinoに書き込みます。
次にRaspberryPiにSPIドライバーをセットします。
I2Cと同様に/etc/modulesに以下の1行を追加します。
spidev
更に、/etc/modprobe.d/raspi-blacklist.confのblacklist spi-bcm2708を#でコメントアウトします。
#blacklist spi-bcm2708
ドバイバーを組み入れるためにRaspberryPiを再起動します。
$ sudo reboot
再起動後に正常にSPIモジュールが組み込まれたかlsmodで確認します。
$ lsmod | grep spi spidev 5136 0 spi_bcm2708 4401 0
テスト用プログラム spidev_test.c をコンパイルします。
$ gcc spidev_test.c -o spidev_test
ArduionoのIDEメニューで「ツール」→「シリアルモニター」を選択し、ボーレートを115200 bpsにセットします。
これで準備完了です。以下のコマンドを実行してHello worldという文字列をArduinoに送ります。
$ $ sudo ./spidev_test spi mode: 0 bits per word: 8 max speed: 500000 Hz (500 KHz)
と出力され、シリアルモニターにHelllo Worldが出力されたら成功です。
接続確認ができたので、mbed互換ライブラリにSPIクラスを追加します。
I2Cと同様にmbedのSPIヘッダをベースに修正します。
SPI.h のprotectedとprivateを以下のように定義します。
protected: const char * _name; PinName _mosi; PinName _miso; PinName _sclk; int _fd; int _bits; int _mode; int _hz; int _delay; // delay usecs private: uint8_t _tx[1]; uint8_t _rx[1];
次にSPI.cpp ですが、基本的にI2Cと同じ方式で作成しました。
SPIのデバイスをオープンし、ioctlを使って制御します。
SPI::SPI(PinName mosi, PinName miso, PinName sclk, const char *name) : _mosi(-1) , _miso(-1) , _sclk(-1) , _fd(-1) , _bits(8) , _hz(1000000) , _mode(0) , _delay(0) { _name = (char *)name; // ハードSPIのみをサポート(ピン固定) if (mosi == MOSI && miso == MISO && sclk == SCKL) { _fd = open("/dev/spidev0.0", O_RDWR); if (ioctl(_fd, SPI_IOC_WR_BITS_PER_WORD, &_bits) < 0) return; if (ioctl(_fd, SPI_IOC_WR_MODE, &_mode) < 0) return; if (ioctl(_fd, SPI_IOC_WR_MAX_SPEED_HZ, &_hz) < 0) return; } } void SPI::format(int bits, int mode) { if (_fd >= 0) { _bits = bits; _mode = mode; if (ioctl(_fd, SPI_IOC_WR_BITS_PER_WORD, &_bits) < 0) return; if (ioctl(_fd, SPI_IOC_WR_MODE, &_mode) < 0) return; } } void SPI::frequency(int hz) { if (_fd >= 0) { _hz = hz; if (ioctl(_fd, SPI_IOC_WR_MAX_SPEED_HZ, &_hz) < 0) return; } } int SPI::write(int value) { int ret; _tx[0] = value; _rx[0] = 0; struct spi_ioc_transfer tr; tr.tx_buf = (unsigned long)_tx; tr.rx_buf = (unsigned long)_rx; tr.len = 1; tr.delay_usecs = _delay; tr.speed_hz = _hz; tr.bits_per_word = _bits; if ((ret = ioctl(_fd, SPI_IOC_MESSAGE(1), &tr)) < 0) return ret; return _rx[0]; }
最後にテスト用のメインを作成し、動作を確認します。
SPITest.cppを以下のように作成し、
#include <mbed.h> #include <stdio.h> SPI spi(p10, p11, p12); int main() { char* buf = "Hello world\n"; int value; spi.frequency(500000); while((value = *buf++)) { spi.write(value); } }
コマンドを実行するには、以下のようにします。
$ sudo ./TestS
互換ライブラリにSPIを追加したファイルを以下に置きます。
液晶モジュールとの接続は、raspberrypi/MARMEX_OBの液晶モジュールと接続に追記しました。
皆様のご意見、ご希望をお待ちしております。