Device-abstraction refactor: HAL + per-board folders + responsive UI
Replaces the build-flag-driven #ifdef sprawl (~30 blocks across 6 files)
with a small HAL in firmware/src/hal/ and per-board folders under
firmware/src/boards/. Shared code (main.cpp, ui.cpp, splash.cpp) no
longer contains a single `#ifdef BOARD_*` — optional features are
guarded by BoardCaps (runtime) and BOARD_HAS_* macros (compile-time,
inside the board's own files).
Why: lets community contributors port to new ESP32 + AMOLED + touch
combos by dropping in a boards/<name>/ folder + a PlatformIO env,
without touching shared files. See docs/porting/adding-a-board.md.
Highlights:
- New HAL: display_hal, touch_hal, input_hal, power_hal, imu_hal,
board_caps. Each board provides display.cpp, touch.cpp, input.cpp,
power.cpp, imu.cpp, caps.cpp, board_init.cpp + private hardware
drivers (e.g. io_expander.{h,cpp} on AMOLED-1.8).
- PlatformIO build_src_filter selects each board's folder per env.
- ui.cpp picks fonts and layout from board_caps() via compute_layout()
with screen-height breakpoints (>= 460 → large, else compact).
- splash.cpp computes CELL = min(W,H)/20 — responsive instead of two
hardcoded values.
- idle.cpp (from #24) rewired through display_hal + power_hal — no
longer depends on the deleted display_cfg.h / power.h.
- power_hal gains power_hal_is_vbus_in() for idle's
IDLE_SLEEP_WHEN_CHARGING gate.
- boards/template/ + docs/porting/{adding-a-board,hal-contract,
capability-flags}.md to bootstrap new ports.
- display_cfg.h, power.{h,cpp}, imu.{h,cpp}, io_expander.{h,cpp}
deleted from src/ root (moved into boards/<name>/ or hal/).
Verification: both `pio run -e waveshare_amoled_216` and
`pio run -e waveshare_amoled_18` succeed unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
f3ed2425bf
commit
20351212b2
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
// Runtime board description consumed by board-agnostic code (UI, main loop).
|
||||
// Each board provides a single BoardCaps instance via board_caps().
|
||||
//
|
||||
// Compile-time-only facts (pin numbers, library choice) belong in
|
||||
// boards/<name>/board.h and never leak into shared code. Anything the UI or
|
||||
// main loop needs at runtime — display size, optional-feature presence —
|
||||
// goes here so shared code stays free of #ifdef BOARD_*.
|
||||
struct BoardCaps {
|
||||
const char* name; // human-readable, e.g. "Waveshare AMOLED 2.16"
|
||||
|
||||
int16_t width; // active display width in pixels
|
||||
int16_t height; // active display height in pixels
|
||||
|
||||
uint8_t button_count; // 1 = primary (BOOT) only; 2 = primary + secondary
|
||||
bool has_rotation; // IMU-driven CPU rotation in the flush callback
|
||||
bool has_battery; // AXP2101 battery measurement is meaningful
|
||||
bool has_imu; // QMI8658 (or compatible) is populated
|
||||
};
|
||||
|
||||
const BoardCaps& board_caps(void);
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
// Display abstraction. The board provides the QSPI bus, panel driver, and any
|
||||
// CPU-side rotation. Shared code (main.cpp, LVGL glue) never sees the GFX
|
||||
// driver type. Dimensions are not declared here — query board_caps().
|
||||
|
||||
// Construct bus + driver objects. Safe to call before display_hal_begin().
|
||||
// On boards with an IO expander gating the LCD reset, the board's
|
||||
// implementation is responsible for ensuring the expander has released the
|
||||
// reset before talking to the panel.
|
||||
void display_hal_init(void);
|
||||
|
||||
// Bring the panel out of reset, clear it, and apply default brightness.
|
||||
void display_hal_begin(void);
|
||||
|
||||
void display_hal_set_brightness(uint8_t level); // 0..255 (driver-defined scale)
|
||||
void display_hal_fill_screen(uint16_t color565);
|
||||
|
||||
// Write a w×h RGB565 bitmap at (x, y). Boards with software rotation
|
||||
// (e.g. CO5300) transform (x, y, w, h) and the pixel buffer here before
|
||||
// pushing to the panel. Shared LVGL flush_cb just calls this — no #ifdef.
|
||||
void display_hal_draw_bitmap(int32_t x, int32_t y, int32_t w, int32_t h,
|
||||
const uint16_t* pixels);
|
||||
|
||||
// Per-loop housekeeping for rotation-aware boards: detects orientation
|
||||
// changes from the IMU, blanks the panel, invalidates LVGL, and ramps
|
||||
// brightness back up. No-op on boards without rotation.
|
||||
void display_hal_tick(void);
|
||||
|
||||
// LVGL flush regions must be even-aligned on the CO5300; harmless on others.
|
||||
void display_hal_round_area(int32_t* x1, int32_t* y1, int32_t* x2, int32_t* y2);
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
// Optional accelerometer-driven orientation tracker. Returns 0..3 (quarter
|
||||
// turns CW from default mounting). Boards without an IMU — or boards with
|
||||
// rotation intentionally disabled, like AMOLED-1.8 fixed at 0° — return 0
|
||||
// from imu_hal_rotation_quadrant() and no-op on init/tick.
|
||||
|
||||
void imu_hal_init(void);
|
||||
void imu_hal_tick(void);
|
||||
uint8_t imu_hal_rotation_quadrant(void);
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
// Physical button abstraction. Boards report up to two screen-independent
|
||||
// buttons:
|
||||
// PRIMARY — left button on this project's boards (BOOT / GPIO 0).
|
||||
// Drives the Claude Code voice-mode PTT (HID Space).
|
||||
// SECONDARY — right button on boards that have one (e.g. GPIO 18 on
|
||||
// AMOLED-2.16). Drives mode-toggle (HID Shift+Tab). Boards
|
||||
// without it report held=false forever and shared code
|
||||
// handles that gracefully via BoardCaps.button_count.
|
||||
//
|
||||
// The PWR button is owned by power_hal (it's tied to the PMU on some boards
|
||||
// and to an IO expander on others — see power_hal_pwr_pressed()).
|
||||
|
||||
enum InputButton {
|
||||
INPUT_BTN_PRIMARY = 0,
|
||||
INPUT_BTN_SECONDARY = 1,
|
||||
};
|
||||
|
||||
void input_hal_init(void);
|
||||
|
||||
// True while the button is physically held (active-low GPIOs are
|
||||
// de-bounced at the caller's expense — the existing code polls every
|
||||
// loop iteration). Boards lacking a button always return false.
|
||||
bool input_hal_is_held(InputButton btn);
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
// Power / battery / power-button abstraction. Replaces the legacy power.h
|
||||
// API but keeps the same shape so existing call sites stay clean.
|
||||
//
|
||||
// Some boards (AMOLED-2.16) wire PWR through the PMU's PKEY IRQ; others
|
||||
// (AMOLED-1.8) route it through an IO expander. The HAL hides which
|
||||
// source produced the press — shared code just polls
|
||||
// power_hal_pwr_pressed() once per loop.
|
||||
|
||||
void power_hal_init(void);
|
||||
void power_hal_tick(void);
|
||||
|
||||
int power_hal_battery_pct(void); // 0..100, or -1 if no battery (see BoardCaps.has_battery)
|
||||
bool power_hal_is_charging(void);
|
||||
bool power_hal_is_vbus_in(void); // USB cable present (true even without a battery)
|
||||
|
||||
// Edge-triggered: returns true once per PWR short-press, then clears.
|
||||
bool power_hal_pwr_pressed(void);
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
// Touch abstraction. The board owns the touch controller driver and the
|
||||
// TP_INT pin wiring. The HAL implementation is responsible for keeping its
|
||||
// own internal "latest sample" state — shared code calls touch_hal_read()
|
||||
// once per loop and feeds it into LVGL.
|
||||
//
|
||||
// Implementations should complete touch_hal_read() in well under 5 ms (a
|
||||
// single I2C burst). LVGL polls this at the screen refresh rate.
|
||||
|
||||
void touch_hal_init(void);
|
||||
|
||||
// Pump the controller and return the latest sample. *pressed reflects
|
||||
// whether any finger is currently down; coordinates are valid only when
|
||||
// pressed is true and are in display (post-orientation) coordinates.
|
||||
void touch_hal_read(uint16_t* x, uint16_t* y, bool* pressed);
|
||||
Reference in New Issue
Block a user