差分

このページの2つのバージョン間の差分を表示します。

この比較画面へのリンク

両方とも前のリビジョン 前のリビジョン
次のリビジョン
前のリビジョン
gimmickkouza:electronic_basic:6:4_lcd_spi [2024/06/16 22:12] – [I2Cの仕組み] matsuhachigimmickkouza:electronic_basic:6:4_lcd_spi [2024/06/21 18:05] (現在) – ★7-3へのインターナルリンク追加 lutamesta
行 1: 行 1:
 ====== 6-4 LCDディスプレイ(SPI) ====== ====== 6-4 LCDディスプレイ(SPI) ======
-シリアル通信3つ目はSPIです!+シリアル通信3つ目はSPIです! 
 ==== Waveshare1.69インチIPS LCDディスプレイについて ==== ==== Waveshare1.69インチIPS LCDディスプレイについて ====
 {{:gimmickkouza:electronic_basic:6:6-31.png?400|}} {{:gimmickkouza:electronic_basic:6:6-31.png?400|}}
行 22: 行 22:
      
 ===== 6-4-1_LCD_SPI_from_sample ===== ===== 6-4-1_LCD_SPI_from_sample =====
-==== I2Cの仕組み ==== +{{youtube>trjpcrRFrFU?large}} 
-UARTに続く二つめのシリアル通信、I2Cの登場です! +    
 +==== SPIの仕組み ====
 === SPIとは === === SPIとは ===
 「SPI」はSerial Peripheral Interfaceの略です。 「SPI」はSerial Peripheral Interfaceの略です。
 SPIと他の2つのシリアル通信(UART、I2C)を見分けるのは簡単で、信号線の数がちょっと多い4本であれば、それはSPIです。 SPIと他の2つのシリアル通信(UART、I2C)を見分けるのは簡単で、信号線の数がちょっと多い4本であれば、それはSPIです。
-信号線の数が多いことを除けばその他の特徴は前項で紹介したI2Cとよく似ています。 +**信号線の数が多い**ことを除けばその他の特徴は前項で紹介したI2Cとよく似ていますが、**I2C・UARTに比べて通信速度が早く、ディスプレイの用途に向いています。** 
 +(実際ディスプレイによく使われています) 
 +  
 SPIの特徴は以下の4つです。 SPIの特徴は以下の4つです。
  
行 37: 行 38:
   * クロック信号に同期させてデータ通信を行う   * クロック信号に同期させてデータ通信を行う
  
-<alert type="danger">特徴としてはI2C・UARTに比べて早く、ディスプレイの用途に向いているというのがあります。+/*<alert type="danger">特徴としてはI2C・UARTに比べて早く、ディスプレイの用途に向いているというのがあります。
 信号線は多いけど… 信号線は多いけど…
 https://qiita.com/Gri_Gra/items/b6f901b35d3917ffc580 https://qiita.com/Gri_Gra/items/b6f901b35d3917ffc580
行 44: 行 45:
 I2CとSPIを比べると、I2Cのアドレスが同じもの使えないといったトラブルも回避できるメリットがあります I2CとSPIを比べると、I2Cのアドレスが同じもの使えないといったトラブルも回避できるメリットがあります
 (まあI2Cのアドレス変換基板なんてものも世の中にはありますが…) (まあI2Cのアドレス変換基板なんてものも世の中にはありますが…)
-</alert>+</alert>*/
  
 順番にひとつずつ解説します。 順番にひとつずつ解説します。
行 78: 行 79:
 == クロック信号で同期を取る == == クロック信号で同期を取る ==
 SPIもI2Cと同じように、クロック信号で同期を取り通信を行います。 SPIもI2Cと同じように、クロック信号で同期を取り通信を行います。
 +  
 +====UART/I2C/SPIって結局どうやって使い分けるの?====
 +<panel type="info" title="まつはちさんからのアドバイス">
 +**信号線の少なさ: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のアドレス変換基板なんてものも世の中にはありますが…)
 +
 +/*<alert type="danger">ディスプレイみたいに連続性が求められてかつ停止してたり一瞬の遅れが目立ってくるようなものはSPIが多いですね・上に書いてあったけど一応</alert>*/
 +</panel>
        
 ==== ライブラリのインストール ==== ==== ライブラリのインストール ====
行 89: 行 112:
 (なおArduinoUnoR3ではエラーは起きなかったので、R3をお持ちで興味のある方は試してみてもいいかもしれません。リファレンスページの説明に従って利用してみてください) (なおArduinoUnoR3ではエラーは起きなかったので、R3をお持ちで興味のある方は試してみてもいいかもしれません。リファレンスページの説明に従って利用してみてください)
 **デモコードの説明を書いた数時間が吹っ飛びましたが、気を取り直していきましょう(主に講師が)** **デモコードの説明を書いた数時間が吹っ飛びましたが、気を取り直していきましょう(主に講師が)**
 +  
 +
 +講師が何と戦っていたのかがわかる(?)参考ページ:[[gimmickkouza:electronic_basic:7:3-r3-r4|]]
      
 ---- ----
行 115: 行 141:
 (画像引用元:https://www.waveshare.com/wiki/1.69inch_LCD_Module) (画像引用元:https://www.waveshare.com/wiki/1.69inch_LCD_Module)
  
 +===使う部品リスト===
 +^ 部品                                                                  ^ 個数  ^
 +| Waveshare1.69インチIPS LCDディスプレイ | 1個   |
 +  
 今回の配線上の注意点は下記の通りです。 今回の配線上の注意点は下記の通りです。
 == 付属のコネクタ付きジャンパワイヤ == == 付属のコネクタ付きジャンパワイヤ ==
行 130: 行 160:
 公式サイトに(Please ensure that the power supply voltage and logic voltage are the same, otherwise it will not work properly.) との記述があるので、VCCは今回は5Vに挿します 公式サイトに(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を開いてください。 サンプルコード6-4-1_LCD_SPI_from_sample.inoを開いてください。
 +死ぬほど長いコードですが、IDEにコピペしたい方の為に一応全文貼り付けています。
 +このコードの下に若干の解説があるので、地道にスクロールして読んで頂ければ幸いです。
 +
 +<code>
 +/**************************************************************************
 +  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);
 +}
 +</code>
 +
 ライブラリ付属のサンプルスケッチそのまんまなので、今回も全文解説はしません。 ライブラリ付属のサンプルスケッチそのまんまなので、今回も全文解説はしません。
  
行 137: 行 518:
 今回使う「Adafruit ST7735 and ST7789 Library」のように、単一のモジュール用としてではなく、似たような種類のデバイスに幅広く対応できるように書かれているライブラリは非常に多いです。 今回使う「Adafruit ST7735 and ST7789 Library」のように、単一のモジュール用としてではなく、似たような種類のデバイスに幅広く対応できるように書かれているライブラリは非常に多いです。
 そういったライブラリを使いたい時なんかに「コードのどういう部分に着目して、どう改造すれば自分用にできるのか」といったシーンでの参考にしていただければ幸いです。 そういったライブラリを使いたい時なんかに「コードのどういう部分に着目して、どう改造すれば自分用にできるのか」といったシーンでの参考にしていただければ幸いです。
 +  
 == 36~52行目:ボード別のピンの設定 == == 36~52行目:ボード別のピンの設定 ==
 <code> <code>
行 158: 行 539:
 #endif #endif
 </code> </code>
-ここではディスプレイを挿すピン番号の設定をしています。+/*<alert type="danger">#if #elif #elseはマクロ上(コンパイラ上)のif文ですとかはあってもいいかも。いきなり出てきてるのでビビる</alert>*/ 
 +ここでの #if #elif #else はプリプロセッサ(コンパイルの前に実行されるコマンド)によって処理される命令です。 
 +超ざっくり言うと、"このプログラムを実行可能な状態にする前に下処理として実行される、ちょっと次元の違う存在"みたいなものです。 
 +「普段のif文と全然書き方ちゃうやんけ!」と困惑するかもしれませんが、別次元で利用される用の文なのでこのような形式で書かれています。 
 +ちょっと難しい内容の話になってくるので、詳しく知る必要が生じるまでは「これはこういうもの」と思っていただければOKかと思います。 
 +   
 +さて、ここではディスプレイを挿すピン番号の設定をしています。
 一番上から順に、Arduino Feather ESP32というマイコンを使った場合のピン設定(36~39行目)、次にESP8266を使った場合のピン設定(41~44行目)、最後に、その他のマイコンを使った場合のピン設定(46~51行目)となります。 一番上から順に、Arduino Feather ESP32というマイコンを使った場合のピン設定(36~39行目)、次にESP8266を使った場合のピン設定(41~44行目)、最後に、その他のマイコンを使った場合のピン設定(46~51行目)となります。
 今回は上二つに該当しないので、一番下のその他のボード設定の欄が適用されます。 今回は上二つに該当しないので、一番下のその他のボード設定の欄が適用されます。