概要
上記の解像度800×480版の詳細記事です。
LovyanGFXの使い方
PSRAM有効化
現在PSRAMを使うことが必須です。無効な状態で実行するとリセットがかかるので注意してください。また、PSRAMを搭載しているボードなので、通常は有効にして利用したほうがよいと思います。ただしちょっと有効化が面倒です。
Arduino IDE
まず、Arduino IDEの場合にはPSRAMを「OPI PSRAM」を選択する必要があります。ESP32は8MBのPSRAMを利用しても4MBまでしか一度にアクセスすることができませんでしたが、ESP32-S3では8MB全部アクセスできるようになりました。たぶん、その影響で選択肢が増えています。
PlatformIO
[env:esp32s3box]
platform = espressif32
board = esp32s3box
framework = arduino
monitor_speed = 115200
build_flags =
-DCORE_DEBUG_LEVEL=5 ;0:None, 1:Error, 2:Warn, 3:Info, 4:Debug, 5:Verbose
lib_deps =
lovyan03/LovyanGFX
「esp32-s3-devkitc-1」はPSRAMを搭載していないボードのため、「esp32s3box」を利用します。iniファイルから指定できそうですが、現状のところ指定したらビルドができなくなってしまいました。
上記がボード定義になります。
"build": {
"arduino":{
"ldscript": "esp32s3_out.ld",
"memory_type": "qio_opi"
},
冒頭部分のmemory_typeが「qio_opi」になっていないとPSRAMが使えませんでした。
board_build.arduino.memory_type = dio_opi
上記の指定で大丈夫と書いてあるのですが、指定がないボードで指定するとビルドに失敗します。
シンプルスケッチ例
#include <Arduino.h>
#include <driver/i2c.h>
#include <LovyanGFX.hpp>
#include <lgfx_user/LGFX_ESP32S3_RGB_ESP32-8048S043.h>
LGFX display;
void setup(void)
{
display.init();
display.setTextSize(2);
display.fillScreen(TFT_BLACK);
}
uint32_t count = ~0;
void loop(void)
{
display.startWrite();
display.setRotation(++count & 7);
display.setColorDepth((count & 8) ? 16 : 24);
if (count % 8 == 0)
{
display.fillScreen(TFT_BLACK);
}
display.setTextColor(TFT_WHITE);
display.drawNumber(display.getRotation(), 16, 0);
display.setTextColor(0xFF0000U);
display.drawString("R", 30, 16);
display.setTextColor(0x00FF00U);
display.drawString("G", 40, 16);
display.setTextColor(0x0000FFU);
display.drawString("B", 50, 16);
display.drawRect(30, 30, display.width() - 60, display.height() - 60, count * 7);
display.drawFastHLine(0, 0, 10);
display.endWrite();
delay(500);
}
上記で動きます。
上記にこのボードの設定例があります。ただしタッチパネルが動きませんでした。
カスタム設定
#include <driver/i2c.h>
#include <LovyanGFX.hpp>
#include <lgfx/v1/platforms/esp32s3/Panel_RGB.hpp>
#include <lgfx/v1/platforms/esp32s3/Bus_RGB.hpp>
class LGFX : public lgfx::LGFX_Device {
public:
lgfx::Bus_RGB _bus_instance;
lgfx::Panel_RGB _panel_instance;
lgfx::Light_PWM _light_instance;
lgfx::Touch_GT911 _touch_instance;
LGFX(void) {
{
auto cfg = _panel_instance.config();
cfg.memory_width = 800;
cfg.memory_height = 480;
cfg.panel_width = 800;
cfg.panel_height = 480;
cfg.offset_x = 0;
cfg.offset_y = 0;
_panel_instance.config(cfg);
}
{
auto cfg = _panel_instance.config_detail();
cfg.use_psram = 1;
_panel_instance.config_detail(cfg);
}
{
auto cfg = _bus_instance.config();
cfg.panel = &_panel_instance;
cfg.pin_d0 = GPIO_NUM_8; // B0
cfg.pin_d1 = GPIO_NUM_3; // B1
cfg.pin_d2 = GPIO_NUM_46; // B2
cfg.pin_d3 = GPIO_NUM_9; // B3
cfg.pin_d4 = GPIO_NUM_1; // B4
cfg.pin_d5 = GPIO_NUM_5; // G0
cfg.pin_d6 = GPIO_NUM_6; // G1
cfg.pin_d7 = GPIO_NUM_7; // G2
cfg.pin_d8 = GPIO_NUM_15; // G3
cfg.pin_d9 = GPIO_NUM_16; // G4
cfg.pin_d10 = GPIO_NUM_4; // G5
cfg.pin_d11 = GPIO_NUM_45; // R0
cfg.pin_d12 = GPIO_NUM_48; // R1
cfg.pin_d13 = GPIO_NUM_47; // R2
cfg.pin_d14 = GPIO_NUM_21; // R3
cfg.pin_d15 = GPIO_NUM_14; // R4
cfg.pin_henable = GPIO_NUM_40;
cfg.pin_vsync = GPIO_NUM_41;
cfg.pin_hsync = GPIO_NUM_39;
cfg.pin_pclk = GPIO_NUM_42;
cfg.freq_write = 16000000;
cfg.hsync_polarity = 0;
cfg.hsync_front_porch = 8;
cfg.hsync_pulse_width = 4;
cfg.hsync_back_porch = 16;
cfg.vsync_polarity = 0;
cfg.vsync_front_porch = 4;
cfg.vsync_pulse_width = 4;
cfg.vsync_back_porch = 4;
cfg.pclk_idle_high = 1;
_bus_instance.config(cfg);
}
_panel_instance.setBus(&_bus_instance);
{
auto cfg = _light_instance.config();
cfg.pin_bl = GPIO_NUM_2;
_light_instance.config(cfg);
}
_panel_instance.light(&_light_instance);
{
auto cfg = _touch_instance.config();
cfg.x_min = 25;
cfg.x_max = 473;
cfg.y_min = 14;
cfg.y_max = 264;
cfg.pin_int = -1;
cfg.bus_shared = true;
cfg.offset_rotation = 0;
// I2C接続
cfg.i2c_port = I2C_NUM_1;
cfg.pin_sda = GPIO_NUM_19;
cfg.pin_scl = GPIO_NUM_20;
cfg.freq = 400000;
cfg.i2c_addr = 0x5D; // 0x5D , 0x14
_touch_instance.config(cfg);
_panel_instance.setTouch(&_touch_instance);
}
setPanel(&_panel_instance);
}
};
LGFX display;
void setup(void) {
Serial.begin(115200);
display.init();
display.setTextSize(2);
if (display.touch() && true) {
if (display.width() < display.height()) display.setRotation(display.getRotation() ^ 1);
display.setTextDatum(textdatum_t::middle_center);
display.drawString("touch the arrow marker.", display.width() >> 1, display.height() >> 1);
display.setTextDatum(textdatum_t::top_left);
std::uint16_t fg = TFT_WHITE;
std::uint16_t bg = TFT_BLACK;
if (display.isEPD()) std::swap(fg, bg);
uint16_t calibrateData[8];
display.calibrateTouch(calibrateData, fg, bg, std::max(display.width(), display.height()) >> 3);
Serial.printf("[0] x_min = %d\n", calibrateData[0]);
Serial.printf("[1] y_min = %d\n", calibrateData[1]);
Serial.printf("[2] x_min = %d\n", calibrateData[2]);
Serial.printf("[3] y_max = %d\n", calibrateData[3]);
Serial.printf("[4] x_max = %d\n", calibrateData[4]);
Serial.printf("[5] y_min = %d\n", calibrateData[5]);
Serial.printf("[6] x_max = %d\n", calibrateData[6]);
Serial.printf("[7] y_max = %d\n", calibrateData[7]);
Serial.printf("x_min = %d\n", (calibrateData[0] + calibrateData[2]) / 2);
Serial.printf("x_max = %d\n", (calibrateData[4] + calibrateData[6]) / 2);
Serial.printf("y_min = %d\n", (calibrateData[1] + calibrateData[5]) / 2);
Serial.printf("y_max = %d\n", (calibrateData[3] + calibrateData[3]) / 2);
}
display.fillScreen(TFT_BLACK);
}
uint32_t count = ~0;
void loop(void) {
display.startWrite();
display.setRotation(++count & 7);
display.setColorDepth((count & 8) ? 16 : 24);
display.setTextColor(TFT_WHITE);
display.drawNumber(display.getRotation(), 16, 0);
display.setTextColor(0xFF0000U);
display.drawString("R", 30, 16);
display.setTextColor(0x00FF00U);
display.drawString("G", 40, 16);
display.setTextColor(0x0000FFU);
display.drawString("B", 50, 16);
display.drawRect(30, 30, display.width() - 60, display.height() - 60, count * 7);
display.drawFastHLine(0, 0, 10);
int32_t x, y;
if (display.getTouch(&x, &y)) {
lgfx::touch_point_t tp;
display.getTouchRaw(&tp);
Serial.printf("raw_x = %d, raw_y = %d\n", tp.x, tp.y);
display.fillRect(x - 2, y - 2, 5, 5, count * 7);
}
display.endWrite();
}
まだなんかおかしいです。以下変更点です。
タッチパネルの通知
cfg.pin_int = -1;
上記を未使用にしています。
手元の基板では上記のR17が未実装なので、IO18に接続されていませんでした。おそらく圧電式のタッチパネルはR5でプルアップ。静電容量のタッチパネルはR5を未実装にして、R17で接続という使い分けだと思います。
私のはR5でプルアップされていて、R17が未実装なので圧電式の実装になっています。。。ただしい基板実装であればGPIO_NUM_18を指定すると、変更があった場合のみI2C経由で取得することになりますが、現状は定期的にI2Cで取得する形となります。
タッチパネルのアドレス変更
cfg.i2c_addr = 0x5D; // 0x5D , 0x14
このタッチパネルは2種類のアドレスがあって、手元のは0x5Dだったため変更しています。動かないときにはI2Cスキャナなどを利用して確認してください。
なんかへん?
タッチパネルの座標が画面を回したときにちゃんと追従できていませんね。getTouchRaw()の値は問題なかったので、offset_rotationなどの初期回転方向がずれいている可能性があります。
まとめ
設定例がありましたが、おもったより動かすのに手がかかりました。タッチパネルはもう少し調整が必要そうでした。
コメント
いつも参考にさせていただいています.
今,出たばかりのM5AtomS3Rをplatformioでプログラミングしようとしています.
platformioで,
board_build.arduino.memory_type = dio_opi
; board_build.arduino.memory_type = qio_opi
build_flags =
-DBOARD_HAS_PSRAM
-mfix-esp32-psram-cache-issue
として,
// PSRAMの初期化
if (!psramInit()) {
Serial.println(“PSRAM初期化失敗”);
} else {
Serial.println(“PSRAM初期化成功”);
}
とするとPSRAM初期化は成功します(board_build.arduino.memory_type = dio_opi がないと失敗します)
しかし,
// void* psram_buffer = heap_caps_malloc(1024 * 1024, MALLOC_CAP_SPIRAM);
としてメモリを確保しようとすると落ちます.
何か手順間違えているでしょうか?
プロジェクトを作成しようとした時にSPRAMが存在しない
[env:m5stack-atoms3]
platform = espressif32
board = m5stack-atoms3
で作成しているのが問題でしょうか?
こちらのコメントを見る前にツイッターでたまたま返事をした気もしますが
https://x.com/saitotetsuya/status/1838153337512034476
PIOについては上記のさいとてつやさんの「PlatformIO IDE用M5Stack定型コード環境」を参考にするおが良いと思います
ESP32のプラットフォームと、M5Unifiedなどのバージョンが最新である必要があるみたいです