サイトロゴ

ESP32でST7735 TFT LCD液晶ディスプレイを使ってみる

著者画像
Toshihiko Arai

はじめに

この記事では、ST7735チップ搭載のTFT LCD液晶ディスプレイをESP32で使う方法をまとめました。ディスプレイデバイスとはSPI接続します。ライブラリを使えばプログラミングは簡単です。ESP32だけでなく、他のArduinoでも動作します。

ゴール

こちらの動画のように、ESP32とTFT LCD、ローターリーエンコーダーを使って、数字を表示するまでをこの記事のゴールとします。このシステムはPTCヒーターを使った温度制御に組み込む予定です。

TFT LCDとESP32の配線

TFT LCDとESP32は次のとおり配線しました。BLピンは使いません。

TFT LCD ESP32 Arduino Uno 説明
BL 未接続 未接続 バックライト制御
CS 5 10 チップセレクト
DC 2 8 データ/コマンド制御
RES 4 9 リセット信号入力
SDA 23 11 シリアルデータ入力
SCL 18 13 シリアルクロック
VCC 3.3V 5V 電源
GND GND GND Ground

Arduino Unoで使う場合、3.3V端子から電圧を供給すると表示が不安定でした。ですから、5V電源に繋いでいます。TFT LCDにはレギュレーターが設置されているので、5Vの電圧でも大丈夫なようになってます。

ライブラリのインストール

ST7735搭載のTFT LCDを動かすには、最低限 Adafruit_GFX Adafruit_SPITFT Adafruit_ST77xx Adafruit_ST7735 gfxfont が必要になります。事前に下記GitHubリポジトリからインストールしてください。

私は最近、VS CodeにPlatformIOを入れてArduino開発してます。そのため下記のような構成でライブラリを配置してます。基本ライブラリマネージャーは使っておらず、ソースコードをそのままダウンロードして配置させてます。ここでは display_test.ino にサンプルプログラムを記述しました。

.
├── include
│   └── README
├── lib
│   ├── README
│   └── TFT_ST7735
│       ├── Adafruit_GFX.cpp
│       ├── Adafruit_GFX.h
│       ├── Adafruit_SPITFT.cpp
│       ├── Adafruit_SPITFT.h
│       ├── Adafruit_ST7735.cpp
│       ├── Adafruit_ST7735.h
│       ├── Adafruit_ST77xx.cpp
│       ├── Adafruit_ST77xx.h
│       ├── examples
│       │   └── test_display
│       │       └── test_display.ino
│       ├── gfxfont.h
│       └── glcdfont.c
├── platformio.ini
├── src
│   └── main.cpp
└── test
    └── README

platformio.ini には次のように記述し、test_display.inoを対象にESP32とArduino Unoの開発環境でビルドできるようにしてます。

[platformio]
src_dir = lib/TFT_ST7735/examples/test_display

[env:esp32]
platform = https://github.com/platformio/platform-espressif32.git
board = esp32dev
framework = arduino
monitor_speed = 115200
platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32#2.0.2


[env:uno]
platform = atmelavr
board = uno
framework = arduino
monitor_speed = 115200

ST7735に数字を表示してみる

前回の記事 の最後で紹介したプログラムを、ST7735版に書き換えました。詳しい説明は前回の記事をご覧ください。

ソースコード(test_display.ino)

/**
 * @date 2022-11-22
 * @author Toshihiko Arai
 * @copyright https://101010.fun
*/
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>

#if defined(ESP32)
#define TFT_RST 4  // IO4
#define TFT_DC 2   // IO2
//#define TFT_MOSI 23      // VSPID
//#define TFT_SCLK 18      // VSPICLK
//#define TFT_BACKLIGHT 7  // IO7
#define TFT_CS 5  // VSPICS0

#else
  #define TFT_CS        10
  #define TFT_RST        9
  #define TFT_DC         8
#endif

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

int number = 0;

void setup(void) {
    Serial.begin(115200);
    tft.initR(INITR_GREENTAB);
    tft.fillScreen(ST77XX_MAGENTA);
    tft.setRotation(1);
    tft.setTextWrap(false);

    tft.setTextSize(2);
    tft.setCursor(5, 5);
    tft.print("Ready...");
    delay(3000);
}

void loop() {
    char buff[4];
    sprintf(buff, "%2d", number);  // 数字を右寄せで表示させるため
    tft.setTextWrap(false);

    tft.fillScreen(ST77XX_MAGENTA);
    tft.setCursor(20, 30);
    tft.setTextColor(ST77XX_WHITE);
    tft.setTextSize(10);
    tft.println(buff);
    delay(500);

    number++;
}

こちらの動画ではESP32とArduino Unoを使って、ST7735 LCDに文字表示をしている様子がご覧いただけます。

画面のチラつき問題(フリッカー)

TFTディスプレイで文字表示を更新する際に、以前から気になっていた画面のチラつき(フリッカー)。ST7735だとだいぶ改善されてます。Arduino Unoの内部クロック周波数は16MHzだったのに対し、ESP32では標準クロック周波数が240MHzと10倍以上の性能が1つの要因でしょう。動画をみて貰えば分かりますが、ESP32の方は滑らかに数値が表示されるのに対し、Arduino Unoでは若干フリッカーが気になります。とはいえ、前回のST7789ほどではないです。こちらのLCDは128×160ドットだったのに対し、前回は240x240ドットでしたから処理速度の差も大きいのでしょう。

【発展】ロータリーエンコーダーを追加して数値を自在に操る

最初に紹介しました記事のゴールである「ロータリーエンコーダーで数値を自在に操る」ことをやってみましょう。ロータリーエンコーダーの使い方の詳しくは をご覧ください。

ESP32とTFT LCDとローターリーエンコーダーの配線

ロータリーエンコーダーは秋月電子通商さんのものを使いました。A、Bの2信号の位相差出力です。

https://akizukidenshi.com/catalog/g/gP-06358/

ロータリーエンコーダーを使うにあたって、プルアップ抵抗が必要になります。ここではESP32内蔵のプルアップ機能を使いました。ただし、ESP32のIO34~IO39はプルアップできないのでご注意ください。

ライブラリのインストール

ロータリーエンコーダーのプログラミングはかなり複雑なので、ライブラリを使うと良いです。

▼ 下記サイトより、RotaryEncoderのライブラリRotaryEncoder.hRotaryEncoder.cpp 拾ってきます。

また、Org_01.h を追加してTFT LCDに表示させる文字のフォントを変えてみました。Org_01.hAdafruit-GFX-Library に同包されてます。

.ttf フォントからC++のヘッダーファイルに変換してくれるツールも配布されてます。

私のPlatform IOのツリー構造は下記の通りです。 with_pot.ino に新しくプログラミングしていきます。

.
├── include
│   └── README
├── lib
│   ├── README
│   └── TFT_ST7735
│       ├── Adafruit_GFX.cpp
│       ├── Adafruit_GFX.h
│       ├── Adafruit_SPITFT.cpp
│       ├── Adafruit_SPITFT.h
│       ├── Adafruit_ST7735.cpp
│       ├── Adafruit_ST7735.h
│       ├── Adafruit_ST77xx.cpp
│       ├── Adafruit_ST77xx.h
│       ├── Org_01.h
│       ├── RotaryEncoder.cpp
│       ├── RotaryEncoder.h
│       ├── examples
│       │   ├── test_display
│       │   │   └── test_display.ino
│       │   └── with_pot
│       │       └── with_pot.ino
│       ├── gfxfont.h
│       └── glcdfont.c
├── platformio.ini
├── src
│   └── main.cpp
└── test
    └── README

ソースコード(with_pot.ino)

下記は、最初に紹介した動画のソースコードです。ロータリーエンコーダーを回すことで、TFTディスプレイに映される数値が変化します。ローターリーエンコーダの上限下限を設けるようにしてあります。詳しくはソースコードないの注釈をご覧ください。


/**
 * @date 2022-11-22
 * @author Toshihiko Arai
 * @copyright https://101010.fun
 */
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <RotaryEncoder.h>
#include <SPI.h>
#include <Org_01.h>

/**
 * @brief TFT LCD ST7735
 */
#define TFT_RST 4  // IO4
#define TFT_DC 2   // IO2
//#define TFT_MOSI 23      // VSPID
//#define TFT_SCLK 18      // VSPICLK
//#define TFT_BACKLIGHT 7  // IO7
#define TFT_CS 5  // VSPICS0

/**
 * @brief Rotary Encoder用のPIN
 * ESP32のIO34~IO39はプルアップしないので注意
 */
#define RE_A 25
#define RE_B 26

/**
 * @brief LimitedRotator.inoを参考に、ロタリーエンコーダーの上限、下限を設定
 * https://github.com/mathertel/RotaryEncoder/blob/master/examples/LimitedRotator/LimitedRotator.ino
*/
#define ROTARYSTEPS 1
#define ROTARYMIN 10
#define ROTARYMAX 60

/**
 * @brief ST77XX_REDなどの色がおかしいので自分で定義している
 * https://forum.arduino.cc/t/st7735-0-96-160x80-shows-wrong-colors/894996
*/
const uint16_t RED = 0x001f;
const uint16_t BLUE = 0xf800;
const uint16_t GREEN = 0x07e0;
const uint16_t YELLOW = RED | GREEN;
const uint16_t ORANGE = 0x02df;

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

RotaryEncoder encoder(RE_A, RE_B, RotaryEncoder::LatchMode::TWO03);

int lastPos = -1;

void clearDisplay() {
    tft.fillScreen(RED);
}

void setup(void) {
    Serial.begin(115200);
    tft.initR(INITR_GREENTAB);
    
    clearDisplay();
    tft.setRotation(1);
    tft.setTextWrap(false);

    tft.setFont(&Org_01);
    tft.setTextSize(2);
    tft.setCursor(20, 20);
    tft.print("Ready...");

    // pinMode(RE_A, INPUT_PULLUP); RotaryEncoderのコンストラクタでプルアップされているので必要ない
    // pinMode(RE_B, INPUT_PULLUP);

    encoder.setPosition(25 / ROTARYSTEPS);

    delay(2000);
}

void loop() {
    encoder.tick();  // just call tick() to check the state.

    int newPos = encoder.getPosition() * ROTARYSTEPS;
    
    if (newPos < ROTARYMIN) {
        encoder.setPosition(ROTARYMIN / ROTARYSTEPS);
        newPos = ROTARYMIN;

    } else if (newPos > ROTARYMAX) {
        encoder.setPosition(ROTARYMAX / ROTARYSTEPS);
        newPos = ROTARYMAX;
    }  // if

    if (lastPos != newPos) {
        lastPos = newPos;

        char buff[5];
        sprintf(buff, "%2d", newPos);  // 数字を右寄せで表示させるため
        tft.setTextWrap(false);

        clearDisplay();

        tft.setCursor(20, 15);
        tft.setTextColor(ST77XX_WHITE);
        tft.setTextSize(1);        
        tft.println("Target temperature:");

        tft.setCursor(20, 80);
        tft.setTextColor(ST77XX_WHITE);
        tft.setTextSize(10);
        tft.println(buff);

        delay(50);
    }
}

TFT LCDの色がおかしい

ソースコードのコメントにも書きましたが、ST77XX_RED などの一部の色が青色になってしまい、TFT LCDの色がおかしいバグがあります。そのため、独自で定義して色を反映させてます。

const uint16_t RED = 0x001f;
const uint16_t BLUE = 0xf800;
const uint16_t GREEN = 0x07e0;
const uint16_t YELLOW = RED | GREEN;
const uint16_t ORANGE = 0x02df;

関連記事