## Summary Adds Xteink X3 hardware support to CrossPoint Reader. The X3 uses the same SSD1677 e-ink controller as the X4 but with a different panel (792x528 vs 800x480), different button layout, and an I2C fuel gauge (BQ27220) instead of ADC-based battery reading. All X3-specific behavior is gated by runtime device detection — X4 behavior is unchanged. Depends on community-sdk X3 support: open-x4-epaper/community-sdk#19 (merged). ## Changes ### HAL Layer **HalGPIO** (`lib/hal/HalGPIO.cpp/.h`) - I2C-based device fingerprinting at boot: probes for BQ27220 fuel gauge, DS3231 RTC, and QMI8658 IMU to distinguish X3 from X4 - Detection result cached in NVS for fast subsequent boots - Exposes `deviceIsX3()` / `deviceIsX4()` helpers used throughout the codebase - X3 button mapping (7 GPIOs vs X4's layout) - USB connection detection and wake classification for X3 **HalDisplay** (`lib/hal/HalDisplay.cpp/.h`) - Calls `einkDisplay.setDisplayX3()` before init when X3 is detected - Requests display resync after power button / flash wake events - Runtime display dimension accessors (`getDisplayWidth()`, `getDisplayHeight()`, `getBufferSize()`) - Exposed as global `display` instance for use by image converters **HalPowerManager** (`lib/hal/HalPowerManager.cpp/.h`) - X3 battery reading via I2C fuel gauge (BQ27220 at 0x55, SOC register) - X3 power button uses GPIO hold for deep sleep ### Display & Rendering **GfxRenderer** (`lib/GfxRenderer/GfxRenderer.cpp/.h`) - Buffer size and display dimensions are now runtime values (not compile-time constants) to support both panel sizes - X3 anti-aliasing tuning: only the darker grayscale level is applied to avoid washed-out text on the X3 panel. X4 retains both levels via `deviceIsX4()` gate **Image Converters** (`lib/JpegToBmpConverter`, `lib/PngToBmpConverter`) - Cover image prescale target uses runtime display dimensions from HAL instead of hardcoded 800x480 ### UI Themes **BaseTheme / LyraTheme** (`src/components/themes/`) - X3 button position mapping for the different physical layout - Adjusted UI element positioning for 792x528 viewport ### Boot & Init **main.cpp** - X3 hardware detection logging - Adjusted init sequence for X3 (no `HalSystem::begin()` dependency on X3 path) **HomeActivity** - Uses runtime `renderer.getBufferSize()` instead of static `GfxRenderer::getBufferSize()` FYI I did not add support for the gyro page turner. That can be it's own PR.
92 lines
3.2 KiB
C++
92 lines
3.2 KiB
C++
#include <HalDisplay.h>
|
|
#include <HalGPIO.h>
|
|
|
|
// Global HalDisplay instance
|
|
HalDisplay display;
|
|
|
|
#define SD_SPI_MISO 7
|
|
|
|
HalDisplay::HalDisplay() : einkDisplay(EPD_SCLK, EPD_MOSI, EPD_CS, EPD_DC, EPD_RST, EPD_BUSY) {}
|
|
|
|
HalDisplay::~HalDisplay() {}
|
|
|
|
void HalDisplay::begin() {
|
|
// Set X3-specific panel mode before initializing.
|
|
if (gpio.deviceIsX3()) {
|
|
einkDisplay.setDisplayX3();
|
|
}
|
|
|
|
einkDisplay.begin();
|
|
|
|
// Request resync after specific wakeup events to ensure clean display state
|
|
const auto wakeupReason = gpio.getWakeupReason();
|
|
if (wakeupReason == HalGPIO::WakeupReason::PowerButton || wakeupReason == HalGPIO::WakeupReason::AfterFlash ||
|
|
wakeupReason == HalGPIO::WakeupReason::Other) {
|
|
einkDisplay.requestResync();
|
|
}
|
|
}
|
|
|
|
void HalDisplay::clearScreen(uint8_t color) const { einkDisplay.clearScreen(color); }
|
|
|
|
void HalDisplay::drawImage(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h,
|
|
bool fromProgmem) const {
|
|
einkDisplay.drawImage(imageData, x, y, w, h, fromProgmem);
|
|
}
|
|
|
|
void HalDisplay::drawImageTransparent(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h,
|
|
bool fromProgmem) const {
|
|
einkDisplay.drawImageTransparent(imageData, x, y, w, h, fromProgmem);
|
|
}
|
|
|
|
EInkDisplay::RefreshMode convertRefreshMode(HalDisplay::RefreshMode mode) {
|
|
switch (mode) {
|
|
case HalDisplay::FULL_REFRESH:
|
|
return EInkDisplay::FULL_REFRESH;
|
|
case HalDisplay::HALF_REFRESH:
|
|
return EInkDisplay::HALF_REFRESH;
|
|
case HalDisplay::FAST_REFRESH:
|
|
default:
|
|
return EInkDisplay::FAST_REFRESH;
|
|
}
|
|
}
|
|
|
|
void HalDisplay::displayBuffer(HalDisplay::RefreshMode mode, bool turnOffScreen) {
|
|
if (gpio.deviceIsX3() && mode == RefreshMode::HALF_REFRESH) {
|
|
einkDisplay.requestResync(1);
|
|
}
|
|
|
|
einkDisplay.displayBuffer(convertRefreshMode(mode), turnOffScreen);
|
|
}
|
|
|
|
void HalDisplay::refreshDisplay(HalDisplay::RefreshMode mode, bool turnOffScreen) {
|
|
if (gpio.deviceIsX3() && mode == RefreshMode::HALF_REFRESH) {
|
|
einkDisplay.requestResync(1);
|
|
}
|
|
|
|
einkDisplay.refreshDisplay(convertRefreshMode(mode), turnOffScreen);
|
|
}
|
|
|
|
void HalDisplay::deepSleep() { einkDisplay.deepSleep(); }
|
|
|
|
uint8_t* HalDisplay::getFrameBuffer() const { return einkDisplay.getFrameBuffer(); }
|
|
|
|
void HalDisplay::copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t* msbBuffer) {
|
|
einkDisplay.copyGrayscaleBuffers(lsbBuffer, msbBuffer);
|
|
}
|
|
|
|
void HalDisplay::copyGrayscaleLsbBuffers(const uint8_t* lsbBuffer) { einkDisplay.copyGrayscaleLsbBuffers(lsbBuffer); }
|
|
|
|
void HalDisplay::copyGrayscaleMsbBuffers(const uint8_t* msbBuffer) { einkDisplay.copyGrayscaleMsbBuffers(msbBuffer); }
|
|
|
|
void HalDisplay::cleanupGrayscaleBuffers(const uint8_t* bwBuffer) { einkDisplay.cleanupGrayscaleBuffers(bwBuffer); }
|
|
|
|
void HalDisplay::displayGrayBuffer(bool turnOffScreen) { einkDisplay.displayGrayBuffer(turnOffScreen); }
|
|
|
|
uint16_t HalDisplay::getDisplayWidth() const { return einkDisplay.getDisplayWidth(); }
|
|
|
|
uint16_t HalDisplay::getDisplayHeight() const { return einkDisplay.getDisplayHeight(); }
|
|
|
|
uint16_t HalDisplay::getDisplayWidthBytes() const { return einkDisplay.getDisplayWidthBytes(); }
|
|
|
|
uint32_t HalDisplay::getBufferSize() const { return einkDisplay.getBufferSize(); }
|