Adds nRF52840 + SX1262 + 1.14" 240×135 ST7789 color TFT support. First color display board in the project; introduces a BoardDisplay trait so each board renders to its native pixel format and resolution without aliasing or color-quantization round-trips. Architecture: - New BoardDisplay trait (src/display.rs) bundles render_*, present(), set_bright/set_dim. Replaces the old DrawTarget<Color=BinaryColor> + DisplayBrightness combo as the LoRaBoard::DisplayDriver bound. - Mono OLED drivers (Sh1106, Ssd1306Async) get adapter impls that delegate to the existing display::render module — no behavior change for any existing board. - T114 ST7789 driver (src/driver/st7789.rs) wraps lcd-async (async fork of mipidsi) with a 64,800-byte raw byte framebuffer in .bss. Renders via lcd_async::raw_framebuf::RawFrameBuf<Rgb565, _>, presents via a single async DMA blit (panel.show_raw_data(...).await) — the await yields the executor for the ~65 ms SPI transfer so the radio task isn't starved during repaints. - PWM backlight on PWM0 / P0.15 (active-LOW, inverted polarity) for smooth dim/bright transitions instead of GPIO toggle. - Native 240×135 Rgb565 renderer (src/display/render_color.rs) with splash + dashboard + sparkline. - Shared QR-code blit (src/display/qr.rs), generic over PixelColor, re-centers the 64×64 bitmap which has cells in cols 0..50 and ~14 px of right-side padding — both mono and color splash now show the QR with 7 px quiet zones on all four sides (was visibly left-shifted). - Shared content types (BoardInfo, DashboardCtx) decouple what to draw from how each board renders it. - create_display() now takes the board's full DisplayParts bundle (was just the I2C bus) so non-I2C displays can carry SPI + control pins through. Hardware: - VEXT_ENABLE on P0.21 driven HIGH at boot — powers the SX1262 + TFT rail. Without this, lora-phy init succeeds at the SPI level but TX_DONE never fires. - VTFT_CTRL on P0.03, active LOW; backlight on P0.15, active LOW. - TFT SPI2 (SCK P1.08 / MOSI P1.09) at 8 MHz over EasyDMA. Async ExclusiveDevice from embedded-hal-bus (with the 'async' feature). - lcd-async builder at display_size(135, 240), display_offset(52, 40), Deg90 rotation, ColorInversion::Inverted — standard 1.14" ST7789 die mapping. - Linker: shared ld/nrf52840-memory.x (S140 SoftDevice preserved at 0x01000-0x26000 per project policy).
95 lines
3.2 KiB
Rust
95 lines
3.2 KiB
Rust
//! Board abstraction trait.
|
||
//!
|
||
//! Every board must implement [`LoRaBoard`] with concrete associated types
|
||
//! for its radio, host communication, display, and LED peripherals. The
|
||
//! compiler enforces completeness — missing types or methods are compile errors.
|
||
//!
|
||
//! See `src/board/PORTING.md` for a step-by-step guide to adding a new board.
|
||
|
||
use crate::display::BoardDisplay;
|
||
|
||
/// Async RGB LED control. Boards without an LED use `()` which is a no-op.
|
||
pub trait RgbLed {
|
||
fn set_rgb(&mut self, r: u8, g: u8, b: u8) -> impl core::future::Future<Output = ()>;
|
||
}
|
||
|
||
impl RgbLed for () {
|
||
async fn set_rgb(&mut self, _r: u8, _g: u8, _b: u8) {}
|
||
}
|
||
|
||
/// Compile-time contract for a DongLoRa board.
|
||
pub trait LoRaBoard: Sized {
|
||
/// Human-readable board name (shown on display splash screen).
|
||
const NAME: &'static str;
|
||
|
||
/// TX power range in dBm (min, max) for this board's radio + PA.
|
||
const TX_POWER_RANGE: (i8, i8);
|
||
|
||
/// Radio peripheral bundle (driver + delay).
|
||
type RadioParts;
|
||
|
||
/// Host communication peripheral bundle (USB or UART driver).
|
||
type CommParts;
|
||
|
||
/// Display peripheral bundle (I2C bus for display init).
|
||
type DisplayParts;
|
||
|
||
/// Concrete display driver type. Each board's driver implements
|
||
/// [`BoardDisplay`] to render the splash + dashboard content into its
|
||
/// native pixel format and resolution and to flush / dim the panel.
|
||
type DisplayDriver: BoardDisplay;
|
||
|
||
/// RGB LED driver. Boards without an LED use `()`.
|
||
type LedDriver: RgbLed;
|
||
|
||
/// Initialize the board hardware.
|
||
fn init() -> Self;
|
||
|
||
/// Read the board's unique hardware address (MAC, device ID, etc.).
|
||
fn mac_address() -> [u8; 6];
|
||
|
||
/// Radio chip identifier reported in `GET_INFO`. Defaults to SX1262
|
||
/// (`0x0002`), matching every currently-supported board. Override for
|
||
/// boards that carry a different chip (LLCC68, SX1261, LR11xx, …).
|
||
fn radio_chip_id() -> u16 {
|
||
// SX1262 — see PROTOCOL.md §8.
|
||
0x0002
|
||
}
|
||
|
||
/// (freq_min_hz, freq_max_hz) — the RF front-end's effective tuning
|
||
/// window on this specific board. Reported in `GET_INFO`. Defaults
|
||
/// to the SX1262's nominal 150–960 MHz range.
|
||
fn freq_range_hz() -> (u32, u32) {
|
||
(150_000_000, 960_000_000)
|
||
}
|
||
|
||
/// Bitmap of supported LoRa spreading factors — bit N = SF N. Defaults
|
||
/// to SX126x's SF5–SF12 (`0x1FE0`). SX127x boards must override to
|
||
/// `0x1FC0` (SF6–SF12); LLCC68 boards to `0x0FE0` (SF5–SF11).
|
||
fn supported_sf_bitmap() -> u16 {
|
||
0x1FE0
|
||
}
|
||
|
||
/// Bitmap of supported LoRa bandwidths. Bit positions match
|
||
/// `LoRaBandwidth::as_u8()` (sub-GHz BW enum values 0..9). Defaults
|
||
/// to all sub-GHz BWs (`0x03FF`). SX128x boards flip the 2.4 GHz
|
||
/// bits when we add them.
|
||
fn supported_bw_bitmap() -> u16 {
|
||
0x03FF
|
||
}
|
||
|
||
/// Decompose initialized board into peripheral bundles for each task.
|
||
fn into_parts(
|
||
self,
|
||
) -> BoardParts<Self::RadioParts, Self::CommParts, Self::DisplayParts, Self::LedDriver>;
|
||
}
|
||
|
||
/// Peripheral bundles for each firmware task, produced by [`LoRaBoard::into_parts`].
|
||
pub struct BoardParts<R, C, D, L> {
|
||
pub radio: R,
|
||
pub host: C,
|
||
pub display: Option<D>,
|
||
pub led: L,
|
||
pub mac: [u8; 6],
|
||
}
|