refactor(radio): chart-first restructure on hsmc 0.3 + soak hardening
Migrate from hsmc 0.2 (local-patched) to hsmc 0.3.0 (crates.io, with embassy + trace-defmt features). Restructure radio chart and bundle several adjacent stability changes that need to land together for soak runs. Radio chart: - Booting (default, no handlers) -> Operational (default Unconfigured; super-state owning CmdPing/CmdGetInfo/Reset/WedgeDetected handlers + Recovering + CheckRecoveryBudget) -> Dead (terminal). - Cold init's during is structurally un-cancellable. - radio_task is a 30-line shim that panics if run() returns (chart is non-terminating). - Internal queue capacity bumped 8 -> 32 to cover action-emit bursts. Host chart: - Connected splits into WaitingForFirstFrame (default, no timer) + Active (chart-owned 1s inactivity timer via on(after) + self- transition on FrameReceived). Manual Timer::after + last_frame_at removed. Soak hardening: - ld/nrf52840-memory.x: app start 0x1000 -> 0x26000 to leave the factory s140 SoftDevice dormant in flash. - hal/nrf52840.rs: dump POWER.RESETREAS on boot -- WARN on DOG/LOCKUP, INFO on RESETPIN/SREQ/wake-from-OFF. - host/usb.rs: read_packet errors discriminated -- Disabled is debug (normal during USB enum), BufferOverflow stays warn. - COBS encode warns now carry type/tag/payload-length for diagnosis. - RADIO_THROTTLED static initializes to true (no proof of host drain at boot). - driver/sh1106.rs: init retries 3x with 100ms backoff (intermittent first-write race on power-up). - Cargo.toml: unexpected_cfgs allowlist for hsmc's journal/trace-* features. docs/semtech_lora_cad_lbt_guidebook.md: vendor the Semtech CAD/LBT reference for offline lookup.
This commit is contained in:
16 files changed
+2417
-262
No files matched your search
@@ -22,11 +22,18 @@ pub type UsbDriver = mcu::UsbNrfDriver;
|
||||
pub type DisplayI2c = mcu::I2cBus;
|
||||
pub type LedDriver = crate::driver::simple_led::SimpleLed<Output<'static>>;
|
||||
|
||||
pub type DisplayDriver = ssd1306::Ssd1306Async<
|
||||
ssd1306::prelude::I2CInterface<DisplayI2c>,
|
||||
ssd1306::size::DisplaySize128x64,
|
||||
ssd1306::mode::BufferedGraphicsModeAsync<ssd1306::size::DisplaySize128x64>,
|
||||
>;
|
||||
// SH1106 hand-rolled driver instead of the ssd1306 crate. The RAK1921
|
||||
// OLED ships under various Chinese-OEM packaging (e.g. JMD0.96C) that
|
||||
// can be either SSD1306 or SH1106 silicon — physically and pin
|
||||
// identical, but use different addressing modes. The bigger reason
|
||||
// though: ssd1306 0.10's display-interface-i2c emits I2C transaction
|
||||
// patterns (consecutive writes, 3+ ops) that embassy_nrf::twim's
|
||||
// `unreachable!()` rejects, so even on real SSD1306 silicon the panic
|
||||
// would fire. Our hand-rolled SH1106 driver issues only single writes
|
||||
// per logical op and is twim-safe. SH1106 init opcodes are similar
|
||||
// enough that real SSD1306 silicon mostly works (worst case: mirrored
|
||||
// display until proper runtime detection lands).
|
||||
pub type DisplayDriver = crate::driver::sh1106::Sh1106<DisplayI2c>;
|
||||
|
||||
// ── Peripheral bundles ──────────────────────────────────────────────
|
||||
|
||||
@@ -46,22 +53,29 @@ pub struct DisplayParts {
|
||||
// ── Display init ────────────────────────────────────────────────────
|
||||
|
||||
pub async fn create_display(i2c: DisplayI2c) -> Option<DisplayDriver> {
|
||||
use ssd1306::mode::DisplayConfigAsync;
|
||||
use ssd1306::prelude::{Brightness, DisplayRotation};
|
||||
use ssd1306::size::DisplaySize128x64;
|
||||
use ssd1306::{I2CDisplayInterface, Ssd1306Async};
|
||||
let mut display = crate::driver::sh1106::Sh1106::new(i2c, 0x3C);
|
||||
|
||||
let interface = I2CDisplayInterface::new(i2c);
|
||||
let mut display = Ssd1306Async::new(interface, DisplaySize128x64, DisplayRotation::Rotate0)
|
||||
.into_buffered_graphics_mode();
|
||||
|
||||
embassy_time::Timer::after_millis(100).await;
|
||||
if display.init().await.is_err() {
|
||||
defmt::error!("SSD1306 display init failed");
|
||||
return None;
|
||||
// Retry 3× with 100ms backoff. The SH1106's first transaction
|
||||
// after power-on can return Err if the chip's internal voltage
|
||||
// converter hasn't fully settled — observed empirically on
|
||||
// RAK4631/RAK1921. Each attempt is independent (full init
|
||||
// sequence). Display works visibly even if the FIRST attempt
|
||||
// failed, because subsequent attempts succeed.
|
||||
for attempt in 0..3u8 {
|
||||
embassy_time::Timer::after_millis(100).await;
|
||||
if display.init().await.is_ok() {
|
||||
let _ = display.set_brightness(0xFF).await;
|
||||
return Some(display);
|
||||
}
|
||||
if attempt < 2 {
|
||||
defmt::warn!(
|
||||
"SH1106 display init attempt {=u8}/3 failed; retrying",
|
||||
attempt + 1
|
||||
);
|
||||
}
|
||||
}
|
||||
let _ = display.set_brightness(Brightness::BRIGHTEST).await;
|
||||
Some(display)
|
||||
defmt::error!("SH1106 display init failed after 3 attempts");
|
||||
None
|
||||
}
|
||||
|
||||
// ── Board init ──────────────────────────────────────────────────────
|
||||
|
||||
Reference in new issue
Block a user