概要
前回は描画速度について説明をしましたが、今回は内蔵LCDではなく外部LCDの接続方法をとりあげたいと思います。
利用機材
- M5StickC
- ST7735S
上記の機材を利用して接続させました。
昔に他のライブラリで利用したことがある組み合わせになります。実は接続の表とスケッチの定義が間違えていて、上記ブログのままだと動かなかったというのを発見しました。。。
配線
ST7735 | M5StickC | 内容 |
GND | GND | 共通GND |
VCC | 3V3 | 電源 |
SCL | 32 | SCK |
SDA | 26 | MOSI |
RES | 33 | リセット |
DC | 0 | MISO |
CS | GND | このデバイスを選択 |
BLK | 未接続 | バックライト制御 |
ピンは入れ替えても動くと思いますが、前回のブログにあったスケッチ例と同じピンアサインにしました。
今回はこんな感じに配線されました。Grove側のピンも利用しているので、ケーブルを持っていないと配線しにくいと思います。ケーブルが短いほうが見栄えはいいですね。
スケッチ例
#define LGFX_AUTODETECT
#include <LovyanGFX.hpp>
// SPI設定用の構造体を作成します。
struct LGFX_Config {
// 使用するSPIを VSPI_HOST または HSPI_HOST で設定します。
static constexpr spi_host_device_t spi_host = HSPI_HOST;
// 使用するDMAチャンネルを 1か2で設定します。
// 使用しない場合は省略するか0を設定します。
static constexpr int dma_channel = 2;
// SPIのSCLKのピン番号を設定します。
static constexpr int spi_sclk = 32;
// SPIのMOSIのピン番号を設定します。
static constexpr int spi_mosi = 26;
// SPIのMISOのピン番号を設定します。
// SDカード等と共通のSPIバスを使う場合はMISOも必ず設定してください。
// 使わない場合は省略するか-1を設定します。
static constexpr int spi_miso = -1;
// SPI通信のデータ長を指定します。
// RaspberryPi用のLCD等を使用する場合に16を指定します。
// 省略時は 8 です。大抵のパネルは8ですので、基本的には省略してください。
static constexpr int spi_dlen = 8;
};
// 用意した設定用の構造体を、LGFX_SPIクラスにテンプレート引数として設定し、インスタンスを作成します。
static lgfx::LGFX_SPI<LGFX_Config> lcd;
// Panelクラスのインスタンスを作成します。
static lgfx::Panel_ST7735S panel;
// Autodetect用クラス
static LGFX lcdIn;
void setup(void) {
// パネルクラスに各種設定値を代入していきます。
// 通常動作時のSPIクロックを設定します。
// ESP32のSPIは80MHzを整数で割った値のみ使用可能です。
// 設定した値に一番近い設定可能な値が使用されます。
panel.freq_write = 20000000;
// 単色の塗り潰し処理時のSPIクロックを設定します。
// 基本的にはfreq_writeと同じ値を設定しますが、
// より高い値を設定しても動作する場合があります。
panel.freq_fill = 27000000;
// LCDから画素データを読取る際のSPIクロックを設定します。
panel.freq_read = 16000000;
// SPI通信モードを0~3から設定します。
panel.spi_mode = 0;
// データ読み取り時のSPI通信モードを0~3から設定します。
panel.spi_mode_read = 0;
// 画素読出し時のダミービット数を設定します。
// 画素読出しでビットずれが起きる場合に調整してください。
panel.len_dummy_read_pixel = 8;
// データの読取りの可否を設定します。読取り不可の場合はfalseを設定します。
// ※ CSピンのないST7789等はfalseにしてください。
// 省略時はtrueになります。
panel.spi_read = true;
// データの読取りMOSIピンで行うパネルの場合はtrueを設定します。
// 省略時はfalseになります。
panel.spi_3wire = false;
// LCDのCSを接続したピン番号を設定します。
// 使わない場合は省略するか-1を設定します。
panel.spi_cs = -1;
// LCDのD/Cを接続したピン番号を設定します。
panel.spi_dc = 0;
// LCDのRSTを接続したピン番号を設定します。
// 使わない場合は省略するか-1を設定します。
panel.gpio_rst = 33;
// LCDのバックライトを接続したピン番号を設定します。
// 使わない場合は省略するか-1を設定します。
panel.gpio_bl = -1;
// バックライト使用時、輝度制御に使用するPWMチャンネル番号を設定します。
// PWM輝度制御を使わない場合は省略するか-1を設定します。
panel.pwm_ch_bl = -1;
// バックライト点灯時の出力レベルがローかハイかを設定します。
// 省略時は true。true=HIGHで点灯 / false=LOWで点灯になります。
panel.backlight_level = true;
// パネルの色反転設定です。trueを設定すると色が反転します。(例:黒が白に、青が黄色に)
// 省略時は false。画面の色が反転している場合は設定を変更してください。
panel.reverse_invert = true;
// パネルの色順がを設定します。 RGB=true / BGR=false
// 省略時はfalse。赤と青が入れ替わっている場合は設定を変更してください。
panel.rgb_order = false;
// LCDドライバチップ内のメモリサイズ(幅と高さ)を設定します。
// 設定が合っていない場合、setRotationを使用した際の座標がずれます。
// (例:ST7735は 132x162 / 128x160 / 132x132 の3通りが存在します)
panel.memory_width = 132;
panel.memory_height = 162;
// パネルが実際に表示可能なピクセル数(幅と高さ)を設定します。
// 省略時はパネルクラスのデフォルト値が使用されます。
panel.panel_width = 80;
panel.panel_height = 160;
// パネルのオフセット量を設定します。
// 省略時はパネルクラスのデフォルト値が使用されます。
panel.offset_x = 26;
panel.offset_y = 1;
// setRotationの初期化直後の値を設定します。
panel.rotation = 0;
// setRotationを使用した時の向きを変更したい場合、offset_rotationを設定します。
// setRotation(0)での向きを 1の時の向きにしたい場合、 1を設定します。
panel.offset_rotation = 2;
// 設定を終えたら、lcdのsetPanel関数でパネルクラスのポインタを渡します。
lcd.setPanel(&panel);
// SPIバスとパネルの初期化を実行すると使用可能になります。
lcd.init();
// 内蔵ディスプレイ初期化
lcdIn.init();
}
uint32_t count = ~0;
void loop(void) {
// 内蔵パネルはランダム色
lcdIn.fillScreen(random(65536));
// 外付けパネルは回転しながら描画
lcd.startWrite();
lcd.fillScreen(TFT_WHITE);
lcd.setRotation(++count & 7);
lcd.setColorDepth((count & 8) ? 16 : 24);
lcd.setTextColor(random(65536));
lcd.drawNumber(lcd.getRotation(), 16, 0);
lcd.setTextColor(0xFF0000U);
lcd.drawString("R", 30, 16);
lcd.setTextColor(0x00FF00U);
lcd.drawString("G", 40, 16);
lcd.setTextColor(0x0000FFU);
lcd.drawString("B", 50, 16);
lcd.drawRect(10, 10, 20, 20, random(65536));
lcd.endWrite();
delay(500);
}
結構長いですが、ほとんどがパネル周りの設定になります。この設定が最適化はちょっと自信が無いです。
struct LGFX_Config{
static constexpr spi_host_device_t spi_host = HSPI_HOST;
static constexpr int dma_channel = 2;
static constexpr int spi_sclk = 32;
static constexpr int spi_mosi = 26;
static constexpr int spi_miso = -1;
static constexpr int spi_dlen = 8;
};
static lgfx::LGFX_SPI<LGFX_Config> lcd;
SPIバスの設定をした構造体を、LGFX_SPIクラスのテンプレートとして渡すことでlcdの初期化をしています。
static lgfx::Panel_ST7735S panel;
setup() {
panel.freq_write = 20000000;
panel.freq_fill = 27000000;
panel.freq_read = 16000000;
panel.spi_mode = 0;
panel.spi_mode_read = 0;
panel.len_dummy_read_pixel = 8;
panel.spi_read = true;
panel.spi_3wire = false;
panel.spi_cs = -1;
panel.spi_dc = 0;
panel.gpio_rst = 33;
panel.gpio_bl = -1;
panel.pwm_ch_bl = -1;
panel.backlight_level = true;
panel.reverse_invert = true;
panel.rgb_order = false;
panel.memory_width = 132;
panel.memory_height = 162;
panel.panel_width = 80;
panel.panel_height = 160;
panel.offset_x = 26;
panel.offset_y = 1;
panel.rotation = 0;
panel.offset_rotation = 2;
lcd.setPanel(&panel);
}
その後は実際に接続されているパネルクラスを宣言し、細かいパラメータを設定します。最後にlcdのsetPanel()関数でlcdとパネルを関連付けします。
細かいパラメータはどう設定するの?
ここは残念ながら頑張るしかありません、、、
各種データシートや、似たパネルの設定、他のライブラリの設定を確認して試行錯誤して調整をします。
公式スケッチ例
- https://github.com/lovyan03/LovyanGFX/blob/master/examples/HowToUse/2_spi_setting/2_spi_setting.ino
上記のHowToUseにある「2_spi_setting」が参考になります。今回のスケッチもこれを元に不要な行を削除してから作成したものです。
まとめ
内蔵LCDと外部LCDを同時に使うことはあまりないと思いますが、実現可能です。とはいえ、LCDを内蔵していないESP32ボードに、外部LCDを接続する場合の方がほとんどだと思います。その場合には同じパネルを内蔵しているボードがあれば、同じピンアサインにすることでLGFX_AUTODETECTの設定をそのまま持ってくればかんたんに動かすことができると思います。
最近はLCDを内蔵しているボードが増えてきているので、外部LCDを接続しようとすると思ったより情報がなくて苦労すると思います。またSPIの通信はケーブルがびろびろーんとなっているとノイズが乗りやすいので、通信速度を下げないと安定しない場合があったりするので注意してください。
コメント
貴重な情報ありがとうございます。大変参考になります。現在、ST7735を搭載した外部LCDで「2_spi_setting」を試しているのですが、白画面の状態で点滅を繰り返し上手く描写されずに困っています。他のライブラリでは描写されるので高速化の際に生じた問題だとは思うのですが…。何か知見があれば是非教えて欲しいです。よろしくお願いします。
んー、おそらくGPIOのピン設定あたりだと思うんですよね。。。
直値で書いてあるのでわかりにくいんですが、複数の場所でPIN番号が設定されているので、個別に見直すしかないのかな?