文書の過去の版を表示しています。
6-4 LCDディスプレイ(SPI)
シリアル通信3つ目はSPIです!
Waveshare1.69インチIPS LCDディスプレイについて
今回使用するディスプレイは、Waveshareの1.69インチIPS LCDディスプレイです。
IPSはLCDの一種で、視野角が非常に広いのが特長です。
もしディスプレイを使ってきぐるみの目、講師が呼称するところの“電子目”を作りたい場合は、視野角が広いディスプレイの使用をオススメします。
視野角が狭いディスプレイは、正面から見る分には問題はないのですが、上下左右から角度をつけて見た際に、画面のコントラストが正常に見えなくなってしまいます。
電子目用に視野角の広いディスプレイを選びたい場合は、IPS型のLCD(液晶)か、OLED(有機EL)がオススメです。
参考1:
Amazonで買った安い中華製LCD(非IPS型)
正面から見た場合(左)はきれいに表示されるが、少し傾けただけ(右)で残念な見た目になる
参考2:
フルカラーOLEDディスプレイ
かなり角度をつけて傾けても綺麗に見える
6-4-1_LCD_SPI_from_sample
I2Cの仕組み
UARTに続く二つめのシリアル通信、I2Cの登場です!
SPIとは
「SPI」はSerial Peripheral Interfaceの略です。
SPIと他の2つのシリアル通信(UART、I2C)を見分けるのは簡単で、信号線の数がちょっと多い4本であれば、それはSPIです。
信号線の数が多いことを除けば、その他の特徴は前項で紹介したI2Cとよく似ています。
SPIの特徴は以下の4つです。
- 信号線は「SCK【CLK】」、「CIPO(MISO)【DO】」、「COPI(MOSI)【DIN】」、「CS(SS)」の4本
- アドレスの代わりに「CS(SS)」を使って通信相手を指定する
- 「コントローラ(マスタ)」と「ペリフェラル (スレーブ)」を分け、コントローラが制御を行う
- クロック信号に同期させてデータ通信を行う
信号線は多いけど…
https://qiita.com/Gri_Gra/items/b6f901b35d3917ffc580
信号線の本数はUART>I2C>SPI、速さはその逆なんで、場合によって使い分けが必要です。
また、UARTが配線を一番長くできて、SPIやI2Cは基板内くらいの距離じゃないと固まっちゃうというのもポイントです。
I2CとSPIを比べると、I2Cのアドレスが同じもの使えないといったトラブルも回避できるメリットがあります
(まあI2Cのアドレス変換基板なんてものも世の中にはありますが…)
順番にひとつずつ解説します。
4本の信号線
SPIは信号線が4本と、ちょっと多めになっています。
「SCK【CLK】」、「CIPO(MISO)【DO】」、「COPI(MOSI)【DIN】」、「CS(SS)」の4本ですが、名称もかなりややこしいです。
基本的には一番左側の名称が使われる…ようになるはずだと思います。
(括弧内の名称は、ポリコレ的観点から今後は一番左側の名称に読み替えが進んでいくものと思われますが、2024年3月現在はまだその途上にあります。
【 】内は別称として使われることがあります。ちなみにこれ以外の別称もあります。ややこしいのでやめてくれ。)
各線の解説です。
- 「SCK【CLK】」:I2Cでも出てきた同期用のクロック信号の線
- 「CIPO(MISO)【DO】」:ペリフェラル(I2Cでいうターゲット)からコントローラへデータを送る線
- 「COPI(MOSI)【DIN】」:コントローラからペリフェラルへデータを送る線
- 「CS(SS)」:アドレスの代わりに通信相手を指定する線
SCK・CIPO・COPIの3本の通信線は複数台のSPIで共有することができますが、CSだけはコントローラに繋ぐペリフェラルの数だけ用意する必要があります。
もし3台のSPI機器をArduinoに繋ぐ場合、Arduino側のSCK・CIPO・COPIピンは1個ずつで済みますが、CSは3つのピンを用意する必要があります。
CSを使って通信相手を指定
I2Cではターゲット固有のI2Cアドレスを使うことで、どのI2C機器とやり取りするかを指定しました。
SPIでは、ペリフェラルに固有のアドレスはありませんが、通信線のCSを使うことで通信相手を指定することができます。
仕組みとしては、CSピンがLOW状態のペリフェラルはコントローラとの通信が有効になります。
コントローラは通信したいペリフェラルのCSをLOWにすることで通信相手として指定し、CSがHIGH状態の他のペリフェラルは、コントローラからの信号を無視します。
「コントローラ」と「ペリフェラル」の関係
SPI通信では繋がれた機器のうち、制御する側を「コントローラ(マスタ)」、される側を「ペリフェラル(スレーブ)」と呼びます。
I2Cと同じように、制御の指示を出す「コントローラ」は必ず一つだけです。それに対し「ペリフェラル」は複数繋ぐことができます。
クロック信号で同期を取る
SPIもI2Cと同じように、クロック信号で同期を取り通信を行います。
ライブラリのインストール
今回使うWAVESHARE-24382ディスプレイは、メーカーのWaveshareがディスプレイ動作用の独自のデモコードを配布しています。
こちらのデモコードはWaveshareの公式リファレンスページからダウンロードできます
(https://www.waveshare.com/wiki/1.69inch_LCD_Module)
……が!講師が試しに使ってみようとしたところ、ArduinoUnoR4ではコンパイルエラーが発生することを確認しました。
なので、こちらのデモコードは今回使いません。
(なおArduinoUnoR3ではエラーは起きなかったので、R3をお持ちで興味のある方は試してみてもいいかもしれません。リファレンスページの説明に従って利用してみてください)
デモコードの説明を書いた数時間が吹っ飛びましたが、気を取り直していきましょう(主に講師が)
さて、WAVESHARE-24382はST7789V2ドライバを搭載しています。
なので、Waveshare提供のデモコード以外にも、ST7789のライブラリをインストールして動かすことが可能です。
今回は代替案としてそちらを使ってみたいと思います。
ArduinoIDEのライブラリマネージャを開き、検索窓に「ST7789」と打ち込んで検索します。
- “ST7789”で検索すると出てくる作者名が「by Adafruit」の「Adafruit ST7735 and ST7789 Library」
このライブラリはディスプレイ用ドライバST7735とST7789の両方に対応したライブラリです。
ちなみに6-3でも出てきた「Adafruit GFX Library」も必要になります。
- “Adafruit GFX”で検索をすると出てくる、作者名が「by Adafruit」の「AdafruitGFX Libraly」
もし前項をすっ飛ばしてここに来た方はこちらもインストールしてください。
もう一つ、忘れてはいけないのが「SPI」ライブラリです。
こちらはSPIを使う際に必要なライブラリで、ArduinoIDEに標準で付属しているのでインストールは不要です。
今回使うコードは「ST7735 and ST7789」に付属のスケッチ、「graphictest_st7789」ですが、こちらは前項と同じくこの資料のサンプルコード6-4-1_LCD_SPI_from_sample.inoとして収録しておりますので、そちらをお使い頂ければと思います。
配線図
fritzingで使えるパーツのデータがなさそうだったので、Waveshare公式の画像を引用させて頂きました
(画像引用元:https://www.waveshare.com/wiki/1.69inch_LCD_Module)
今回の配線上の注意点は下記の通りです。
付属のコネクタ付きジャンパワイヤ
まず、ディスプレイ付属のジャンパ線がついたコネクタをディスプレイに挿します
コネクタの向きは、表面がすべすべの方が裏・歯のような段々がある方が表です。
(ディスプレイのソケットの内側のピンの位置と、コネクタの穴の位置を合わせてみるとわかりやすいかもしれません)
挿し込む際にディスプレイ表面やディスプレイのフチを強く押さないようご注意ください
ちなみに仕様なのか、講師が買った個体がハズレだったのかはわかりませんが、講師のもとにはジャンパ線の色順が完全に上下逆の(つまり、VCCが紫、GNDが白、DINが緑…の)個体が送られてきました。
まあそういうこともあります。
Arduinoに配線する際に惑わされないよう気を付ければ普通に使えます。
ちなみにDCはData/Command、RSTはReset、BLはBacklightの略です
VCCは何Vに挿すか
公式サイトに(Please ensure that the power supply voltage and logic voltage are the same, otherwise it will not work properly.) との記述があるので、VCCは今回は5Vに挿します
サンプルコード解説(しません?)
サンプルコード6-4-1_LCD_SPI_from_sample.inoを開いてください。
ライブラリ付属のサンプルスケッチそのまんまなので、今回も全文解説はしません。
ただ“スケッチ例”から呼び出した状態から、これをWAVESHARE-24382でも動作にするコードにするために多少手を加えている部分がありますので、その解説をします。
今回使う「Adafruit ST7735 and ST7789 Library」のように、単一のモジュール用としてではなく、似たような種類のデバイスに幅広く対応できるように書かれているライブラリは非常に多いです。
そういったライブラリを使いたい時なんかに「コードのどういう部分に着目して、どう改造すれば自分用にできるのか」といったシーンでの参考にしていただければ幸いです。
36~52行目:ボード別のピンの設定
#if defined(ARDUINO_FEATHER_ESP32) // Feather Huzzah32 #define TFT_CS 14 #define TFT_RST 15 #define TFT_DC 32 #elif defined(ESP8266) #define TFT_CS 4 #define TFT_RST 16 #define TFT_DC 5 #else // For the breakout board, you can use any 2 or 3 pins. // These pins will also work for the 1.8" TFT shield. #define TFT_CS 10 #define TFT_RST 8//9 // Or set to -1 and connect to Arduino RESET pin #define TFT_DC 7//8 #endif
ここではディスプレイを挿すピン番号の設定をしています。
一番上から順に、Arduino Feather ESP32というマイコンを使った場合のピン設定(36~39行目)、次にESP8266を使った場合のピン設定(41~44行目)、最後に、その他のマイコンを使った場合のピン設定(46~51行目)となります。
今回は上二つに該当しないので、一番下のその他のボード設定の欄が適用されます。
ここではCS/RST/DCの3つのピン指定をしています。
このコードのデフォルトではCS:10/RST・9/DC:8でしたが、RSTとDCは先ほどの配線図に則り、8と7に修正しています。
54~66行目:ハードウェアSPIかソフトウェアSPIを使うか
// OPTION 1 (recommended) is to use the HARDWARE SPI pins, which are unique // to each board and not reassignable. For Arduino Uno: MOSI = pin 11 and // SCLK = pin 13. This is the fastest mode of operation and is required if // using the breakout board's microSD card. Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST); // OPTION 2 lets you interface the display using ANY TWO or THREE PINS, // tradeoff being that performance is not as fast as hardware SPI above. //#define TFT_MOSI 11 // Data out //#define TFT_SCLK 13 // Clock out //Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
SPIにもUARTと同じようにハードウェアSPIとソフトウェアSPIがあります。
コードのコメントにあるように、ハードウェアSPIの方が高速に通信できるという特徴があるようで、Adafruitもこちらの使用を推奨しています。
ハードウェアSPIを使う場合は59行目を有効のままにしておきます。今回はこちらでいきます。
ソフトウェアSPIを使いたい場合は、63・64行目の「//」を取ってピン指定をし、66行目の「//」も取ってこちらを有効にします。
逆に59行目は「//」を付けコメントアウトしてください。
73行目
Serial.print(F("Hello! ST7789 TFT Test"));
折角なのでシリアルモニタに表示する文字列をST7789にします(デフォルトはST77xx)
気分の問題なので変えなくても動作にまったく問題はありません。
75~91行目:使うディスプレイを指定する
// Use this initializer (uncomment) if using a 1.3" or 1.54" 240x240 TFT: //tft.init(240, 240); // Init ST7789 240x240 // OR use this initializer (uncomment) if using a 1.69" 280x240 TFT: tft.init(240, 280); // Init ST7789 280x240 // OR use this initializer (uncomment) if using a 2.0" 320x240 TFT: //tft.init(240, 320); // Init ST7789 320x240 // OR use this initializer (uncomment) if using a 1.14" 240x135 TFT: //tft.init(135, 240); // Init ST7789 240x135 // OR use this initializer (uncomment) if using a 1.47" 172x320 TFT: //tft.init(172, 320); // Init ST7789 172x320 // OR use this initializer (uncomment) if using a 1.9" 170x320 TFT: //tft.init(170, 320); // Init ST7789 170x320
このライブラリはST7789を搭載した複数の異なるディスプレイでも使いまわせるような設計になっています。
但しあらゆるディスプレイを繋ぐだけで全自動ですべてをいい感じにしてくれる…というわけではありません。
36~52行目の設定はワイヤを挿すマイコンのピン番号が違った場合を想定して設けられていました。
同様に、75~91行目はディスプレイの解像度を設定するために設けられた行です。
デフォルトでは76行目が有効になっています。
コメント欄を読むと「1.3インチか1.54インチで解像度が240×240のディスプレイを使う時はこの行を有効にしてね(概訳)」と書いてあります。
今回使うのは1.69インチで解像度280×240のディスプレイなので、76行目はコメントアウトし、79行目の「//」を消してこの行を有効にします。
余談ですが、今回のコードではディスプレイ別に複数の設定が用意されていますが、これはかなり親切なコードだと思います。
よくあるパターンだと、
tft.init(240, 240); //数値は君が使うディスプレイのサイズに書き換えてくれよな!(←英語)
というのが多いです。
もちろんその行を自分で発見して数値を手動で打ちかえる必要があります。