feat: Initial support for the x3 (#875)
## 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.
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <BatteryMonitor.h>
|
||||
#include <InputManager.h>
|
||||
|
||||
// Display SPI pins (custom pins for XteinkX4, not hardware SPI defaults)
|
||||
@@ -18,6 +17,27 @@
|
||||
|
||||
#define UART0_RXD 20 // Used for USB connection detection
|
||||
|
||||
// Xteink X3 Hardware
|
||||
#define X3_I2C_SDA 20
|
||||
#define X3_I2C_SCL 0
|
||||
#define X3_I2C_FREQ 400000
|
||||
|
||||
// TI BQ27220 Fuel gauge I2C
|
||||
#define I2C_ADDR_BQ27220 0x55 // Fuel gauge I2C address
|
||||
#define BQ27220_SOC_REG 0x2C // StateOfCharge() command code (%)
|
||||
#define BQ27220_CUR_REG 0x0C // Current() command code (signed mA)
|
||||
#define BQ27220_VOLT_REG 0x08 // Voltage() command code (mV)
|
||||
|
||||
// Analog DS3231 RTC I2C
|
||||
#define I2C_ADDR_DS3231 0x68 // RTC I2C address
|
||||
#define DS3231_SEC_REG 0x00 // Seconds command code (BCD)
|
||||
|
||||
// QST QMI8658 IMU I2C
|
||||
#define I2C_ADDR_QMI8658 0x6B // IMU I2C address
|
||||
#define I2C_ADDR_QMI8658_ALT 0x6A // IMU I2C fallback address
|
||||
#define QMI8658_WHO_AM_I_REG 0x00 // WHO_AM_I command code
|
||||
#define QMI8658_WHO_AM_I_VALUE 0x05 // WHO_AM_I expected value
|
||||
|
||||
class HalGPIO {
|
||||
#if CROSSPOINT_EMULATED == 0
|
||||
InputManager inputMgr;
|
||||
@@ -26,9 +46,19 @@ class HalGPIO {
|
||||
bool lastUsbConnected = false;
|
||||
bool usbStateChanged = false;
|
||||
|
||||
public:
|
||||
enum class DeviceType : uint8_t { X4, X3 };
|
||||
|
||||
private:
|
||||
DeviceType _deviceType = DeviceType::X4;
|
||||
|
||||
public:
|
||||
HalGPIO() = default;
|
||||
|
||||
// Inline device type helpers for cleaner downstream checks
|
||||
inline bool deviceIsX3() const { return _deviceType == DeviceType::X3; }
|
||||
inline bool deviceIsX4() const { return _deviceType == DeviceType::X4; }
|
||||
|
||||
// Start button GPIO and setup SPI for screen and SD card
|
||||
void begin();
|
||||
|
||||
@@ -41,6 +71,14 @@ class HalGPIO {
|
||||
bool wasAnyReleased() const;
|
||||
unsigned long getHeldTime() const;
|
||||
|
||||
// Setup wake up GPIO and enter deep sleep
|
||||
void startDeepSleep();
|
||||
|
||||
// Verify power button was held long enough after wakeup.
|
||||
// If verification fails, enters deep sleep and does not return.
|
||||
// Should only be called when wakeup reason is PowerButton.
|
||||
void verifyPowerButtonWakeup(uint16_t requiredDurationMs, bool shortPressAllowed);
|
||||
|
||||
// Check if USB is connected
|
||||
bool isUsbConnected() const;
|
||||
|
||||
@@ -61,4 +99,4 @@ class HalGPIO {
|
||||
static constexpr uint8_t BTN_POWER = 6;
|
||||
};
|
||||
|
||||
extern HalGPIO gpio; // Singleton
|
||||
extern HalGPIO gpio;
|
||||
|
||||
Reference in New Issue
Block a user