mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-10-09 11:16:39 +00:00
- BLE (ESP32): fast advertising (20-30 ms) for 30 s, then 152.5 ms, as the nRF52 interface; the library default kept 30-60 ms forever - Live map tiles: the WiFi drops after a minute with nothing missing - ES8311 to standby while the I2S clocks are off, set up again before the amp comes on - Buzzer: no channel (DMA out of memory) waits out the samples instead of spinning above the UI loop - OTA: the image header check waits for its 16 bytes - Comments: P10 is just the LED; the LCD SPI clock is 40 MHz on the wire Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
218 lines
6.8 KiB
C++
218 lines
6.8 KiB
C++
#pragma once
|
|
|
|
#include <helpers/ui/LGFXDisplay.h>
|
|
#include <Wire.h>
|
|
|
|
#define LGFX_USE_V1
|
|
#include <LovyanGFX.hpp>
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Wio Tracker L2 Pro display stack:
|
|
// - NV3031B 320x240 panel on quad-SPI (SCLK 42, IO0-3 = 41/40/39/38, CS 46)
|
|
// - GT911 capacitive touch on I2C 0x5D (SDA 47 / SCL 48)
|
|
// - LP5814 4-channel LED driver on I2C 0x2C used as backlight
|
|
// Panel power/reset lines are on the TCA9535 expander and are already
|
|
// sequenced by WioTrackerL2Board::begin() before this driver initializes.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// The SPI clock is 80 MHz / an integer: 75 MHz asked gives 40 MHz on the wire.
|
|
#ifndef L2_SPI_FREQUENCY
|
|
#define L2_SPI_FREQUENCY 75000000
|
|
#endif
|
|
|
|
#define LP5814_I2C_ADDR 0x2C
|
|
|
|
// LP5814 used as backlight controller (all 4 channels in parallel)
|
|
class WioTrackerL2Backlight : public lgfx::v1::ILight {
|
|
static constexpr uint8_t REG_DEVICE_CONFIG0 = 0x00;
|
|
static constexpr uint8_t REG_MAX_CURRENT = 0x01;
|
|
static constexpr uint8_t REG_ENABLE_CONTROL = 0x02;
|
|
static constexpr uint8_t REG_DIM_MODE = 0x04;
|
|
static constexpr uint8_t REG_ENGINE_MODE = 0x05;
|
|
static constexpr uint8_t REG_UPDATE = 0x0F;
|
|
static constexpr uint8_t REG_LED0_DC = 0x14;
|
|
static constexpr uint8_t REG_LED0_PWM = 0x18;
|
|
|
|
uint8_t _brightness = 153; // 60%
|
|
bool _standby = false; // chip disabled (the screen is off)
|
|
|
|
void writeReg(uint8_t reg, uint8_t value) {
|
|
Wire.beginTransmission(LP5814_I2C_ADDR);
|
|
Wire.write(reg);
|
|
Wire.write(value);
|
|
Wire.endTransmission();
|
|
}
|
|
|
|
void configure() {
|
|
writeReg(REG_DEVICE_CONFIG0, 0x01); // chip enable
|
|
writeReg(REG_MAX_CURRENT, 0x01); // 51 mA max current
|
|
writeReg(REG_ENABLE_CONTROL, 0x00); // outputs off while configuring
|
|
writeReg(REG_DIM_MODE, 0x4E);
|
|
writeReg(REG_ENGINE_MODE, 0xF0);
|
|
for (uint8_t i = 0; i < 4; i++) {
|
|
writeReg(REG_LED0_DC + i, 200);
|
|
}
|
|
writeReg(REG_ENABLE_CONTROL, 0x0F); // enable all 4 channels
|
|
writeReg(REG_UPDATE, 0x55); // latch (LP5814 requires 0x55)
|
|
delay(5);
|
|
}
|
|
|
|
public:
|
|
bool init(uint8_t brightness) override {
|
|
Wire.beginTransmission(LP5814_I2C_ADDR);
|
|
if (Wire.endTransmission() != 0) {
|
|
return false; // LP5814 not found
|
|
}
|
|
configure();
|
|
setBrightness(brightness);
|
|
return true;
|
|
}
|
|
|
|
// 0 (LovyanGFX's sleep()) puts the chip in standby; the next level brings it
|
|
// back, set up again.
|
|
void setBrightness(uint8_t brightness) override {
|
|
if (brightness == 0) {
|
|
writeReg(REG_DEVICE_CONFIG0, 0x00); // chip disable: standby
|
|
_standby = true;
|
|
return;
|
|
}
|
|
if (_standby) { configure(); _standby = false; }
|
|
for (uint8_t i = 0; i < 4; i++) {
|
|
writeReg(REG_LED0_PWM + i, brightness);
|
|
}
|
|
_brightness = brightness;
|
|
}
|
|
|
|
uint8_t getBrightness() const { return _brightness; }
|
|
|
|
virtual ~WioTrackerL2Backlight() = default;
|
|
};
|
|
|
|
class LGFX_WioTrackerL2 : public lgfx::LGFX_Device {
|
|
lgfx::Panel_NV3031B _panel_instance;
|
|
lgfx::Bus_SPI _bus_instance;
|
|
lgfx::Touch_GT911 _touch_instance;
|
|
WioTrackerL2Backlight _light_instance;
|
|
|
|
public:
|
|
bool init_impl(bool use_reset, bool use_clear) override {
|
|
// bring up backlight controller while the I2C bus is still clean
|
|
_light_instance.init(_light_instance.getBrightness());
|
|
|
|
bool result = LGFX_Device::init_impl(use_reset, use_clear);
|
|
|
|
// GT911 probe can leave the ESP32 I2C peripheral with a stuck BUSY flag;
|
|
// cycling Wire resets it so later LP5814/sensor traffic doesn't time out
|
|
Wire.end();
|
|
Wire.begin(47, 48);
|
|
|
|
return result;
|
|
}
|
|
|
|
LGFX_WioTrackerL2(void) {
|
|
{
|
|
auto cfg = _bus_instance.config();
|
|
cfg.spi_host = SPI3_HOST;
|
|
cfg.spi_mode = 3;
|
|
cfg.freq_write = L2_SPI_FREQUENCY;
|
|
cfg.freq_read = 16000000;
|
|
cfg.pin_sclk = 42;
|
|
// quad SPI data pins
|
|
cfg.pin_io0 = 41;
|
|
cfg.pin_io1 = 40;
|
|
cfg.pin_io2 = 39;
|
|
cfg.pin_io3 = 38;
|
|
_bus_instance.config(cfg);
|
|
_panel_instance.setBus(&_bus_instance);
|
|
}
|
|
|
|
{
|
|
auto cfg = _panel_instance.config();
|
|
cfg.pin_cs = 46;
|
|
cfg.pin_rst = -1; // reset is on the IO expander, done in board init
|
|
cfg.pin_busy = -1;
|
|
cfg.panel_width = 240; // native portrait orientation
|
|
cfg.panel_height = 320;
|
|
cfg.memory_width = 240;
|
|
cfg.memory_height = 320;
|
|
cfg.offset_x = 0;
|
|
cfg.offset_y = 0;
|
|
cfg.offset_rotation = 1; // panel mounted landscape (320x240)
|
|
cfg.invert = true;
|
|
cfg.rgb_order = true;
|
|
cfg.dlen_16bit = false;
|
|
cfg.bus_shared = false;
|
|
_panel_instance.config(cfg);
|
|
}
|
|
|
|
{
|
|
auto cfg = _touch_instance.config();
|
|
cfg.pin_cs = -1;
|
|
cfg.x_min = 0;
|
|
cfg.x_max = 239;
|
|
cfg.y_min = 0;
|
|
cfg.y_max = 319;
|
|
cfg.pin_int = -1; // INT is on the IO expander
|
|
cfg.offset_rotation = 2;
|
|
cfg.i2c_port = 0;
|
|
cfg.i2c_addr = 0x5D;
|
|
cfg.pin_sda = 47;
|
|
cfg.pin_scl = 48;
|
|
cfg.bus_shared = false;
|
|
cfg.freq = 400000;
|
|
_touch_instance.config(cfg);
|
|
_panel_instance.setTouch(&_touch_instance);
|
|
}
|
|
|
|
_panel_instance.setLight(&_light_instance);
|
|
setPanel(&_panel_instance);
|
|
}
|
|
};
|
|
|
|
class WioTrackerL2Display : public LGFXDisplay {
|
|
LGFX_WioTrackerL2 disp;
|
|
public:
|
|
WioTrackerL2Display() : LGFXDisplay(320, 240, disp) {}
|
|
|
|
// NodePrefs::display_brightness level 0-4 -> backlight PWM
|
|
void setBrightness(uint8_t level) override {
|
|
static const uint8_t PWM[5] = { 20, 60, 120, 190, 255 };
|
|
display->setBrightness(PWM[level > 4 ? 4 : level]);
|
|
}
|
|
|
|
// direct access to the LGFX device (LVGL flush/touch glue)
|
|
lgfx::LGFX_Device* lgfxDevice() { return &disp; }
|
|
|
|
// reliable touch read in logical (UI_ZOOM-scaled) coords; returns false when
|
|
// not touched (LGFXDisplay::getTouch reads an uninitialized point on release)
|
|
bool readTouch(int& x, int& y) {
|
|
lgfx::touch_point_t tp;
|
|
if (disp.getTouch(&tp, 1) == 0) return false;
|
|
x = tp.x / UI_ZOOM;
|
|
y = tp.y / UI_ZOOM;
|
|
return true;
|
|
}
|
|
|
|
// shadows LGFXDisplay::begin() (non-virtual, called on the concrete type in
|
|
// main.cpp) - identical except rotation: our panel config already carries
|
|
// offset_rotation=1 for the landscape mounting, so no extra rotation here
|
|
bool begin() {
|
|
turnOn();
|
|
display->init();
|
|
display->setRotation(0);
|
|
display->setColorDepth(8);
|
|
// The zoomed sprite blit stops a pixel short of the panel edge, so those
|
|
// pixels keep whatever the panel powered up with (they showed as coloured
|
|
// lines down the right side and along the bottom). Clear the panel once.
|
|
display->fillScreen(TFT_BLACK);
|
|
display->setBrightness(153);
|
|
display->setTextColor(TFT_WHITE);
|
|
|
|
buffer.setColorDepth(8);
|
|
buffer.setPsram(true);
|
|
buffer.createSprite(width(), height());
|
|
|
|
return true;
|
|
}
|
|
};
|