[[FrontPage]]

#contents

2012/11/11からのアクセス回数 &counter;

** Raspberry PiのSPI接続を試す [#l2f913dc]
センサーのインタフェースには、RS232Cのシリアル通信を使ったものや先に接続を確認したI2Cの他に、
高速な通信をサポートするSPI接続があります。

今回は、Raspberry Piのドライバーを使ってSPIデバイスと接続する方法をArduinoを使いながら説明します。

** ArduinoとのSPI接続を試す [#b5139540]
液晶ディスプレイやデジタル・シリアル変換(DAC)などは、RS232CやI2C以上に高速な通信を必要とします。
そこで、Raspberry PiのSPIドライバーを使ってArduinoと接続することで、SPI接続の手順を確認してみます。

SPIと言ってもこれまで接続したことがないデバイスとつなぐのも大変ですし、いきなりカラー液晶と接続するのも無謀です。

そこで、[[Raspberry Pi+Arduino + SPI>http://mitchtech.net/raspberry-pi-arduino-spi/]] 
を手持ちのArduinoを使って動作を確認した後に、mbed互換ライブラリに追加してみることにします。

*** Arduinoとの接続 [#sb526373]
Raspberry PiとArduinoとの接続は、((Raspberry Pi+Arduino + SPIからの転用))

&ref(raspi_arduino_spi.png);

の通りに接続します。
| Raspberry PI | Arduiono |h
| p10 MOSI | 11 |
| p11 MISO | 12 |
| p12 SCKL | 13 |
| p23 GND | GND |

*** Arduino側のプログラムの組込み [#rdc1e83b]
[[Raspberry Pi+Arduino + SPI>http://mitchtech.net/raspberry-pi-arduino-spi/]] 
の説明通り、&ref(arduino_spi_slave.pde); のスケッチをArduino IDEで作成し、Arduinoに書き込みます。

** Raspberry Pi側の設定 [#o66c5c97]
次にRaspberryPiにSPIドライバーをセットします。

I2Cと同様に/etc/modulesに以下の1行を追加します。
#pre{{
spidev
}}

更に、/etc/modprobe.d/raspi-blacklist.confのblacklist spi-bcm2708を#でコメントアウトします。
#pre{{
#blacklist spi-bcm2708
}}

ドバイバーを組み入れるためにRaspberryPiを再起動します。
#pre{{
$ sudo reboot
}}

再起動後に正常にSPIモジュールが組み込まれたかlsmodで確認します。
#pre{{
$ lsmod | grep spi
spidev                  5136  0 
spi_bcm2708             4401  0 
}}

*** Hello worldを送る [#t0691142]
テスト用プログラム &ref(spidev_test.c); をコンパイルします。

#pre{{
$ gcc spidev_test.c -o spidev_test
}}

ArduionoのIDEメニューで「ツール」→「シリアルモニター」を選択し、ボーレートを115200 bpsにセットします。

これで準備完了です。以下のコマンドを実行してHello worldという文字列をArduinoに送ります。
#pre{{
$ $ sudo ./spidev_test 
spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)
}}

と出力され、シリアルモニターにHelllo Worldが出力されたら成功です。

&ref(th_SPI-Test.jpg);

** mbed互換ライブラリにSPIを追加 [#ec9f5247]
接続確認ができたので、mbed互換ライブラリにSPIクラスを追加します。

*** SPI.h [#d7fb64ac]
I2Cと同様にmbedのSPIヘッダをベースに修正します。

&ref(SPI.h); のprotectedとprivateを以下のように定義します。
#pre{{
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 [#n8e55fba]
次に&ref(SPI.cpp); ですが、基本的にI2Cと同じ方式で作成しました。
SPIのデバイスをオープンし、ioctlを使って制御します。
#pre{{
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];
}
}}

*** 動作確認 [#obaeff99]
最後にテスト用のメインを作成し、動作を確認します。

SPITest.cppを以下のように作成し、
#pre{{
#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);
    }
}
}}

コマンドを実行するには、以下のようにします。
#pre{{
$ sudo ./TestS
}}

&ref(RaspberryPi+Arduino.png);


** 最新のソース [#z1925ef7]
互換ライブラリにSPIを追加したファイルを以下に置きます。
- &ref(Raspberry-Pi.tgz);

液晶モジュールとの接続は、[[raspberrypi/MARMEX_OBの液晶モジュールと接続]]に追記しました。

** コメント [#rc1f1933]
#vote(おもしろかった[44],そうでもない[1],わかりずらい[5])
#vote(おもしろかった[45],そうでもない[1],わかりずらい[5])

皆様のご意見、ご希望をお待ちしております。
- 突然Arduino に繋ぐのでわかりにくい。RPi とArduino はUSB で接続されているので、これを利用した接続の説明があると助かります。この後にSPI接続の説明があると分かりやすい。RPi とArduino をつなぐ説明はどこをみればよいでしょうか。よろしく。 -- [[ysato]] &new{2014-03-09 (日) 10:01:06};
- YouTube で Programming an Arduino from Raspberry Pi を見つけました。http://www.youtube.com/watch?v=mfIacE-SPvg 参考になります。 -- [[ysato]] &new{2014-03-10 (月) 12:23:53};
- ysatoさま、ご指摘ありがとうございます。結線のテーブルとなぜArduinoとRaspberry Piを最初に接続した説明を追加しました。 -- [[竹本 浩]] &new{2014-03-10 (月) 21:22:23};
- 面白かったです。 -- [[はぎの]] &new{2016-06-03 (金) 14:01:53};
- RPiのgpioは3.3Vですが、arduinoからの出力を分圧してRPiに接続しなくて良いのでしょうか? -- [[774]] &new{2017-05-19 (金) 16:32:47};

#comment_kcaptcha

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
SmartDoc