6-4 LCDディスプレイ(SPI)

シリアル通信3つ目はSPIです! 

今回使用するディスプレイは、Waveshareの1.69インチIPS LCDディスプレイです。

IPSはLCDの一種で、視野角が非常に広いのが特長です。
もしディスプレイを使ってきぐるみの目、講師が呼称するところの“電子目”を作りたい場合は、視野角が広いディスプレイの使用をオススメします。
視野角が狭いディスプレイは、正面から見る分には問題はないのですが、上下左右から角度をつけて見た際に、画面のコントラストが正常に見えなくなってしまいます。
電子目用に視野角の広いディスプレイを選びたい場合は、IPS型のLCD(液晶)か、OLED(有機EL)がオススメです。
  

参考1:
 Amazonで買った安い中華製LCD(非IPS型)
 正面から見た場合(左)はきれいに表示されるが、少し傾けただけ(右)で残念な見た目になる 
  

参考2:
 フルカラーOLEDディスプレイ
 かなり角度をつけて傾けても綺麗に見える
  

   

SPIとは

「SPI」はSerial Peripheral Interfaceの略です。
SPIと他の2つのシリアル通信(UART、I2C)を見分けるのは簡単で、信号線の数がちょっと多い4本であれば、それはSPIです。
信号線の数が多いことを除けばその他の特徴は前項で紹介したI2Cとよく似ていますが、I2C・UARTに比べて通信速度が早く、ディスプレイの用途に向いています。
(実際ディスプレイによく使われています)
  
SPIの特徴は以下の4つです。

  • 信号線は「SCK【CLK】」、「CIPO(MISO)【DO】」、「COPI(MOSI)【DIN】」、「CS(SS)」の4本
  • アドレスの代わりに「CS(SS)」を使って通信相手を指定する
  • 「コントローラ(マスタ)」と「ペリフェラル (スレーブ)」を分け、コントローラが制御を行う
  • クロック信号に同期させてデータ通信を行う

順番にひとつずつ解説します。
  

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と同じように、クロック信号で同期を取り通信を行います。
  

まつはちさんからのアドバイス

信号線の少なさ:UART>I2C>SPI
通信の速さ:SPI>I2C>UART

です。この特徴を踏まえて、場合によっては使い分けが必要です。下記のリンクもご参照ください。
https://qiita.com/Gri_Gra/items/b6f901b35d3917ffc580
  
作った回路を制作物に実装するのであれば、配線の長さについて考慮する必要があります。
作る物次第で一概には言えませんが、SPIやI2Cでは長い配線は用いることができないと思っておいた方がいいです。
場合によっては基板内くらいの距離、数cm~10数cm程度の配線にしておかないと通信不良を起こすこともあります。
(ちなみに講師はそれを知らずにI2C機器に20cmの配線を使おうとして通信不良を起こし、トラブルシューティングに数日溶かしたことがあります byるためすた)
対してUARTは比較的長めの配線を使うことができます。
  
ディスプレイのように、連続性が求められてかつ停止や一瞬の遅れが目立つような機器にはSPIが向いています。
  
またI2CとSPIを比べると、「I2Cのアドレスが同じものを使えない~~~」といったトラブルを、SPIでは回避できるメリットがあります。
(とはいえ、I2Cのアドレス変換基板なんてものも世の中にはありますが…)

   

今回使うWAVESHARE-24382ディスプレイは、メーカーのWaveshareがディスプレイ動作用の独自のデモコードを配布しています。
こちらのデモコードはWaveshareの公式リファレンスページからダウンロードできます
(https://www.waveshare.com/wiki/1.69inch_LCD_Module)
  
……が!講師が試しに使ってみようとしたところ、ArduinoUnoR4ではコンパイルエラーが発生することを確認しました。
なので、こちらのデモコードは今回使いません。

(なおArduinoUnoR3ではエラーは起きなかったので、R3をお持ちで興味のある方は試してみてもいいかもしれません。リファレンスページの説明に従って利用してみてください)
デモコードの説明を書いた数時間が吹っ飛びましたが、気を取り直していきましょう(主に講師が)
  

講師が何と戦っていたのかがわかる(?)参考ページ:7-3 Arduino Uno R4と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)

使う部品リスト

部品 個数
Waveshare1.69インチIPS LCDディスプレイ 1個

  
今回の配線上の注意点は下記の通りです。

付属のコネクタ付きジャンパワイヤ

まず、ディスプレイ付属のジャンパ線がついたコネクタをディスプレイに挿します
コネクタの向きは、表面がすべすべの方が裏・歯のような段々がある方が表です。
(ディスプレイのソケットの内側のピンの位置と、コネクタの穴の位置を合わせてみるとわかりやすいかもしれません)
挿し込む際にディスプレイ表面やディスプレイのフチを強く押さないようご注意ください
ちなみに仕様なのか、講師が買った個体がハズレだったのかはわかりませんが、講師のもとにはジャンパ線の色順が完全に上下逆の(つまり、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を開いてください。
死ぬほど長いコードですが、IDEにコピペしたい方の為に一応全文貼り付けています。
このコードの下に若干の解説があるので、地道にスクロールして読んで頂ければ幸いです。

/**************************************************************************
  This is a library for several Adafruit displays based on ST77* drivers.

  This example works with the 1.14" TFT breakout
    ----> https://www.adafruit.com/product/4383
  The 1.3" TFT breakout
    ----> https://www.adafruit.com/product/4313
  The 1.47" TFT breakout
    ----> https://www.adafruit.com/product/5393
  The 1.54" TFT breakout
    ----> https://www.adafruit.com/product/3787
  The 1.69" TFT breakout
    ----> https://www.adafruit.com/product/5206
  The 1.9" TFT breakout
    ----> https://www.adafruit.com/product/5394
  The 2.0" TFT breakout
    ----> https://www.adafruit.com/product/4311


  Check out the links above for our tutorials and wiring diagrams.
  These displays use SPI to communicate, 4 or 5 pins are required to
  interface (RST is optional).

  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.
  MIT license, all text above must be included in any redistribution
 **************************************************************************/

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include <SPI.h>

#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

// 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);


float p = 3.1415926;

void setup(void) {
  Serial.begin(9600);
  Serial.print(F("Hello! ST7789 TFT Test"));

  // 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

  // SPI speed defaults to SPI_DEFAULT_FREQ defined in the library, you can override it here
  // Note that speed allowable depends on chip and quality of wiring, if you go too fast, you
  // may end up with a black screen some times, or all the time.
  //tft.setSPISpeed(40000000);
  Serial.println(F("Initialized"));

  uint16_t time = millis();
  tft.fillScreen(ST77XX_BLACK);
  time = millis() - time;

  Serial.println(time, DEC);
  delay(500);

  // large block of text
  tft.fillScreen(ST77XX_BLACK);
  testdrawtext("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur adipiscing ante sed nibh tincidunt feugiat. Maecenas enim massa, fringilla sed malesuada et, malesuada sit amet turpis. Sed porttitor neque ut ante pretium vitae malesuada nunc bibendum. Nullam aliquet ultrices massa eu hendrerit. Ut sed nisi lorem. In vestibulum purus a tortor imperdiet posuere. ", ST77XX_WHITE);
  delay(1000);

  // tft print function!
  tftPrintTest();
  delay(4000);

  // a single pixel
  tft.drawPixel(tft.width()/2, tft.height()/2, ST77XX_GREEN);
  delay(500);

  // line draw test
  testlines(ST77XX_YELLOW);
  delay(500);

  // optimized lines
  testfastlines(ST77XX_RED, ST77XX_BLUE);
  delay(500);

  testdrawrects(ST77XX_GREEN);
  delay(500);

  testfillrects(ST77XX_YELLOW, ST77XX_MAGENTA);
  delay(500);

  tft.fillScreen(ST77XX_BLACK);
  testfillcircles(10, ST77XX_BLUE);
  testdrawcircles(10, ST77XX_WHITE);
  delay(500);

  testroundrects();
  delay(500);

  testtriangles();
  delay(500);

  mediabuttons();
  delay(500);

  Serial.println("done");
  delay(1000);
}

void loop() {
  tft.invertDisplay(true);
  delay(500);
  tft.invertDisplay(false);
  delay(500);
}

void testlines(uint16_t color) {
  tft.fillScreen(ST77XX_BLACK);
  for (int16_t x=0; x < tft.width(); x+=6) {
    tft.drawLine(0, 0, x, tft.height()-1, color);
    delay(0);
  }
  for (int16_t y=0; y < tft.height(); y+=6) {
    tft.drawLine(0, 0, tft.width()-1, y, color);
    delay(0);
  }

  tft.fillScreen(ST77XX_BLACK);
  for (int16_t x=0; x < tft.width(); x+=6) {
    tft.drawLine(tft.width()-1, 0, x, tft.height()-1, color);
    delay(0);
  }
  for (int16_t y=0; y < tft.height(); y+=6) {
    tft.drawLine(tft.width()-1, 0, 0, y, color);
    delay(0);
  }

  tft.fillScreen(ST77XX_BLACK);
  for (int16_t x=0; x < tft.width(); x+=6) {
    tft.drawLine(0, tft.height()-1, x, 0, color);
    delay(0);
  }
  for (int16_t y=0; y < tft.height(); y+=6) {
    tft.drawLine(0, tft.height()-1, tft.width()-1, y, color);
    delay(0);
  }

  tft.fillScreen(ST77XX_BLACK);
  for (int16_t x=0; x < tft.width(); x+=6) {
    tft.drawLine(tft.width()-1, tft.height()-1, x, 0, color);
    delay(0);
  }
  for (int16_t y=0; y < tft.height(); y+=6) {
    tft.drawLine(tft.width()-1, tft.height()-1, 0, y, color);
    delay(0);
  }
}

void testdrawtext(char *text, uint16_t color) {
  tft.setCursor(0, 0);
  tft.setTextColor(color);
  tft.setTextWrap(true);
  tft.print(text);
}

void testfastlines(uint16_t color1, uint16_t color2) {
  tft.fillScreen(ST77XX_BLACK);
  for (int16_t y=0; y < tft.height(); y+=5) {
    tft.drawFastHLine(0, y, tft.width(), color1);
  }
  for (int16_t x=0; x < tft.width(); x+=5) {
    tft.drawFastVLine(x, 0, tft.height(), color2);
  }
}

void testdrawrects(uint16_t color) {
  tft.fillScreen(ST77XX_BLACK);
  for (int16_t x=0; x < tft.width(); x+=6) {
    tft.drawRect(tft.width()/2 -x/2, tft.height()/2 -x/2 , x, x, color);
  }
}

void testfillrects(uint16_t color1, uint16_t color2) {
  tft.fillScreen(ST77XX_BLACK);
  for (int16_t x=tft.width()-1; x > 6; x-=6) {
    tft.fillRect(tft.width()/2 -x/2, tft.height()/2 -x/2 , x, x, color1);
    tft.drawRect(tft.width()/2 -x/2, tft.height()/2 -x/2 , x, x, color2);
  }
}

void testfillcircles(uint8_t radius, uint16_t color) {
  for (int16_t x=radius; x < tft.width(); x+=radius*2) {
    for (int16_t y=radius; y < tft.height(); y+=radius*2) {
      tft.fillCircle(x, y, radius, color);
    }
  }
}

void testdrawcircles(uint8_t radius, uint16_t color) {
  for (int16_t x=0; x < tft.width()+radius; x+=radius*2) {
    for (int16_t y=0; y < tft.height()+radius; y+=radius*2) {
      tft.drawCircle(x, y, radius, color);
    }
  }
}

void testtriangles() {
  tft.fillScreen(ST77XX_BLACK);
  uint16_t color = 0xF800;
  int t;
  int w = tft.width()/2;
  int x = tft.height()-1;
  int y = 0;
  int z = tft.width();
  for(t = 0 ; t <= 15; t++) {
    tft.drawTriangle(w, y, y, x, z, x, color);
    x-=4;
    y+=4;
    z-=4;
    color+=100;
  }
}

void testroundrects() {
  tft.fillScreen(ST77XX_BLACK);
  uint16_t color = 100;
  int i;
  int t;
  for(t = 0 ; t <= 4; t+=1) {
    int x = 0;
    int y = 0;
    int w = tft.width()-2;
    int h = tft.height()-2;
    for(i = 0 ; i <= 16; i+=1) {
      tft.drawRoundRect(x, y, w, h, 5, color);
      x+=2;
      y+=3;
      w-=4;
      h-=6;
      color+=1100;
    }
    color+=100;
  }
}

void tftPrintTest() {
  tft.setTextWrap(false);
  tft.fillScreen(ST77XX_BLACK);
  tft.setCursor(0, 30);
  tft.setTextColor(ST77XX_RED);
  tft.setTextSize(1);
  tft.println("Hello World!");
  tft.setTextColor(ST77XX_YELLOW);
  tft.setTextSize(2);
  tft.println("Hello World!");
  tft.setTextColor(ST77XX_GREEN);
  tft.setTextSize(3);
  tft.println("Hello World!");
  tft.setTextColor(ST77XX_BLUE);
  tft.setTextSize(4);
  tft.print(1234.567);
  delay(1500);
  tft.setCursor(0, 0);
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextColor(ST77XX_WHITE);
  tft.setTextSize(0);
  tft.println("Hello World!");
  tft.setTextSize(1);
  tft.setTextColor(ST77XX_GREEN);
  tft.print(p, 6);
  tft.println(" Want pi?");
  tft.println(" ");
  tft.print(8675309, HEX); // print 8,675,309 out in HEX!
  tft.println(" Print HEX!");
  tft.println(" ");
  tft.setTextColor(ST77XX_WHITE);
  tft.println("Sketch has been");
  tft.println("running for: ");
  tft.setTextColor(ST77XX_MAGENTA);
  tft.print(millis() / 1000);
  tft.setTextColor(ST77XX_WHITE);
  tft.print(" seconds.");
}

void mediabuttons() {
  // play
  tft.fillScreen(ST77XX_BLACK);
  tft.fillRoundRect(25, 10, 78, 60, 8, ST77XX_WHITE);
  tft.fillTriangle(42, 20, 42, 60, 90, 40, ST77XX_RED);
  delay(500);
  // pause
  tft.fillRoundRect(25, 90, 78, 60, 8, ST77XX_WHITE);
  tft.fillRoundRect(39, 98, 20, 45, 5, ST77XX_GREEN);
  tft.fillRoundRect(69, 98, 20, 45, 5, ST77XX_GREEN);
  delay(500);
  // play color
  tft.fillTriangle(42, 20, 42, 60, 90, 40, ST77XX_BLUE);
  delay(50);
  // pause color
  tft.fillRoundRect(39, 98, 20, 45, 5, ST77XX_RED);
  tft.fillRoundRect(69, 98, 20, 45, 5, ST77XX_RED);
  // play color
  tft.fillTriangle(42, 20, 42, 60, 90, 40, ST77XX_GREEN);
}

ライブラリ付属のサンプルスケッチそのまんまなので、今回も全文解説はしません。

ただ“スケッチ例”から呼び出した状態から、これを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

ここでの #if #elif #else はプリプロセッサ(コンパイルの前に実行されるコマンド)によって処理される命令です。
超ざっくり言うと、“このプログラムを実行可能な状態にする前に下処理として実行される、ちょっと次元の違う存在”みたいなものです。
「普段のif文と全然書き方ちゃうやんけ!」と困惑するかもしれませんが、別次元で利用される用の文なのでこのような形式で書かれています。
ちょっと難しい内容の話になってくるので、詳しく知る必要が生じるまでは「これはこういうもの」と思っていただければOKかと思います。
  
さて、ここではディスプレイを挿すピン番号の設定をしています。
一番上から順に、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); //数値は君が使うディスプレイのサイズに書き換えてくれよな!(←英語)

というのが多いです。
もちろんその行を自分で発見して数値を手動で打ちかえる必要があります。
  
  
  
  

第6章はこれにて終了です!
ここまですべて修了したあなたは、本資料における入門レベルの内容をマスターしたことになります!
  

  
本資料で学んだ内容を活かし、ぜひ電子ギミックの制作にチャレンジしてみてください。
お疲れさまでした!
  
  
lutamesta