mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-09-14 15:16:40 +00:00
feat(boards): add M5Stack Cardputer ADV and LilyGO T-Echo Lite KeyShield
New M5Stack Cardputer ADV variant (ESP32-S3, ST7789 TFT, built-in TCA8418 QWERTY keyboard, PI4IOE5V6408 LoRa-cap IO-expander autodetect), and a KeyShield accessory variant for the existing LilyGO T-Echo Lite (external TCA8418 T9 keypad + AW21009 backlight driver). Both keyboards share one ENV_USE_TCA8418 polling block in UITask.cpp::loop(), coexisting with the unrelated CardKB support (different chip/address/flag). Fixes carried in from the contributed T-Echo Lite code: swapped GPS RX/TX pins, TX-LED hooks, TCXO voltage, missing GxEPD2_122_T61 panel include. Fixed during integration: I2C bus was probed for an RTC before Wire.begin() configured its pins on Cardputer ADV (silent RTC autodetect failure). Added dedicated *_solo_dual release envs for both boards (auto-picked up by the solo-firmware release workflow). Gave the T-Echo Lite KeyShield solo build -Os/-Ofast-unflag like every other nRF52 solo build (was missing, cut flash usage from 90.7% to 61.4%). Ported the shared misc-fixed 6x9 font (full Latin/Greek/Cyrillic, opt-in via OLED_MISC_FIXED_FONT) to ST7789Display for the Cardputer's on-screen keyboard. ST7789Spi isn't Adafruit_GFX-based like the other single-font drivers, and this panel's logical->physical scale is non-integer, so glyphs are re-packed to XBM and blitted through the existing drawXbm(), which already does correct fractional-scale boundary math, rather than duplicating that logic. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,11 @@
|
||||
|
||||
#include "ST7789Display.h"
|
||||
|
||||
#ifdef OLED_MISC_FIXED_FONT
|
||||
#include "MiscFixedFont.h"
|
||||
#include "LemonIcons.h"
|
||||
#endif
|
||||
|
||||
#ifndef X_OFFSET
|
||||
#define X_OFFSET 0 // No offset needed for landscape
|
||||
#endif
|
||||
@@ -13,11 +18,49 @@
|
||||
#ifdef HELTEC_VISION_MASTER_T190
|
||||
#define SCALE_X 2.5f // 320 / 128
|
||||
#define SCALE_Y 2.65625f // 170 / 64
|
||||
#elif defined(CARDPUTER_ADV)
|
||||
#define SCALE_X 1.875f // 240 / 128
|
||||
#define SCALE_Y 2.109375f // 135 / 64
|
||||
#else
|
||||
#define SCALE_X 1.875f // 240 / 128
|
||||
#define SCALE_Y 2.109375f // 135 / 64
|
||||
#endif
|
||||
|
||||
#ifdef OLED_MISC_FIXED_FONT
|
||||
// MiscFixedFont.h/LemonIcons.h store each glyph's rows bit-packed
|
||||
// *continuously* (Adafruit_GFX's native GFXfont convention -- no per-row byte
|
||||
// padding), but drawXbm() below expects classic XBM packing (each row padded
|
||||
// to a whole byte). Re-pack into a small stack buffer so the glyph can be
|
||||
// blitted through drawXbm() -- which already implements the correct
|
||||
// logical->physical scaling for this panel's non-integer SCALE_X/Y (via
|
||||
// per-pixel boundary differences), instead of duplicating that math here.
|
||||
static bool repackGlyphToXbm(const uint8_t* bitmapProgmem, uint16_t byteOffset, uint8_t w, uint8_t h, uint8_t* out, uint8_t outCap) {
|
||||
uint8_t widthInBytes = (w + 7) / 8;
|
||||
uint16_t needed = (uint16_t)widthInBytes * h;
|
||||
if (needed == 0 || needed > outCap) return false;
|
||||
memset(out, 0, needed);
|
||||
uint16_t srcBit = 0;
|
||||
for (uint8_t row = 0; row < h; row++) {
|
||||
for (uint8_t col = 0; col < w; col++) {
|
||||
uint16_t byteIdx = byteOffset + (srcBit >> 3);
|
||||
uint8_t bitMask = 0x80 >> (srcBit & 7);
|
||||
if (pgm_read_byte(bitmapProgmem + byteIdx) & bitMask) {
|
||||
out[row * widthInBytes + (col >> 3)] |= (0x80 >> (col & 7));
|
||||
}
|
||||
srcBit++;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t ST7789Display::glyphXAdvance(uint32_t cp) {
|
||||
for (uint8_t i = 0; i < lemonIconCount; i++)
|
||||
if (pgm_read_dword(&lemonIconCPs[i]) == cp) return pgm_read_byte(&lemonIconGlyphs[i].xAdvance);
|
||||
if (cp < MiscFixed.first || cp > MiscFixed.last) return 6;
|
||||
return pgm_read_byte(&MiscFixedGlyphs[cp - MiscFixed.first].xAdvance);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ST7789Display::begin() {
|
||||
if(!_isOn) {
|
||||
pinMode(PIN_TFT_VDD_CTL, OUTPUT);
|
||||
@@ -131,12 +174,55 @@ void ST7789Display::setColor(Color c) {
|
||||
}
|
||||
|
||||
void ST7789Display::setCursor(int x, int y) {
|
||||
#ifdef OLED_MISC_FIXED_FONT
|
||||
_lx = x;
|
||||
_ly = y;
|
||||
#endif
|
||||
_x = x*SCALE_X + X_OFFSET;
|
||||
_y = y*SCALE_Y + Y_OFFSET;
|
||||
}
|
||||
|
||||
void ST7789Display::print(const char* str) {
|
||||
#ifdef OLED_MISC_FIXED_FONT
|
||||
int lx = _lx, ly = _ly;
|
||||
uint8_t glyphBuf[16];
|
||||
const uint8_t* p = (const uint8_t*)str;
|
||||
while (*p) {
|
||||
uint32_t cp = DisplayDriver::decodeCodepoint(p);
|
||||
if (cp == '\n') { ly += MiscFixed.yAdvance; lx = _lx; continue; }
|
||||
|
||||
bool drawn = false;
|
||||
for (uint8_t i = 0; i < lemonIconCount; i++) {
|
||||
if (pgm_read_dword(&lemonIconCPs[i]) != cp) continue;
|
||||
const GFXglyph* g = &lemonIconGlyphs[i];
|
||||
uint8_t gw = pgm_read_byte(&g->width), gh = pgm_read_byte(&g->height);
|
||||
int8_t xo = (int8_t)pgm_read_byte(&g->xOffset), yo = (int8_t)pgm_read_byte(&g->yOffset);
|
||||
uint16_t bo = pgm_read_word(&g->bitmapOffset);
|
||||
if (repackGlyphToXbm(lemonIconBitmaps, bo, gw, gh, glyphBuf, sizeof(glyphBuf)))
|
||||
drawXbm(lx + xo, ly + 6 + yo, glyphBuf, gw, gh);
|
||||
lx += pgm_read_byte(&g->xAdvance);
|
||||
drawn = true;
|
||||
break;
|
||||
}
|
||||
if (drawn) continue;
|
||||
|
||||
if (cp < MiscFixed.first || cp > MiscFixed.last) {
|
||||
if (cp >= 0x20) fillRect(lx + 1, ly, 4, 6);
|
||||
lx += 6;
|
||||
continue;
|
||||
}
|
||||
|
||||
const GFXglyph* g = &MiscFixedGlyphs[cp - MiscFixed.first];
|
||||
uint8_t gw = pgm_read_byte(&g->width), gh = pgm_read_byte(&g->height);
|
||||
int8_t xo = (int8_t)pgm_read_byte(&g->xOffset), yo = (int8_t)pgm_read_byte(&g->yOffset);
|
||||
uint16_t bo = pgm_read_word(&g->bitmapOffset);
|
||||
if (repackGlyphToXbm(MiscFixedBitmaps, bo, gw, gh, glyphBuf, sizeof(glyphBuf)))
|
||||
drawXbm(lx + xo, ly + 7 + yo, glyphBuf, gw, gh);
|
||||
lx += pgm_read_byte(&g->xAdvance);
|
||||
}
|
||||
#else
|
||||
display.drawString(_x, _y, str);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ST7789Display::printWordWrap(const char* str, int max_width) {
|
||||
@@ -188,7 +274,14 @@ void ST7789Display::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
|
||||
}
|
||||
|
||||
uint16_t ST7789Display::getTextWidth(const char* str) {
|
||||
#ifdef OLED_MISC_FIXED_FONT
|
||||
uint16_t w = 0;
|
||||
const uint8_t* p = (const uint8_t*)str;
|
||||
while (*p) w += glyphXAdvance(DisplayDriver::decodeCodepoint(p));
|
||||
return w;
|
||||
#else
|
||||
return display.getStringWidth(str) / SCALE_X;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ST7789Display::endFrame() {
|
||||
|
||||
@@ -10,17 +10,46 @@ class ST7789Display : public DisplayDriver {
|
||||
ST7789Spi display;
|
||||
bool _isOn;
|
||||
uint16_t _color;
|
||||
int _x=0, _y=0;
|
||||
int _x=0, _y=0; // physical (SCALE_X/Y-scaled) cursor, used by the ArialMT path
|
||||
#ifdef OLED_MISC_FIXED_FONT
|
||||
int _lx=0, _ly=0; // logical (pre-scale) cursor, used by the misc-fixed path
|
||||
#endif
|
||||
|
||||
bool i2c_probe(TwoWire& wire, uint8_t addr);
|
||||
#ifdef OLED_MISC_FIXED_FONT
|
||||
// Thin wrapper over the shared misc-fixed glyph metrics, kept out of the
|
||||
// header so the font tables land in one translation unit only.
|
||||
uint8_t glyphXAdvance(uint32_t cp);
|
||||
#endif
|
||||
public:
|
||||
#ifdef HELTEC_VISION_MASTER_T190
|
||||
ST7789Display() : DisplayDriver(128, 64), display(&SPI, PIN_TFT_RST, PIN_TFT_DC, PIN_TFT_CS, GEOMETRY_RAWMODE, 320, 170,PIN_TFT_SDA,-1,PIN_TFT_SCL) {_isOn = false;}
|
||||
#elif defined(CARDPUTER_ADV)
|
||||
ST7789Display() : DisplayDriver(128, 64), display(&SPI, PIN_TFT_RST, PIN_TFT_DC, PIN_TFT_CS, GEOMETRY_RAWMODE, 240, 135, PIN_TFT_SDA, -1, PIN_TFT_SCL) {_isOn = false;}
|
||||
#else
|
||||
ST7789Display() : DisplayDriver(128, 64), display(&SPI1, PIN_TFT_RST, PIN_TFT_DC, PIN_TFT_CS, GEOMETRY_RAWMODE, 240, 135) {_isOn = false;}
|
||||
#endif
|
||||
bool begin();
|
||||
|
||||
#ifdef OLED_MISC_FIXED_FONT
|
||||
// Same misc-fixed 6x9 font the SH1106 and e-ink drivers use: full
|
||||
// Latin/Greek/Cyrillic instead of the built-in ArialMT font's Latin-only
|
||||
// coverage, so the keyboard's alphabets render as themselves. Opt-in per
|
||||
// variant (the font costs ~14 KB of flash) — see the boards that set
|
||||
// OLED_MISC_FIXED_FONT in their platformio.ini.
|
||||
int getLineHeight() const override { return 9; } // 6x9 box height, logical units
|
||||
// The font's own glyph table is ink-tight, unlike ArialMT (below).
|
||||
int textWidthTrailingGap() const override { return 0; }
|
||||
bool isSingleFont() const override { return true; }
|
||||
void setSingleFont(bool) override { } // single-font: ignore toggles
|
||||
void translateUTF8ToBlocks(char* dest, const char* src, size_t dest_size) override {
|
||||
// No transliteration needed — print() renders UTF-8 directly.
|
||||
strncpy(dest, src, dest_size - 1);
|
||||
dest[dest_size - 1] = '\0';
|
||||
}
|
||||
uint16_t getCodepointWidth(uint32_t cp) override { return glyphXAdvance(cp); }
|
||||
#endif
|
||||
|
||||
bool isOn() override { return _isOn; }
|
||||
void turnOn() override;
|
||||
void turnOff() override;
|
||||
|
||||
Reference in New Issue
Block a user