LCDモジュールを使う(ILI9341)

コンピュータ、組み込み

Arduino 環境でのLCDモジュールの表示制御をします。
LCDモジュールは多種ありますが、今回比較的中型のILI9341ドライバ搭載モデルを紹介します。
RP2040系でのSPI接続、ESP32系でのSPI接続、タッチパネル、SDカードからのJPG表示をしてみます。

今回紹介するもの

LCDモジュール (ILI9341) 2.4inch

特徴

Arduino環境で使用できる液晶カラーモニタとしては中型の2.4inch サイズです。
画面描画命令をSPIを使った通信で表示を行います。

SDカードスロットがついていますが、モニタ部とは独立していています。
電源とGNDは共通ですが、SPI配線と命令は通常のSDカードモジュールを制御するのと同じです。

感圧式タッチパネルによりタッチ操作ができます。

注意
ILI9341搭載モデルでも、SDカードスロットの無いモデルや、タッチパネルの無いモデルは販売されています。
購入の際には商品画像だけでなく、商品紹介にSDカードスロットやタッチパネルについて記載のあるものを確認してください。

画像上がタッチ無、画像下がタッチ有です。
タッチ有にはHR2046というドライバが実装されています。

製品情報
サイズ2.2, 2.4, 2.8, 3.2inch (今回 2.4インチの紹介)
LCDドライバILI9341
解像度(X, Y)240, 320
通信方式SPI
動作電圧3.3~5V
タッチドライバ感圧式タッチパネル(HR2046)
その他SDカードリーダ
外観

前面

背面

これまで使用してきたST7735と比べてみると一回り大きいです。
ピン数はタッチパネル分増えていますが、LCDのみであれば同じピン数で使用できます。

使用感

ILI9341ドライバを搭載するLCDモジュールは、中型(2.4inch~3.2inch)に使用されています。
解像度もST7735より高いので、画像の表現力が上がります。

2inchを超える画面であれば、ハンディタイプの創作物であれば十分な画像サイズだと思います。

感圧タッチパネルの解像度は縦横比が違いますが4K x 4Kです。
付属のタッチペンを使ってタッチ後不動でも±30位のばらつきがあるので、おおざっぱな座標読み取りか、平均化処理をするなど工夫が必要です。
タッチパネルの少しシートには少したわみも気になります
(複数所有していますが、どれも同じくらいです)

小型サイズLCD ST7735はこちらで紹介しています。

準備

ライブラリ

LCDを制御するためのライブラリにはAdafruit製ライブラリを使用します。

ライブラリ名用途検索確認時バージョン
Adafruit GFX Library by AdafruitグラフィックGFX1.11.3
Adafruit ILI9341 by AdafruitグラフィックILI93411.5.12
XPT2046_Touchscreen by Paul StoffregenタッチパネルXPT20461.4
TJpg_Decoder by Bodmerjpg表示jpg1.0.8

Adafruit ILI9341 by Adafruitライブラリをインストールすると、依存ライブラリが不足している場合不足している依存関係のインストールの問い合わせがあります。
「すべてをインストール」を選択してください。

使い方(LCD表示)

サンプルのRP2040系の配線は、Raspberry Pi pico および互換基板による配線を行います。

<互換基板>
Raspberry Pi pico

LCD表示

説明

サンプルは、LCDの情報(固定文字列)を表示します。
RP2040系のSPIの配線と設定方法、ESP32系のSPIの配線と設定方法について記述します。

RP2040系でSPI1を使用する場合SPI1の記述のコメントを解除し、SPI0をコメント化してください。

そのほかの描画サンプルについては
スケッチ例 > Adafruit ILI9341 > graphictest を参照してください。

配線(RP2040系 Raspberry Pi Pico SPI0)

SPI0を使う場合のサンプルです。
サンプルはRaspberry Pi Pico互換を使用した場合ですので、別のRP2040基板を使用する場合はマイコン基板のピンアサイン表を参照して配線してください。

Raspberry Pi pico配線ILI9341
3.3VVCC
3.3VLED
GNDGND
GP17(SPI0 CS)CS
GP22Reset
GP28D/C
GP19(SPI0 TX)SDI(MOSI)
GP18(SPI0 SCK)SCK

[2023/02/26] SPI0 ピン番号変更。他のLCDサンプルと共通番号にするため。

スケッチ(RP2040系 Raspberry Pi Pico SPI0)
/**********************************************************************
【ライセンスについて】
Copyright(c) 2022 by tamanegi
Released under the MIT license
'http://tamanegi.digick.jp/about-licence/

【マイコン基板】
RP2040搭載基板で利用できます。

例 : Raspberry Pi Pico

【スケッチの説明】
ILI9341 LCDの制御をします。
SPIは SPI0とSPI1のどちらのサンプルも掲載しますが、
SPI1側はコメントアウトしますので、状況に応じてコメントを外してください。
※コメント検索 [SPI1の場合]

【ライブラリ】
例 Raspberry Pi Pico / RP2040 > Raspberry Pi Pico

【準備】
マイコン基板 <-> ILI9341
3.3V                  <-> VCC
GND                   <-> GND
GPIO17(SPI0 CS)       <-> TFT CS
GPIO22                <-> TFT Reset
GPIO28                <-> TFT AO(D/C)
GPIO19(SPI0 MOSI)     <-> TFT SDA
GPIO18(SPI0 SCK)      <-> TFT SCK
3.3V                  <-> LED

SPI1の場合
3V3               <-> VCC
GND               <-> GND
GPIO13(SPI1 CS)   <-> CS
GPIO28            <-> Reset
GPIO27            <-> AO
GPIO14(SPI1 MOSI) <-> SDA
GPIO15(SPI1 SCK)  <-> SCK

【バージョン情報】
2022/10/30 : 新規
2023/2/26 : SPI0 のピン番号変更。 他のLCDサンプルと共通番号にするため。
            サンプルで表示する文字列をLCD情報に変更。
**********************************************************************/

#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include <SPI.h>

//SPI0の場合
#define TFT_CS          17   // CS
#define TFT_RST         22   // Reset 
#define TFT_DC          28   // D/C
#define TFT_MOSI        19   // SDI(MOSI)
#define TFT_SCK         18   // SCK

//SPI1の場合
// #define TFT_CS          13  // CS
// #define TFT_RST         28  // Reset 
// #define TFT_DC          27  // D/C
// #define TFT_MOSI        15  // SDI(MOSI)
// #define TFT_SCK         14  // SCK

//SPI0の場合
Adafruit_ILI9341 tft = Adafruit_ILI9341(&SPI, TFT_DC, TFT_CS, TFT_RST);

//SPI1の場合
//Adafruit_ILI9341 tft = Adafruit_ILI9341(&SPI1, TFT_DC, TFT_CS, TFT_RST);

//ソフトウエアSPIの場合
//Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCK, TFT_RST, -1);


void setup(void) 
{
  //SPI0の場合
  SPI.setTX(TFT_MOSI);
  SPI.setSCK(TFT_SCK);

  //SPI1の場合
  // SPI1.setTX(TFT_MOSI);
  // SPI1.setSCK(TFT_SCK);
   tft.begin();
  
  tft.fillScreen(ILI9341_BLACK);               //背景の塗りつぶし

  //テキスト表示
  tft.setRotation(3);                         //画面回転
  tft.setTextSize(4);                         //サイズ

  tft.setCursor(0, 10);                       //カーソル位置                      
  tft.setTextColor(ILI9341_GREEN);             //緑
  tft.printf("TAMANEGI\n\n");

  tft.setTextSize(3);                         //サイズ
  tft.setTextColor(ILI9341_RED);               //赤
  tft.printf("2.4inch LCD\n");
  tft.setTextColor(ILI9341_YELLOW);            //黄
  tft.printf("Res=320 x 240\n");
  tft.setTextColor(ILI9341_BLUE);              //青
  tft.printf("ILI9341\n");
}

void loop()
{
}
配線(ESP32系 Banana Pi PicoW SPI)

VSPIを使う場合のサンプルです。
サンプルはRaspberry Pi Pico互換を使用した場合ですので、別のRP2040基板を使用する場合はマイコン基板のピンアサイン表を参照して配線してください。

Banana Pi picoW配線ILI9341
3.3VVCC
3.3VLED
GNDGND
GPIO2CS
GPIO7Reset
GPIO10D/C
GPIO4SDI(MOSI)
GPIO3SCK
スケッチ(ESP32系 Banana Pi PicoW SPI)
/**********************************************************************
【ライセンスについて】
Copyright(c) 2022 by tamanegi
Released under the MIT license
'http://tamanegi.digick.jp/about-licence/

【マイコン基板】
ESP32 S3 BananaPi PicoW

【スケッチの説明】
ILI9341 LCDの制御をします。

【ライブラリ】
esp32 > ESP32S3 Dev Module

Adafruit GFX Library
Adafruit ILI9486

【準備】
Raspberry Pi Pico     <-> ILI9486
3.3V                  <-> VCC
GND                   <-> GND
GPIO2                 <-> TFT CS
GPIO7                 <-> TFT Reset
GPIO10                <-> TFT AO(D/C)
GPIO4                 <-> TFT SDA
GPIO3                 <-> TFT SCK
3.3V                  <-> LED

【バージョン情報】
2023/3/10 : 新規
**********************************************************************/
#include <SPI.h>
#include <ILI9486_SPI.h>
#include <Adafruit_GFX.h>

#define TFT_CS    2
#define TFT_RST   7
#define TFT_DC    10
#define TFT_MOSI  4
#define TFT_SCK   3

#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include <SPI.h>

//SPI0の場合
Adafruit_ILI9341 tft = Adafruit_ILI9341(&SPI, TFT_DC, TFT_CS, TFT_RST);

//ソフトウエアSPIの場合
//Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCK, TFT_RST, -1);

void setup(void) 
{
  //SPIピン設定
  SPI.begin(TFT_SCK, -1, TFT_MOSI, TFT_CS); 

  tft.begin();
  
  tft.fillScreen(ILI9341_BLACK);               //背景の塗りつぶし

  //テキスト表示
  tft.setRotation(3);                         //画面回転
  tft.setTextSize(4);                         //サイズ

  tft.setCursor(0, 10);                       //カーソル位置                      
  tft.setTextColor(ILI9341_GREEN);             //緑
  tft.printf("TAMANEGI\n\n");

  tft.setTextSize(3);                         //サイズ
  tft.setTextColor(ILI9341_RED);               //赤
  tft.printf("2.4inch LCD\n");
  tft.setTextColor(ILI9341_YELLOW);            //黄
  tft.printf("Res=320 x 240\n");
  tft.setTextColor(ILI9341_BLUE);              //青
  tft.printf("ILI9341\n");
}

void loop()
{
}
結果

LCDの表示ができました。

タッチパネル

説明

ILI9341では感圧式タッチパネルがのタッチ状態と、タッチ座標を読み出して表示します。

使用したILI9341のタッチドライバはHR2046が実装されていました。

配線

画面の表示とタッチの読み取りはSPI0を共通で使用します。
タッチに使用するCSピンは画面とは別のCSピンを指定してください。

Raspberry Pi Pico配線ILI9341
3.3VVCC
3.3VLED
GNDGND
GPIO17(SPI0 CS)CS
GPIO22Reset
GPIO28D/C
GPIO19(SPI0 TX)SDI(MOSI)
GPIO18(SPI0 SCK)SCK
GPIO20T_CS
GPIO16(SPI0 MISO)T_OUT(MISO)
GPIO18(SPI0 SCK)T_CLK
GPIO19(SPI0 MOSI)T_DIN(MOSI)

[2023/02/26] SPI0 ピン番号変更。他のLCDサンプルと共通番号にするため。

スケッチ
/**********************************************************************
【ライセンスについて】
Copyright(c) 2022 by tamanegi
Released under the MIT license
'http://tamanegi.digick.jp/about-licence/

【マイコン基板】
YD-RP2040

【スケッチの説明】
ILI9341 LCDのタッチ入力座標を表示します。

SPI0を使用します。
表示、タッチの入力ともにSPI0を使用します。
タッチのCSのみ個別にピンを選択してください。(SPI0用のピンでも構いません)

【ライブラリ】
 Raspberry Pi Pico / RP2040 > Generic RP2040

Adafruit GFX Libyrary
Adafruit ILI9341
XPT2046_Touchscreen

【準備】
マイコン基板 <-> ILI9341
3V3               <-> VCC
GND               <-> GND
GPIO17(SPI0 CS)   <-> CS
GPIO22            <-> Reset
GPIO28            <-> AO
GPIO19(SPI0 MOSI) <-> SDA
GPIO18(SPI0 SCK)  <-> SCK

GPIO20            <-> T_CS
GPIO16(SPI0 MISO) <-> T_OUT(MISO)
GPIO18(SPI0 SCK)  <-> T_CLK
GPIO19(SPI0 MOSI) <-> T_DIN(MOSI)

【バージョン情報】
2023/2/10 : 新規
2023/2/26 : SPI0 のピン番号変更。 他のLCDサンプルと共通番号にするため。
**********************************************************************/

#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <XPT2046_Touchscreen.h>
#include <SPI.h>


#define COMMON_SCK  18
#define COMMON_MOSI 19
#define COMMON_MISO 16

#define TOUCH_CS 20

#define TFT_DC  28
#define TFT_CS  17
#define TFT_RST 22

XPT2046_Touchscreen ts(TOUCH_CS);
Adafruit_ILI9341 tft = Adafruit_ILI9341(&SPI, TFT_DC, TFT_CS, TFT_RST);

void setup()
{
  Serial.begin(115200);

  //ESP SPI ピン設定
  SPI.setTX(COMMON_MOSI);
  SPI.setRX(COMMON_MISO);
  SPI.setSCK(COMMON_SCK);

  //表示開始
  tft.begin();
  tft.setRotation(1);
  tft.setTextSize(2);
  tft.fillScreen(ILI9341_BLACK);

  //タッチ入力開始
  ts.begin();
  ts.setRotation(3);

}

void loop()
{
  //タッチ状態読み取り
  boolean bTouch = ts.touched();
  //画面クリア
  tft.fillRect(0, 0, 640, 100, ILI9341_BLACK);

  //タッチがあればタッチされている座標の表示
  if (bTouch == true)
  {
   TS_Point tPoint = ts.getPoint();
    tft.setTextColor(ILI9341_WHITE);
    tft.setCursor(0, 0);
    tft.printf("(x,y) = (%d, %d)\r\n", tPoint.x, tPoint.y);
    Serial.printf("(x,y) = (%d, %d)\r\n", tPoint.x, tPoint.y);
  }
  else 
  {
    //タッチがなければタッチ無表示
    tft.setTextColor(ILI9341_RED);
    tft.setCursor(0, 0);
    tft.print("No Touch");
    
  }
  delay(100);
}
結果

タッチの座標を読み出しました。
※結果の画像は、ピン配置変更前のものです。

表示(SDカードからjpgファイルの表示)

説明

SDカードリーダよりjpgファイル”test.jpg”を読み取り、LCDに表示をします。

SDカードのルートフォルダには”test.jpg”ファイルを保存してください。
jpgファイルのサイズは480 x 320で作成します。

配線

RP2040系(Raspberry Pi Pico)を使用した配線を掲載します。
SPI0をLCDとSDカードリーダ共通で使用します。

LCDのSDカードリーダも使用することができます。
電源とGND以外をシルク記載のピンに接続します。

Raspberry pi pico配線ILI9341配線TFカードリーダ
3.3VVCCVCC
GNDGNDGND
GPIO17(SPI0 CS)TFT CS
GPIO22TFT Reset
GPIO28TFT AO(D/C)
GPIO19(SPI0 MOSI)TFT SDAMOSI
GPIO18(SPI0 SCK)TFT SCKSCK
3.3VLED
GPIO21SD CS
GPIO16(SPI0 MISO)MISO
GPIO20CS
スケッチ
/**********************************************************************
【ライセンスについて】
Copyright(c) 2022 by tamanegi
Released under the MIT license
'http://tamanegi.digick.jp/about-licence/

【マイコン基板】
Raspberry Pi Pico

【スケッチの説明】
SDカードモジュールからtest.jpgを読み出しILI9341に表示します。

【ライブラリ】
Raspberry Pi Pico/RP2040 > Raspberry Pi Pico

Adafruit GFX Library
Adafruit ILI9341
TJpg_Decoder

【準備】
SDカードのルートフォルダに "test.jpg"を保存します。
jpgファイルのサイズは 320 x 240 サイズで保存します。

SPI0のMOSIとSCKは共通で使用します。
ILI9341に実装されているSDカードリーダ以外のモジュールを使用する場合、電源とGNDを配線します。

<<TFT側>>
Raspberry Pi Pico     <-> ILI9341
3.3V                  <-> VCC
GND                   <-> GND
GPIO17(SPI0 CS)       <-> TFT CS
GPIO22                <-> TFT Reset
GPIO28                <-> TFT AO(D/C)
GPIO19(SPI0 MOSI)     <-> TFT SDA
GPIO18(SPI0 SCK)      <-> TFT SCK
3.3V                  <-> LED

<<SDカードリーダ側>>
Raspberry Pi Pico     <-> TFカードリーダ
3.3V                  <-> VCC
GND                   <-> GND
GPIO21                <-> SD CS
GPIO19(SPI0 MOSI)     <-> SD MOSI
GPIO16(SPI0 MISO)     <-> SD MISO
GPIO18(SPI0 SCK)      <-> SD SCK

【バージョン情報】
2023/2/26 : 新規
**********************************************************************/

#include <SPI.h>
#include <SD.h>

#include <TJpg_Decoder.h>
#include <Adafruit_ILI9341.h>
#include <Adafruit_GFX.h>

//TFT SD共通ピン設定
#define COMMON_MOSI   19
#define COMMON_SCK    18

//TFTピン設定
#define TFT_CS        17
#define TFT_RST       22
#define TFT_DC        28

//SDピン設定
#define SD_CS         21  
#define SD_MISO       16
#define FILENAME      "/test.jpg"

#define BLACK   0x0000      //パレット 黒
#define WHITE   0xFFFF      //パレット 白

// JPGの最大サイズ(バッファを静的に確保するようにしているため、決め打ち。取り扱う最大ファイルサイズで変えるようにする)
#define JPG_SIZE_MAX (100 * 1024) //MAX 100KByteを想定

Adafruit_ILI9341 tft = Adafruit_ILI9341(&SPI, TFT_DC, TFT_CS, TFT_RST);

struct jpg_file
{
  size_t size;
  uint8_t buf[JPG_SIZE_MAX];
};

jpg_file jpg;

//デコードを行うコールバック関数
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
{
  if (y >= tft.height())
    return 0;

  tft.drawRGBBitmap(x, y, bitmap, w, h);

  return 1;
}

void setup()
{
  //SPIピン設定
  SPI.setTX(COMMON_MOSI);
  SPI.setRX(SD_MISO);
  SPI.setSCK(COMMON_SCK);

  //TFTの初期化と初期設定
  tft.begin();                   //Init ILI9431初期化  
  tft.fillScreen(BLACK);        //背景の塗りつぶし
  tft.setRotation(3);

  Serial.begin(115200);
  //while(!Serial);
  delay(1000);
  
  //SDカードリーダの初期化とファイルの読み取り
  if (!SD.begin(SD_CS, SD_SCK_MHZ(8)))
  {
    Serial.println("SD initialization failed!");
    while(1);
  }

  TJpgDec.setCallback(tft_output);

  File jpgFile = SD.open(FILENAME, FILE_READ);
  if (!jpgFile)
  {
    Serial.printf("Open file failed [%s]\r\n", FILENAME);
    while(1);
  }

  jpg.size = jpgFile.size();

  if(sizeof(jpg.buf) < jpg.size) 
  {
    Serial.println("File size over");
    return;
  }

  //ファイル情報の表示
  uint16_t w = 0, h = 0;
  Serial.printf("file size = %d bytes\r\n", jpgFile.readBytes((char *)jpg.buf, jpg.size));
  TJpgDec.getJpgSize(&w, &h, jpg.buf, jpg.size);
  Serial.printf("Width = %d, height = %d\r\n", w, h);

  TJpgDec.setJpgScale(1);
  TJpgDec.drawJpg(0, 0, jpg.buf, jpg.size);             //画像の表示

  jpgFile.close();
}

void loop()
{
}
結果

SDカード内のjpgファイルの表示をしました。
組み合わせて使用することでフォトフレームなどを作ることができます。

コメント

タイトルとURLをコピーしました