ESP32 Arduino2 LovyanGFX1 M5Stack Display Module 13.2駆動
ESP32からArduinoCoreを使用してLovyanGFXを使用、M5Stack Display Module 13.2に出力する方法。
M5Stackに重ねて使うのではなく、Display Moduleを単体で動かす話です。
環境について
- | 環境 | バージョン | 概要 |
---|---|---|---|
開発環境 | Arduino | 2.2.1 | Arduino IDE使用 |
ボード定義 | esp32 | 2.0.14 | ESP32 Dev Module設定 |
ライブラリ | LovyanGFX | 1.1.12 | Display Module出力用 |
- 使用ボード:ESP32-DevKitC-32E
- Display Module購入先:Display Module 13.2 (SKU: M126)
- Display Module情報:m5-docs
注意点としては、LovyanGFX 1.1.12から倍角表示で1080p60Hzの出力が可能になりました。
一部のコードは1.1.12でないと動作しないと思われます。
Display Module接続情報
公開されている寸法図に書き入れた画像を以下に示します。
(クリックで拡大)
HDMIコネクタの位置はM5Stack社に確認済みです。
横の電源スイッチはDCジャックから電源供給する場合に使用します。
バスから5Vが供給されていれば、DCジャック、電源スイッチ共に触る必要はありません。
以下のModuleピン配置はM5Stack Basicを裏から見た図で書いています。
上の画像とは左右の列が入れ替わります。
つまり、Moduleはピンヘッダではなくピンソケットが見える側から見ている図です。
M5Stack Core2等ではピン番号が若干違いますが、Module側のピン名が決まっていないため、便宜上、Basicのピン名を使うことにします。
概要 | ESP32接続先ピン | Moduleピン | Moduleピン | ESP32接続先ピン | 概要 |
---|---|---|---|---|---|
GND | GND | GND | G35 | - | |
GND | GND | GND | G36 | - | |
GND | GND | GND | EN | - | |
MOSI/TDI | 23 (VSPI_MOSI) | G23 (MOSI) | G25 | - | FPGA (予備未使用) |
MISO/TDO | 19 (VSPI_MISO) | G19 (MISO) | G26 | - | FPGA (予備未使用) |
SCK/TCK | 18 (VSPI_SCK) | G18 (SCK) | 3.3V | 3.3V | 3.3V電源 |
- | G3 | G1 | - | ||
FPGA (予備未使用) | - | G16 | G17 | - | FPGA (予備未使用) |
I2C SDA | 21 (WIRE_SDA) | G21 | G22 | 22 (WIRE_SCL) | I2C SCL |
FPGA (予備未使用) | - | G2 | G5 | - | FPGA (予備未使用) |
FPGA (I2S_0 BCK) | (I2S音声使用時) | G12 | G13 | 5 (VSPI_SS) | CS/TMS |
FPGA (I2S_0 SDO) | (I2S音声使用時) | G15 | G0 | (I2S音声使用時) | FPGA (I2S_0 LRCK) |
- | HPWR | G34 | - | ||
- | HPWR | 5V | 5V | 5V電源 | |
- | HPWR | BAT | - |
基本的には、I2CとSPI、電源が繋がっていれば最低限、映像出力は可能です。
加えてI2Sも接続すると音が出せるという感じです。
FPGAの予備のピンがIOに出ているのは、Display Moduleの動作としては未使用で、開発時のデバッグや別のFPGAデザインを作って遊ぶときに使えるかも?程度で出していると設計者から聞いています。
プログラミングに使用するピンをSPIバスとして使用しているため、初期化時にはマイコン側からFPGAのコンフィギュレーションデータを流し込み、デザインが動き始めるとIOが通信用のSPIバスとして使用できるという動作になっています。
回路図を見ると3.3V電源のピンは単純に5V電源のスイッチをしているだけなので、バスのEN/RSTが接続されていない以上、ホストからはリセット端子として扱っても良いかもしれません。
Arduinoコード(720p 60Hz)
元々は、M5Stackに接続した際に、ライブラリ「M5GFX」にて簡単に扱えるようになっています。
しかし私は、素のESP32等で動かしたかったので、LovyanGFXで動かすべく、コードを移植しました。
以下を参考にしました。
- M5GFX/src/M5ModuleDisplay.h
- M5GFX/src/lgfx/v1/panel/Panel_M5HDMI.hpp
- LovyanGFX/src/lgfx/v1/panel/Panel_M5HDMI.hpp
作者が同じとあって、M5GFXにはLovyanGFXがそのまま内包されている形になっています。
上記コードとLovyanGFXの2_user_setting.inoを参考にコードを書いていきます。
#include <LovyanGFX.hpp>
#include "lgfx/v1/panel/Panel_M5HDMI.hpp"
class LGFX : public lgfx::LGFX_Device
{
lgfx::Panel_M5HDMI _panel_instance;
lgfx::Bus_SPI _bus_instance;
public:
LGFX(void)
{
{
auto cfg = _bus_instance.config();
cfg.spi_host = VSPI_HOST;
cfg.spi_mode = 3;
cfg.freq_write = 80000000;
cfg.freq_read = 16000000;
cfg.spi_3wire = false;
cfg.use_lock = true;
cfg.dma_channel = SPI_DMA_CH_AUTO;
cfg.pin_sclk = 18;
cfg.pin_mosi = 23;
cfg.pin_miso = 19;
_bus_instance.config(cfg);
_panel_instance.setBus(&_bus_instance);
}
{
auto cfg = _panel_instance.config_transmitter();
cfg.freq_read = 100000;
cfg.freq_write = 100000;
cfg.pin_scl = 22;
cfg.pin_sda = 21;
cfg.i2c_port = 0;
cfg.i2c_addr = 0x39;
cfg.prefix_cmd = 0x00;
cfg.prefix_data = 0x00;
cfg.prefix_len = 0;
_panel_instance.config_transmitter(cfg);
}
{
auto cfg = _panel_instance.config();
cfg.offset_rotation = 3;
cfg.pin_cs = 5;
cfg.readable = false;
cfg.bus_shared = true;
_panel_instance.config(cfg);
_panel_instance.setRotation(1);
}
{
lgfx::Panel_M5HDMI::config_resolution_t cfg_reso;
cfg_reso.logical_width = 1280;
cfg_reso.logical_height = 720;
cfg_reso.refresh_rate = 60.0f;
cfg_reso.output_width = 0;
cfg_reso.output_height = 0;
cfg_reso.scale_w = 0;
cfg_reso.scale_h = 0;
cfg_reso.pixel_clock = 74250000;
_panel_instance.config_resolution(cfg_reso);
}
setPanel(&_panel_instance);
}
};
LGFX display;
void setup(void)
{
display.init();
display.setTextSize((std::max(display.width(), display.height()) + 255) >> 8);
display.fillScreen(TFT_BLACK);
}
void loop(void)
{
display.clear(TFT_RED);
display.setTextColor(TFT_WHITE);
display.drawString("Display Test 1", 100, 100);
delay(10000);
display.clear(TFT_GREEN);
display.setTextColor(TFT_WHITE);
display.drawString("Display Test 2", 200, 200);
delay(10000);
display.clear(TFT_BLUE);
display.setTextColor(TFT_WHITE);
display.drawString("Display Test 3", 300, 300);
delay(10000);
}
M5Stackに接続されている場合は、最短距離の接続のため安定して動作しますが、ジャンパー線で伸ばした場合に、80MHzだと安定して動かない場合がありました。
途中で表示が崩れる、命令が無視されるなどの挙動です。
cfg.freq_write = 40000000;
などとして40MHz設定にするなど、周波数を下げると安定して動きます。
それ以外の要因で動作しない場合は、行頭に以下を追加して、ログを確認してみましょう。
#include "esp_log.h"
static const char *TAG = "main";
シリアルモニターに以下のようなログが表示されます。
[ 10010][I][Panel_M5HDMI.cpp:474] init(): [M5HDMI] i2c port:0 sda:21 scl:22
[ 10011][I][esp32-hal-i2c.c:75] i2cInit(): Initialising I2C Master: sda=21 scl=22 freq=100000
[ 10017][I][Panel_M5HDMI.cpp:481] init(): [M5HDMI] Chip ID: 17 02 e2
[ 10020][I][Panel_M5HDMI.cpp:487] init(): [M5HDMI] Resetting HDMI transmitter...
[ 10034][I][Panel_M5HDMI.cpp:468] _read_fpga_id(): [M5HDMI] FPGA ID:ffffffff
[ 10035][I][Panel_M5HDMI.cpp:125] LOAD_FPGA(): [M5HDMI] Erase FPGA SRAM...
[ 10049][I][Panel_M5HDMI.cpp:141] LOAD_FPGA(): [M5HDMI] Starting Writing to SRAM...
[ 10513][I][Panel_M5HDMI.cpp:185] LOAD_FPGA(): [M5HDMI] SRAM Prog Finish...
[ 10513][I][Panel_M5HDMI.cpp:500] init(): [M5HDMI] Waiting the FPGA gets idle...
[ 12162][I][Panel_M5HDMI.cpp:468] _read_fpga_id(): [M5HDMI] FPGA ID:48440004
[ 12163][D][Panel_M5HDMI.cpp:684] _init_resolution(): [M5HDMI] PLL feedback_div:1 input_div:1 output_div:8 OUTPUT_CLOCK:74250000
[ 12170][D][Panel_M5HDMI.cpp:685] _init_resolution(): [M5HDMI] logical resolution: w:1280 h:720
[ 12178][D][Panel_M5HDMI.cpp:686] _init_resolution(): [M5HDMI] scaling resolution: w:1280 h:720
[ 12186][D][Panel_M5HDMI.cpp:687] _init_resolution(): [M5HDMI] output resolution: w:1280 h:720
[ 12195][D][Panel_M5HDMI.cpp:688] _init_resolution(): [M5HDMI] video timing(Hori) total:1650 active:1280 frontporch:176 sync:25 backporch:169
[ 12207][D][Panel_M5HDMI.cpp:689] _init_resolution(): [M5HDMI] video timing(Vert) total:750 active:720 frontporch:14 sync:1 backporch:15
[ 12219][I][Panel_M5HDMI.cpp:550] init(): [M5HDMI] Initialize HDMI transmitter...
[ 12256][I][Panel_M5HDMI.cpp:557] init(): [M5HDMI] done.
(行頭の通り、InfoとDebugメッセージなので、ツールの中にあるCore Debug Levelの設定をDebugやVerboseにしないとメッセージは表示されません)
HDMIプロトコルアナライザでのタイミングレポートです。
ディスプレイが受けられる解像度について
CTA-861(旧CEA-861)で決まっています。
ディスプレイで一般に用いられるフルHD解像度(1920 * 1080 [1080p])では、リフレッシュレート(V Freq)が60Hzなのが一般的です。
しかし、その場合、ピクセルクロックが148.5MHzとなりFPGA等で出力するのにもそこそこ負担になります。
そのため、HD解像度(1280 * 720 [720p])、ピクセルクロック74.25MHzでの出力を使うことが多いです。
リフレッシュレートがそこまで必要ないかわりに、解像度がほしい場合、あまり一般的ではないのですが、ピクセルクロック74.25MHzのまま1080pを出力する仕様が定められています。
CTA-861の表を参考にすると、1080pで24Hzと25Hzの仕様が決まっているようです。
しかしながら、実際に受けられるディスプレイは少数です。
業務用の映像機器であると、24Hz/25Hzは受けられることが多いのですが、一般によく使われるPC向けディスプレイですと、1080p 60Hzしか受けられないということが多いです。
各種ディスプレイでテストした結果を以下に置いておきます。
Arduinoコード(1080p 24Hz)
上記コードからの変更点のみ抜粋
{
lgfx::Panel_M5HDMI::config_resolution_t cfg_reso;
cfg_reso.logical_width = 1920;
cfg_reso.logical_height = 1080;
cfg_reso.refresh_rate = 24.0f;
cfg_reso.output_width = 0;
cfg_reso.output_height = 0;
cfg_reso.scale_w = 0;
cfg_reso.scale_h = 0;
cfg_reso.pixel_clock = 74250000;
_panel_instance.config_resolution(cfg_reso);
}
デバッグメッセージ
[ 10010][I][Panel_M5HDMI.cpp:474] init(): [M5HDMI] i2c port:0 sda:21 scl:22
[ 10011][I][esp32-hal-i2c.c:75] i2cInit(): Initialising I2C Master: sda=21 scl=22 freq=100000
[ 10017][I][Panel_M5HDMI.cpp:481] init(): [M5HDMI] Chip ID: 17 02 e2
[ 10020][I][Panel_M5HDMI.cpp:487] init(): [M5HDMI] Resetting HDMI transmitter...
[ 10034][I][Panel_M5HDMI.cpp:468] _read_fpga_id(): [M5HDMI] FPGA ID:48440004
[ 10035][I][Panel_M5HDMI.cpp:468] _read_fpga_id(): [M5HDMI] FPGA ID:48440004
[ 10041][D][Panel_M5HDMI.cpp:684] _init_resolution(): [M5HDMI] PLL feedback_div:1 input_div:1 output_div:8 OUTPUT_CLOCK:74250000
[ 10053][D][Panel_M5HDMI.cpp:685] _init_resolution(): [M5HDMI] logical resolution: w:1920 h:1080
[ 10061][D][Panel_M5HDMI.cpp:686] _init_resolution(): [M5HDMI] scaling resolution: w:1920 h:1080
[ 10070][D][Panel_M5HDMI.cpp:687] _init_resolution(): [M5HDMI] output resolution: w:1920 h:1080
[ 10078][D][Panel_M5HDMI.cpp:688] _init_resolution(): [M5HDMI] video timing(Hori) total:2750 active:1920 frontporch:410 sync:27 backporch:393
[ 10090][D][Panel_M5HDMI.cpp:689] _init_resolution(): [M5HDMI] video timing(Vert) total:1125 active:1080 frontporch:22 sync:1 backporch:22
[ 10103][I][Panel_M5HDMI.cpp:550] init(): [M5HDMI] Initialize HDMI transmitter...
[ 10139][I][Panel_M5HDMI.cpp:557] init(): [M5HDMI] done.
HDMIプロトコルアナライザでのタイミングレポートです。
Arduinoコード(1080p 25Hz)
上記コードからの変更点のみ抜粋
{
lgfx::Panel_M5HDMI::config_resolution_t cfg_reso;
cfg_reso.logical_width = 1920;
cfg_reso.logical_height = 1080;
cfg_reso.refresh_rate = 25.0f;
cfg_reso.output_width = 0;
cfg_reso.output_height = 0;
cfg_reso.scale_w = 0;
cfg_reso.scale_h = 0;
cfg_reso.pixel_clock = 74250000;
_panel_instance.config_resolution(cfg_reso);
}
デバッグメッセージ
[ 10010][I][Panel_M5HDMI.cpp:474] init(): [M5HDMI] i2c port:0 sda:21 scl:22
[ 10011][I][esp32-hal-i2c.c:75] i2cInit(): Initialising I2C Master: sda=21 scl=22 freq=100000
[ 10017][I][Panel_M5HDMI.cpp:481] init(): [M5HDMI] Chip ID: 17 02 e2
[ 10020][I][Panel_M5HDMI.cpp:487] init(): [M5HDMI] Resetting HDMI transmitter...
[ 10035][I][Panel_M5HDMI.cpp:468] _read_fpga_id(): [M5HDMI] FPGA ID:48440004
[ 10035][I][Panel_M5HDMI.cpp:468] _read_fpga_id(): [M5HDMI] FPGA ID:48440004
[ 10041][D][Panel_M5HDMI.cpp:684] _init_resolution(): [M5HDMI] PLL feedback_div:1 input_div:1 output_div:8 OUTPUT_CLOCK:74250000
[ 10053][D][Panel_M5HDMI.cpp:685] _init_resolution(): [M5HDMI] logical resolution: w:1920 h:1080
[ 10061][D][Panel_M5HDMI.cpp:686] _init_resolution(): [M5HDMI] scaling resolution: w:1920 h:1080
[ 10070][D][Panel_M5HDMI.cpp:687] _init_resolution(): [M5HDMI] output resolution: w:1920 h:1080
[ 10078][D][Panel_M5HDMI.cpp:688] _init_resolution(): [M5HDMI] video timing(Hori) total:2700 active:1920 frontporch:385 sync:27 backporch:368
[ 10091][D][Panel_M5HDMI.cpp:689] _init_resolution(): [M5HDMI] video timing(Vert) total:1100 active:1080 frontporch:9 sync:1 backporch:10
[ 10103][I][Panel_M5HDMI.cpp:550] init(): [M5HDMI] Initialize HDMI transmitter...
[ 10139][I][Panel_M5HDMI.cpp:557] init(): [M5HDMI] done.
HDMIプロトコルアナライザでのタイミングレポートです。
Arduinoコード(1080p 60Hz)
上記コードからの変更点のみ抜粋
※LovyanGFX 1.1.12から実装された機能です。
960 * 540として転送することによってFPGA側で倍の解像度(1920 * 1080)で表示します。
設定されたピクセルクロック、解像度をライブラリ側で見ていて、解像度を倍にする処理は自動で入るとのことです。
{
lgfx::Panel_M5HDMI::config_resolution_t cfg_reso;
cfg_reso.logical_width = 960;
cfg_reso.logical_height = 540;
cfg_reso.refresh_rate = 60.0f;
cfg_reso.output_width = 1920;
cfg_reso.output_height = 1080;
cfg_reso.scale_w = 0;
cfg_reso.scale_h = 0;
cfg_reso.pixel_clock = 148500000;
_panel_instance.config_resolution(cfg_reso);
}
デバッグメッセージ
[ 10010][I][Panel_M5HDMI.cpp:474] init(): [M5HDMI] i2c port:0 sda:21 scl:22
[ 10011][I][esp32-hal-i2c.c:75] i2cInit(): Initialising I2C Master: sda=21 scl=22 freq=100000
[ 10017][I][Panel_M5HDMI.cpp:481] init(): [M5HDMI] Chip ID: 17 02 e2
[ 10020][I][Panel_M5HDMI.cpp:487] init(): [M5HDMI] Resetting HDMI transmitter...
[ 10035][I][Panel_M5HDMI.cpp:468] _read_fpga_id(): [M5HDMI] FPGA ID:48440004
[ 10035][I][Panel_M5HDMI.cpp:468] _read_fpga_id(): [M5HDMI] FPGA ID:48440004
[ 10041][D][Panel_M5HDMI.cpp:684] _init_resolution(): [M5HDMI] PLL feedback_div:2 input_div:1 output_div:4 OUTPUT_CLOCK:148500000
[ 10053][D][Panel_M5HDMI.cpp:685] _init_resolution(): [M5HDMI] logical resolution: w:960 h:540
[ 10061][D][Panel_M5HDMI.cpp:686] _init_resolution(): [M5HDMI] scaling resolution: w:960 h:1080
[ 10069][D][Panel_M5HDMI.cpp:687] _init_resolution(): [M5HDMI] output resolution: w:960 h:1080
[ 10078][D][Panel_M5HDMI.cpp:688] _init_resolution(): [M5HDMI] video timing(Hori) total:1125 active:960 frontporch:72 sync:24 backporch:69
[ 10090][D][Panel_M5HDMI.cpp:689] _init_resolution(): [M5HDMI] video timing(Vert) total:1100 active:1080 frontporch:9 sync:1 backporch:10
[ 10102][I][Panel_M5HDMI.cpp:550] init(): [M5HDMI] Initialize HDMI transmitter...
[ 10139][I][Panel_M5HDMI.cpp:557] init(): [M5HDMI] done.
現状、デバッグメッセージは少しバグり気味っぽいです。
水平方向が拡大されていないことになっています。
HDMIプロトコルアナライザでのタイミングレポートです。
Display Module接続情報(2画面接続テスト)
モジュール1
概要 | ESP32接続先ピン | Moduleピン | Moduleピン | ESP32接続先ピン | 概要 |
---|---|---|---|---|---|
GND | GND | GND | G35 | - | |
GND | GND | GND | G36 | - | |
GND | GND | GND | EN | - | |
MOSI/TDI | 23 (VSPI_MOSI) | G23 (MOSI) | G25 | - | FPGA (予備未使用) |
MISO/TDO | 19 (VSPI_MISO) | G19 (MISO) | G26 | - | FPGA (予備未使用) |
SCK/TCK | 18 (VSPI_SCK) | G18 (SCK) | 3.3V | 3.3V | 3.3V電源 |
- | G3 | G1 | - | ||
FPGA (予備未使用) | - | G16 | G17 | - | FPGA (予備未使用) |
I2C SDA | 21 (WIRE_SDA) | G21 | G22 | 22 (WIRE_SCL) | I2C SCL |
FPGA (予備未使用) | - | G2 | G5 | - | FPGA (予備未使用) |
FPGA (I2S_0 BCK) | (I2S音声使用時) | G12 | G13 | 5 (VSPI_SS) | CS/TMS |
FPGA (I2S_0 SDO) | (I2S音声使用時) | G15 | G0 | (I2S音声使用時) | FPGA (I2S_0 LRCK) |
- | HPWR | G34 | - | ||
- | HPWR | 5V | 5V | 5V電源 | |
- | HPWR | BAT | - |
モジュール2
概要 | ESP32接続先ピン | Moduleピン | Moduleピン | ESP32接続先ピン | 概要 |
---|---|---|---|---|---|
GND | GND | GND | G35 | - | |
GND | GND | GND | G36 | - | |
GND | GND | GND | EN | - | |
MOSI/TDI | 23 (VSPI_MOSI) | G23 (MOSI) | G25 | - | FPGA (予備未使用) |
MISO/TDO | 19 (VSPI_MISO) | G19 (MISO) | G26 | - | FPGA (予備未使用) |
SCK/TCK | 18 (VSPI_SCK) | G18 (SCK) | 3.3V | 3.3V | 3.3V電源 |
- | G3 | G1 | - | ||
FPGA (予備未使用) | - | G16 | G17 | - | FPGA (予備未使用) |
I2C SDA | 16 | G21 | G22 | 17 | I2C SCL |
FPGA (予備未使用) | - | G2 | G5 | - | FPGA (予備未使用) |
FPGA (I2S_0 BCK) | (I2S音声使用時) | G12 | G13 | 4 | CS/TMS |
FPGA (I2S_0 SDO) | (I2S音声使用時) | G15 | G0 | (I2S音声使用時) | FPGA (I2S_0 LRCK) |
- | HPWR | G34 | - | ||
- | HPWR | 5V | 5V | 5V電源 | |
- | HPWR | BAT | - |
SPIバスは共通に接続し、SSとI2Cバスは別々に接続しました。
Arduinoコード(720p 60Hz 2画面接続テスト)
#include <LovyanGFX.hpp>
#include "lgfx/v1/panel/Panel_M5HDMI.hpp"
class LGFX1 : public lgfx::LGFX_Device
{
lgfx::Panel_M5HDMI _panel_instance;
lgfx::Bus_SPI _bus_instance;
public:
LGFX1(void)
{
{
auto cfg = _bus_instance.config();
cfg.spi_host = VSPI_HOST;
cfg.spi_mode = 3;
cfg.freq_write = 80000000;
cfg.freq_read = 16000000;
cfg.spi_3wire = false;
cfg.use_lock = true;
cfg.dma_channel = SPI_DMA_CH_AUTO;
cfg.pin_sclk = 18;
cfg.pin_mosi = 23;
cfg.pin_miso = 19;
_bus_instance.config(cfg);
_panel_instance.setBus(&_bus_instance);
}
{
auto cfg = _panel_instance.config_transmitter();
cfg.freq_read = 100000;
cfg.freq_write = 100000;
cfg.pin_scl = 22;
cfg.pin_sda = 21;
cfg.i2c_port = 0;
cfg.i2c_addr = 0x39;
cfg.prefix_cmd = 0x00;
cfg.prefix_data = 0x00;
cfg.prefix_len = 0;
_panel_instance.config_transmitter(cfg);
}
{
auto cfg = _panel_instance.config();
cfg.offset_rotation = 3;
cfg.pin_cs = 5;
cfg.readable = false;
cfg.bus_shared = true;
_panel_instance.config(cfg);
_panel_instance.setRotation(1);
}
{
lgfx::Panel_M5HDMI::config_resolution_t cfg_reso;
cfg_reso.logical_width = 1280;
cfg_reso.logical_height = 720;
cfg_reso.refresh_rate = 60.0f;
cfg_reso.output_width = 0;
cfg_reso.output_height = 0;
cfg_reso.scale_w = 0;
cfg_reso.scale_h = 0;
cfg_reso.pixel_clock = 74250000;
_panel_instance.config_resolution(cfg_reso);
}
setPanel(&_panel_instance);
}
};
class LGFX2 : public lgfx::LGFX_Device
{
lgfx::Panel_M5HDMI _panel_instance;
lgfx::Bus_SPI _bus_instance;
public:
LGFX2(void)
{
{
auto cfg = _bus_instance.config();
cfg.spi_host = VSPI_HOST;
cfg.spi_mode = 3;
cfg.freq_write = 80000000;
cfg.freq_read = 16000000;
cfg.spi_3wire = false;
cfg.use_lock = true;
cfg.dma_channel = SPI_DMA_CH_AUTO;
cfg.pin_sclk = 18;
cfg.pin_mosi = 23;
cfg.pin_miso = 19;
_bus_instance.config(cfg);
_panel_instance.setBus(&_bus_instance);
}
{
auto cfg = _panel_instance.config_transmitter();
cfg.freq_read = 100000;
cfg.freq_write = 100000;
cfg.pin_scl = 17;
cfg.pin_sda = 16;
cfg.i2c_port = 1;
cfg.i2c_addr = 0x39;
cfg.prefix_cmd = 0x00;
cfg.prefix_data = 0x00;
cfg.prefix_len = 0;
_panel_instance.config_transmitter(cfg);
}
{
auto cfg = _panel_instance.config();
cfg.offset_rotation = 3;
cfg.pin_cs = 4;
cfg.readable = false;
cfg.bus_shared = true;
_panel_instance.config(cfg);
_panel_instance.setRotation(1);
}
{
lgfx::Panel_M5HDMI::config_resolution_t cfg_reso;
cfg_reso.logical_width = 1280;
cfg_reso.logical_height = 720;
cfg_reso.refresh_rate = 60.0f;
cfg_reso.output_width = 0;
cfg_reso.output_height = 0;
cfg_reso.scale_w = 0;
cfg_reso.scale_h = 0;
cfg_reso.pixel_clock = 74250000;
_panel_instance.config_resolution(cfg_reso);
}
setPanel(&_panel_instance);
}
};
LGFX1 display1;
LGFX2 display2;
void setup(void)
{
display1.init();
display1.setTextSize((std::max(display1.width(), display1.height()) + 255) >> 8);
display1.fillScreen(TFT_BLACK);
display2.init();
display2.setTextSize((std::max(display2.width(), display2.height()) + 255) >> 8);
display2.fillScreen(TFT_RED);
}
void loop(void)
{
display1.clear(TFT_RED);
display1.setTextColor(TFT_WHITE);
display1.drawString("Display1 Test 1", 100, 100);
display2.clear(TFT_BLUE);
display2.setTextColor(TFT_WHITE);
display2.drawString("Display2 Test 1", 100, 100);
delay(10000);
display1.clear(TFT_GREEN);
display1.setTextColor(TFT_WHITE);
display1.drawString("Display1 Test 2", 200, 200);
display2.clear(TFT_RED);
display2.setTextColor(TFT_WHITE);
display2.drawString("Display2 Test 2", 200, 200);
delay(10000);
display1.clear(TFT_BLUE);
display1.setTextColor(TFT_WHITE);
display1.drawString("Display1 Test 3", 300, 300);
display2.clear(TFT_GREEN);
display2.setTextColor(TFT_WHITE);
display2.drawString("Display2 Test 3", 300, 300);
delay(10000);
}
デバッグメッセージ
[ 10010][I][Panel_M5HDMI.cpp:474] init(): [M5HDMI] i2c port:0 sda:21 scl:22
[ 10011][I][esp32-hal-i2c.c:75] i2cInit(): Initialising I2C Master: sda=21 scl=22 freq=100000
[ 10017][I][Panel_M5HDMI.cpp:481] init(): [M5HDMI] Chip ID: 17 02 e2
[ 10020][I][Panel_M5HDMI.cpp:487] init(): [M5HDMI] Resetting HDMI transmitter...
[ 10034][I][Panel_M5HDMI.cpp:468] _read_fpga_id(): [M5HDMI] FPGA ID:ffffffff
[ 10035][I][Panel_M5HDMI.cpp:125] LOAD_FPGA(): [M5HDMI] Erase FPGA SRAM...
[ 10049][I][Panel_M5HDMI.cpp:141] LOAD_FPGA(): [M5HDMI] Starting Writing to SRAM...
[ 10513][I][Panel_M5HDMI.cpp:185] LOAD_FPGA(): [M5HDMI] SRAM Prog Finish...
[ 10513][I][Panel_M5HDMI.cpp:500] init(): [M5HDMI] Waiting the FPGA gets idle...
[ 12162][I][Panel_M5HDMI.cpp:468] _read_fpga_id(): [M5HDMI] FPGA ID:48440004
[ 12163][D][Panel_M5HDMI.cpp:684] _init_resolution(): [M5HDMI] PLL feedback_div:1 input_div:1 output_div:8 OUTPUT_CLOCK:74250000
[ 12170][D][Panel_M5HDMI.cpp:685] _init_resolution(): [M5HDMI] logical resolution: w:1280 h:720
[ 12178][D][Panel_M5HDMI.cpp:686] _init_resolution(): [M5HDMI] scaling resolution: w:1280 h:720
[ 12186][D][Panel_M5HDMI.cpp:687] _init_resolution(): [M5HDMI] output resolution: w:1280 h:720
[ 12195][D][Panel_M5HDMI.cpp:688] _init_resolution(): [M5HDMI] video timing(Hori) total:1650 active:1280 frontporch:176 sync:25 backporch:169
[ 12207][D][Panel_M5HDMI.cpp:689] _init_resolution(): [M5HDMI] video timing(Vert) total:750 active:720 frontporch:14 sync:1 backporch:15
[ 12219][I][Panel_M5HDMI.cpp:550] init(): [M5HDMI] Initialize HDMI transmitter...
[ 12256][I][Panel_M5HDMI.cpp:557] init(): [M5HDMI] done.
[ 12256][I][Panel_M5HDMI.cpp:474] init(): [M5HDMI] i2c port:1 sda:16 scl:17
[ 12257][I][esp32-hal-i2c.c:75] i2cInit(): Initialising I2C Master: sda=16 scl=17 freq=100000
[ 12268][I][Panel_M5HDMI.cpp:481] init(): [M5HDMI] Chip ID: 17 02 e2
[ 12271][I][Panel_M5HDMI.cpp:487] init(): [M5HDMI] Resetting HDMI transmitter...
[ 12285][I][Panel_M5HDMI.cpp:468] _read_fpga_id(): [M5HDMI] FPGA ID:ffffffff
[ 12285][I][Panel_M5HDMI.cpp:125] LOAD_FPGA(): [M5HDMI] Erase FPGA SRAM...
[ 12299][I][Panel_M5HDMI.cpp:141] LOAD_FPGA(): [M5HDMI] Starting Writing to SRAM...
[ 12763][I][Panel_M5HDMI.cpp:185] LOAD_FPGA(): [M5HDMI] SRAM Prog Finish...
[ 12763][I][Panel_M5HDMI.cpp:500] init(): [M5HDMI] Waiting the FPGA gets idle...
[ 14412][I][Panel_M5HDMI.cpp:468] _read_fpga_id(): [M5HDMI] FPGA ID:48440004
[ 14413][D][Panel_M5HDMI.cpp:684] _init_resolution(): [M5HDMI] PLL feedback_div:1 input_div:1 output_div:8 OUTPUT_CLOCK:74250000
[ 14420][D][Panel_M5HDMI.cpp:685] _init_resolution(): [M5HDMI] logical resolution: w:1280 h:720
[ 14428][D][Panel_M5HDMI.cpp:686] _init_resolution(): [M5HDMI] scaling resolution: w:1280 h:720
[ 14436][D][Panel_M5HDMI.cpp:687] _init_resolution(): [M5HDMI] output resolution: w:1280 h:720
[ 14445][D][Panel_M5HDMI.cpp:688] _init_resolution(): [M5HDMI] video timing(Hori) total:1650 active:1280 frontporch:176 sync:25 backporch:169
[ 14457][D][Panel_M5HDMI.cpp:689] _init_resolution(): [M5HDMI] video timing(Vert) total:750 active:720 frontporch:14 sync:1 backporch:15
[ 14469][I][Panel_M5HDMI.cpp:550] init(): [M5HDMI] Initialize HDMI transmitter...
[ 14506][I][Panel_M5HDMI.cpp:557] init(): [M5HDMI] done.
初期化時にHDMIトランスミッタとI2Cで通信しているのですが、アドレスが同一のために2個取り付けると干渉します。
ESP32ではI2Cペリフェラルを2つ持っているため、今回はI2Cを2つ用いることで解決しました。
その他、I2Cバスマルチプレクサを使用して切り替えて使用するなどでも良いと思います。