Arduino とシフトレジスタ 74HC595、74HC597 間で、SPIで同時に送信と受信を行なってみました。
前回までに、Arduino を コントローラ (Controller)、シフトレジスタをペリフェラル (Peripheral) として SPIで通信する方法を実験しました。
SPI はコントローラとペリフェラルの間でデータを交換するしくみですから、送信と受信を同時に行なうことができます。今回は、じっさいに送受信を同時に行なうことができることを確認してみましょう。
回路図
回路は、前々回の送信、前回の受信の回路を合わせただけです。
シフトレジスタ 74HC595、74HC597 まわりに変更はありません。Arduino では COPI と CIPO の両方が、それぞれ 74HC595、74HC597 へつながります。
Arduino をはさんで左右にシフトレジスタを描いていますが、本来のペリフェラルは 1つのシフトレジスタで両方の機能を持ちます。PIPO のシフトレジスタなら実現できるのかなと思いますが、部品箱にないので確認してません。フリップフロップで作ればいいじゃん。あ、いや、それは…
左の 8ビット Dipスイッチのデータを Arduino で受信して、右の 8個の LEDを点灯制御します。SPI ですので Arduino につながるラインは 4本です。
スケッチ
スケッチも、基本はこれまで同様です。
しかし、受信したデータをそのまま送信するのではあまりに芸がなさすぎるので、受信した 8ビットバイナリデータを 8ビットグレイコードに変換して送信することにしました。
- // Sketch for 74HC597 to Arduino SPI-Test 2023.9.16 meyon
- #include <SPI.h>
- SPISettings mySettings(8000000, MSBFIRST, SPI_MODE0);
- byte convertBinaryCodeToGrayCode(byte bin) {
- byte gray = bin ^ (bin >> 1);
- return gray;
- }
- void setup() {
- // In Nano Every,SS is pin-D8, Mode setting required
- pinMode(SS, OUTPUT);
- SPI.begin();
- }
- void loop() {
- static byte sentValue = 0;
- static byte receivedValue = 0;
- sentValue = convertBinaryCodeToGrayCode(receivedValue);
- SPI.beginTransaction(mySettings);
- digitalWrite(SS, LOW);
- receivedValue = SPI.transfer(sentValue);
- digitalWrite(SS, HIGH);
- SPI.endTransaction();
- delay(50);
- }
7~10行目がバイナリコード bin をグレイコード gray に変換する関数です。変換は、元のバイナリコードと、それを右へひとつシフトしたデータの排他的論理和 (XOR) をとります。
gray = bin ^ (bin >> 1)
26行目で受信したデータを、22行目でグレイコードに変換し、受信と同じ 26行目で送信しています。つまり、送信するデータは 1回前に受信したデータということになります。データサンプリングも 1回前ですので、出力されるのは 2回通信した後です。
グレイコードに関しては過去記事を参照ください。
ブレッドボード
実験中のブレッドボードです。
下のブレッドボード。Dipスイッチで 8ビットバイナリデータを生成して、シフトレジスタ 74HC597 で Arduino へデータを送ります。
右上が Arduino Nano Every で、その左が データを受信して LEDに出力しているシフトレジスタ 74HC595 です。
Dip スイッチ、LED ともに左が LSB です。Dipスイッチはバイナリコード「10001011」、LED はグレイコードで「11001110」。ちゃんと変換できてますよ、ね。
複数のペリフェラルをつなぐには
SPI では、ペリフェラルを複数接続することができます。が、これまで実験した回路をそのまま複数つないでも、うまく動かないです。
ペリフェラルを複数接続する場合、選択されていないペリフェラルが動作しないようにしなければなりません。
そのために必要なことは二つ。一つは、クロック SCK を無効にしてシフトレジスタが動作しないようにすること。二つ目は、CIPO をハイインピーダンスにして他の出力と衝突しないようにすること。
まぁ、本格的に SPI を使うならモジュールを採用したほうがよいと思うので今回はやりませんけど、もしやるなら、回路をそんなふうに変更しましょう。
後記
今回は、Arduino をコントローラとして、シフトレジスタ 74HC597、74HC595 と同時に送受信できることを確認しました。データの伝送にはレイテンシが生じますが、データの送信と受信は同時に行なうことができました。
今回の実験のなかでちょっと気づいたのですが、いつも使っている JKフリップフロップの CD4027 ってクロック周波数が最大 3.5MHz (5V時) なんですね。SPI のクロックを 8MHzにすると使えない。Dフリップフロップの 74HC74 では 31MHz (4.5V時) です。こちらなら大丈夫。
っていうか、JKフリップフロップってそんだけ遅いんや、って話。以前どこかのサイトで、順序回路には Dフリップフロップを使うんだと書かれてあるのをみたのですが、その理由のひとつをみつけてしまった気がします。
今回はフリップフロップ使ってませんが、shiftIn()、shiftOut() では JKフリップフロップ使えるけど、SPIでは要注意だと、記憶しておきましょう。