· 

SPIで出力ポートを増やす(Arduino)

例えばたくさんのSPIデバイスをArduinoで制御する際に、CSがArduinoのポートを占拠してしまうこと、あると思います。どうするか、、、もう一つArduinoを持ってきて、それはCSをコントロールするためだけに使うというアイデア、、、2つ使うなら分担すれば、、、という案もあるけど、2つのArduinoで情報を共有する方法も考えないといけなくなるので、やっぱり、頭脳は一つのほうが素人にはやりやすい。いやいや、じゃぁピン数の多いマイコンにすれば、、、まぁその通りですが。

(ちなみに個人的にはI2Cのほうが好きで、I2Cだとこういう問題に直面することはないですが、私的な好き嫌いを乗り越えないといけないときもあります。)

で、こんなイメージです。

では、さっそくSlaveのプログラムです。

0x55につづくB yteでPORTCを上げ下げします。CSがHになったときにバッファ配列位置をクリアしていますが、このせいで、Master側には待ち時間が必要になることになります。ほかにいい手があったら知りたい、、、本当ならPCINTで割り込みをかければいいんだろうけど、SPI Slaveにすると、SSはSPIに乗っ取られるってデータシートに書いてあったので試していない。

/*
 * SPI Slave
 * SS   - Pin10
 * MOSI - Pin11
 * MISO - Pin12
 * SCK  - Pin13
 */

#include 
unsigned char pos;
unsigned char buf[4];
void setup()
{
    DDRC = 0x3F;
    PORTC = 0x00;
    pos = 0;
    pinMode(MISO, OUTPUT); // これは必要らしい
    SPI.setBitOrder(MSBFIRST);
    SPI.setDataMode(SPI_MODE2);
    SPCR |= _BV(SPE);
    SPI.attachInterrupt();
}

ISR(SPI_STC_vect)
{
    buf[pos] = SPDR;
    pos = ((pos + 1) & 0x03);
}

void loop()
{
    if (digitalRead(SS) == HIGH)
    {
        pos = 0;
    }
    if (pos > 1)
    {
        if (buf[0] == 0x55)
        {
            PORTC = buf[1];
        }
    }
}

つぎはマスター側です。待ち時間が追加されている以外は何のことはない、ただSPIを送っているだけです。

/*
 * SPI Master
 * SS   - Pin10
 * MOSI - Pin11
 * MISO - Pin12
 * SCK  - Pin13
 */

#include 

void setup()
{
    pinMode(SS, OUTPUT);
    digitalWrite(SS, HIGH);
    SPI.setBitOrder(MSBFIRST);
    SPI.setClockDivider(SPI_CLOCK_DIV4);
    SPI.setDataMode(SPI_MODE2);
    SPI.begin();
}

void loop()
{
    char rcv;
    for (int i = 0; i < 6; i++)
    {
        CS_SEL(i);
        delay(100);
        CS_UNSEL_ALL();
        delay(100);
    }
}

void CS_SEL(unsigned char p){
    SLV_Port_CTRL(0x3F & ~(1 << p));
}

void CS_UNSEL_ALL(){
    SLV_Port_CTRL(0x3F);
}

void SLV_Port_CTRL(unsigned char port_pattern)
{
    char rcv;
    digitalWrite(SS, LOW);
    delayMicroseconds(4);
    rcv = SPI.transfer(0x55);
    delayMicroseconds(4);
    rcv = SPI.transfer(port_pattern);
    delayMicroseconds(8);
    digitalWrite(SS, HIGH);
}

こんな感じで試しています。

まぁArduinoのSS,SCK,MOSI,MISO,GNDをつなげばいいだけなんで回路図的なものは勘弁してください。Slave側のPORTCに今回はLEDをつけています。

見た目がArduinoじゃなくてだいぶアレです。

今回は3.3Vで組みたかったので、Arduinoも自作です。

左側の紙フェノール(たぶん)のユニバーサル基板に組んであるのは、いろんな足のDIPマイコンでいろいろ試すためのもので、10年ちかく前につくったものです。Arduinoにもできるように作ってあります。DIP20とかDIP14とかDIP8のソケットがじゃまくさいけど、まぁときどき役に立つんです。USB-UART変換はwsnakさんのところから購入しました。当時は素人が趣味でプリント基板を起こすなんて夢みたいなものでしたので(wsnakさんは趣味なのか仕事なのかわかりませんが)。右上はブレッドボードに組んだArduinoです。USB-UART変換は秋月のFT231Xボードです。5Vも3.3Vも設定変更なしで対応するのが良い。で、パスコンがないので動作が不安定です(マネしないでください)。レギュレータ後段にすらない。

まぁこんな感じで動きます。

ひさしぶりにArduinoさわったわ、、、やっぱり8ビットマイコンは簡潔でいいね。ARMとか意外としきたりが多い(機能が多いのでしょうがないけど)ので、ちゃちゃっと何かをやるには向いてないよねー。

SeeeduinoXIAO(SAMD21G18)でSPI Slaveやった場合のコードはこちら(ポートは3つだけ)。当初の趣旨と違うが。

Arduino M0だとこちら