style: cargo fmt

This commit is contained in:
swaits committed 2026-04-16 13:25:19 -06:00
1 parent 649c29218c
commit f9582c0b94
19 files changed
+142 -86

No files matched your search

+1 -2
View File
@@ -118,8 +118,7 @@ fn main() {
}; };
if let Some(src) = memory_x { if let Some(src) = memory_x {
let out_dir = std::env::var("OUT_DIR").unwrap(); let out_dir = std::env::var("OUT_DIR").unwrap();
fs::copy(src, format!("{out_dir}/memory.x")) fs::copy(src, format!("{out_dir}/memory.x")).expect("failed to copy memory.x");
.expect("failed to copy memory.x");
println!("cargo:rustc-link-search={out_dir}"); println!("cargo:rustc-link-search={out_dir}");
} }
println!("cargo:rerun-if-changed=ld/nrf52840-memory.x"); println!("cargo:rerun-if-changed=ld/nrf52840-memory.x");
+15 -3
View File
@@ -35,14 +35,22 @@ pub struct RadioParts {
// SSD1306 display support is Heltec-specific. ESP32-S3 boards that use a // SSD1306 display support is Heltec-specific. ESP32-S3 boards that use a
// different controller (e.g. ThinkNode-M2 uses SH1106) provide their own // different controller (e.g. ThinkNode-M2 uses SH1106) provide their own
// DisplayParts / DisplayDriver / create_display directly. // DisplayParts / DisplayDriver / create_display directly.
#[cfg(any(feature = "heltec_v3", feature = "heltec_v3_uart", feature = "heltec_v4"))] #[cfg(any(
feature = "heltec_v3",
feature = "heltec_v3_uart",
feature = "heltec_v4"
))]
pub struct DisplayParts { pub struct DisplayParts {
pub i2c: DisplayI2c, pub i2c: DisplayI2c,
} }
// ── Display driver ────────────────────────────────────────────────── // ── Display driver ──────────────────────────────────────────────────
#[cfg(any(feature = "heltec_v3", feature = "heltec_v3_uart", feature = "heltec_v4"))] #[cfg(any(
feature = "heltec_v3",
feature = "heltec_v3_uart",
feature = "heltec_v4"
))]
pub type DisplayDriver = ssd1306::Ssd1306Async< pub type DisplayDriver = ssd1306::Ssd1306Async<
ssd1306::prelude::I2CInterface<DisplayI2c>, ssd1306::prelude::I2CInterface<DisplayI2c>,
ssd1306::size::DisplaySize128x64, ssd1306::size::DisplaySize128x64,
@@ -50,7 +58,11 @@ pub type DisplayDriver = ssd1306::Ssd1306Async<
>; >;
/// Construct and initialize an SSD1306 display from raw I2C. /// Construct and initialize an SSD1306 display from raw I2C.
#[cfg(any(feature = "heltec_v3", feature = "heltec_v3_uart", feature = "heltec_v4"))] #[cfg(any(
feature = "heltec_v3",
feature = "heltec_v3_uart",
feature = "heltec_v4"
))]
pub async fn create_display(i2c: DisplayI2c) -> Option<DisplayDriver> { pub async fn create_display(i2c: DisplayI2c) -> Option<DisplayDriver> {
use ssd1306::mode::DisplayConfigAsync; use ssd1306::mode::DisplayConfigAsync;
use ssd1306::prelude::{Brightness, DisplayRotation}; use ssd1306::prelude::{Brightness, DisplayRotation};
+1 -3
View File
@@ -14,9 +14,7 @@ use super::traits::{BoardParts, LoRaBoard};
use crate::driver::simple_led::SimpleLed; use crate::driver::simple_led::SimpleLed;
use crate::hal::esp32s3 as mcu; use crate::hal::esp32s3 as mcu;
pub use super::esp32s3::{ pub use super::esp32s3::{create_display, DisplayDriver, DisplayParts, RadioDriver, RadioParts};
create_display, DisplayDriver, DisplayParts, RadioDriver, RadioParts,
};
pub type UsbDriver = esp_hal::otg_fs::asynch::Driver<'static>; pub type UsbDriver = esp_hal::otg_fs::asynch::Driver<'static>;
pub type LedDriver = SimpleLed<Output<'static>>; pub type LedDriver = SimpleLed<Output<'static>>;
+1 -3
View File
@@ -13,9 +13,7 @@ use super::traits::{BoardParts, LoRaBoard};
use crate::driver::simple_led::SimpleLed; use crate::driver::simple_led::SimpleLed;
use crate::hal::esp32s3 as mcu; use crate::hal::esp32s3 as mcu;
pub use super::esp32s3::{ pub use super::esp32s3::{create_display, DisplayDriver, DisplayParts, RadioDriver, RadioParts};
create_display, DisplayDriver, DisplayParts, RadioDriver, RadioParts,
};
pub type UartDriver = esp_hal::uart::Uart<'static, esp_hal::Async>; pub type UartDriver = esp_hal::uart::Uart<'static, esp_hal::Async>;
pub type LedDriver = SimpleLed<Output<'static>>; pub type LedDriver = SimpleLed<Output<'static>>;
+1 -3
View File
@@ -6,9 +6,7 @@ use super::traits::{BoardParts, LoRaBoard};
use crate::driver::simple_led::SimpleLed; use crate::driver::simple_led::SimpleLed;
use crate::hal::esp32s3 as mcu; use crate::hal::esp32s3 as mcu;
pub use super::esp32s3::{ pub use super::esp32s3::{create_display, DisplayDriver, DisplayParts, RadioDriver, RadioParts};
create_display, DisplayDriver, DisplayParts, RadioDriver, RadioParts,
};
pub type UsbDriver = esp_hal::otg_fs::asynch::Driver<'static>; pub type UsbDriver = esp_hal::otg_fs::asynch::Driver<'static>;
+6 -1
View File
@@ -129,7 +129,12 @@ impl LoRaBoard for Board {
// ── Display (optional RAK1921 SSD1306 OLED on I2C) ───── // ── Display (optional RAK1921 SSD1306 OLED on I2C) ─────
let twim_buf = mcu::alloc_i2c_buffer(); let twim_buf = mcu::alloc_i2c_buffer();
let i2c = embassy_nrf::twim::Twim::new( let i2c = embassy_nrf::twim::Twim::new(
p.TWISPI0, mcu::Irqs, p.P0_13, p.P0_14, twim::Config::default(), twim_buf, p.TWISPI0,
mcu::Irqs,
p.P0_13,
p.P0_14,
twim::Config::default(),
twim_buf,
); );
let display = Some(DisplayParts { i2c }); let display = Some(DisplayParts { i2c });
+3 -1
View File
@@ -48,7 +48,9 @@ pub trait LoRaBoard: Sized {
fn mac_address() -> [u8; 6]; fn mac_address() -> [u8; 6];
/// Decompose initialized board into peripheral bundles for each task. /// Decompose initialized board into peripheral bundles for each task.
fn into_parts(self) -> BoardParts<Self::RadioParts, Self::CommParts, Self::DisplayParts, Self::LedDriver>; fn into_parts(
self,
) -> BoardParts<Self::RadioParts, Self::CommParts, Self::DisplayParts, Self::LedDriver>;
} }
/// Peripheral bundles for each firmware task, produced by [`LoRaBoard::into_parts`]. /// Peripheral bundles for each firmware task, produced by [`LoRaBoard::into_parts`].
+12 -3
View File
@@ -21,7 +21,8 @@ pub type LedDriver = crate::driver::simple_led::SimpleLed<Output<'static>>;
// Display types — never instantiated (no OLED on this board), but the // Display types — never instantiated (no OLED on this board), but the
// trait requires concrete associated types that satisfy the bounds. // trait requires concrete associated types that satisfy the bounds.
pub type DisplayI2c = embassy_rp::i2c::I2c<'static, embassy_rp::peripherals::I2C0, embassy_rp::i2c::Async>; pub type DisplayI2c =
embassy_rp::i2c::I2c<'static, embassy_rp::peripherals::I2C0, embassy_rp::i2c::Async>;
pub type DisplayDriver = ssd1306::Ssd1306Async< pub type DisplayDriver = ssd1306::Ssd1306Async<
ssd1306::prelude::I2CInterface<DisplayI2c>, ssd1306::prelude::I2CInterface<DisplayI2c>,
ssd1306::size::DisplaySize128x64, ssd1306::size::DisplaySize128x64,
@@ -78,7 +79,8 @@ impl LoRaBoard for Board {
let p = self.p; let p = self.p;
// ── MAC address from flash unique ID ─────────────────── // ── MAC address from flash unique ID ───────────────────
let mut flash = embassy_rp::flash::Flash::<_, _, { 2 * 1024 * 1024 }>::new_blocking(p.FLASH); let mut flash =
embassy_rp::flash::Flash::<_, _, { 2 * 1024 * 1024 }>::new_blocking(p.FLASH);
mcu::init_mac(&mut flash); mcu::init_mac(&mut flash);
// ── SPI1 bus for SX1262 ──────────────────────────────── // ── SPI1 bus for SX1262 ────────────────────────────────
@@ -92,7 +94,14 @@ impl LoRaBoard for Board {
let mut spi_cfg = spi::Config::default(); let mut spi_cfg = spi::Config::default();
spi_cfg.frequency = 1_000_000; spi_cfg.frequency = 1_000_000;
let spi = spi::Spi::new( let spi = spi::Spi::new(
p.SPI1, p.PIN_14, p.PIN_15, p.PIN_24, p.DMA_CH0, p.DMA_CH1, mcu::Irqs, spi_cfg, p.SPI1,
p.PIN_14,
p.PIN_15,
p.PIN_24,
p.DMA_CH0,
p.DMA_CH1,
mcu::Irqs,
spi_cfg,
); );
let spi_bus = mcu::share_spi_bus(spi); let spi_bus = mcu::share_spi_bus(spi);
+6 -1
View File
@@ -128,7 +128,12 @@ impl LoRaBoard for Board {
// ── Display (SH1106 OLED on I2C) ──────────────────────── // ── Display (SH1106 OLED on I2C) ────────────────────────
let twim_buf = mcu::alloc_i2c_buffer(); let twim_buf = mcu::alloc_i2c_buffer();
let i2c = embassy_nrf::twim::Twim::new( let i2c = embassy_nrf::twim::Twim::new(
p.TWISPI0, mcu::Irqs, p.P0_06, p.P0_05, twim::Config::default(), twim_buf, p.TWISPI0,
mcu::Irqs,
p.P0_06,
p.P0_05,
twim::Config::default(),
twim_buf,
); );
let display = Some(DisplayParts { i2c }); let display = Some(DisplayParts { i2c });
+43 -40
View File
@@ -236,7 +236,7 @@ pub async fn display_task(
mod render { mod render {
use core::fmt::Write; use core::fmt::Write;
use embedded_graphics::mono_font::ascii::{FONT_6X10, FONT_9X15_BOLD}; use embedded_graphics::mono_font::ascii::{FONT_6X10, FONT_9X15_BOLD};
use embedded_graphics::mono_font::MonoTextStyle; use embedded_graphics::mono_font::MonoTextStyle;
use embedded_graphics::pixelcolor::BinaryColor; use embedded_graphics::pixelcolor::BinaryColor;
@@ -245,31 +245,31 @@ mod render {
use embedded_graphics::text::renderer::CharacterStyle; use embedded_graphics::text::renderer::CharacterStyle;
use embedded_graphics::text::{Alignment, Text}; use embedded_graphics::text::{Alignment, Text};
use heapless::String; use heapless::String;
use crate::channel::{RadioState, RadioStatus}; use crate::channel::{RadioState, RadioStatus};
use crate::protocol::Bandwidth; use crate::protocol::Bandwidth;
/// RSSI ring buffer length — supports displays up to 256px wide (2px per bar). /// RSSI ring buffer length — supports displays up to 256px wide (2px per bar).
pub const RSSI_HISTORY_LEN: usize = 128; pub const RSSI_HISTORY_LEN: usize = 128;
// Font metrics (FONT_6X10) // Font metrics (FONT_6X10)
const CHAR_W: i32 = 6; const CHAR_W: i32 = 6;
const FONT_H: i32 = 10; const FONT_H: i32 = 10;
// Mode box: 2 chars + 1px padding each side // Mode box: 2 chars + 1px padding each side
const MODE_BOX_W: i32 = 2 * CHAR_W + 2; // 14px const MODE_BOX_W: i32 = 2 * CHAR_W + 2; // 14px
// RSSI mapping range (dBm) // RSSI mapping range (dBm)
const RSSI_MIN: i16 = -120; const RSSI_MIN: i16 = -120;
const RSSI_MAX: i16 = 0; const RSSI_MAX: i16 = 0;
/// Static board identity info for display rendering. /// Static board identity info for display rendering.
pub struct BoardInfo<'a> { pub struct BoardInfo<'a> {
pub name: &'a str, pub name: &'a str,
pub version: &'a str, pub version: &'a str,
pub mac: &'a str, pub mac: &'a str,
} }
/// Render the active dashboard (shown when radio is in RX or TX mode). /// Render the active dashboard (shown when radio is in RX or TX mode).
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn dashboard( pub fn dashboard(
@@ -294,16 +294,16 @@ mod render {
let spark_top = sep2_y + 2; // 1px line + 1px gap above graph let spark_top = sep2_y + 2; // 1px line + 1px gap above graph
let spark_h = h - spark_top; let spark_h = h - spark_top;
let visible_bars = ((w / 2) as usize).min(RSSI_HISTORY_LEN); let visible_bars = ((w / 2) as usize).min(RSSI_HISTORY_LEN);
let _ = target.clear(BinaryColor::Off); let _ = target.clear(BinaryColor::Off);
let style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On); let style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On);
let mut buf: String<32> = String::new(); let mut buf: String<32> = String::new();
// ── Mode box (left column, 2 rows tall) ───────────────────────── // ── Mode box (left column, 2 rows tall) ─────────────────────────
let fill = PrimitiveStyle::with_fill(BinaryColor::On); let fill = PrimitiveStyle::with_fill(BinaryColor::On);
let mut inv_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::Off); let mut inv_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::Off);
inv_style.set_background_color(Some(BinaryColor::On)); inv_style.set_background_color(Some(BinaryColor::On));
match status.state { match status.state {
RadioState::Receiving => { RadioState::Receiving => {
// "RX" inverted on row 0 // "RX" inverted on row 0
@@ -333,16 +333,16 @@ mod render {
} }
RadioState::Idle => {} // Not shown on active screen RadioState::Idle => {} // Not shown on active screen
} }
// Vertical separator between mode box and title box // Vertical separator between mode box and title box
Line::new(Point::new(MODE_BOX_W, 0), Point::new(MODE_BOX_W, sep1_y)) Line::new(Point::new(MODE_BOX_W, 0), Point::new(MODE_BOX_W, sep1_y))
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1)) .into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
.draw(target) .draw(target)
.ok(); .ok();
// ── Title box (right of mode box) ─────────────────────────────── // ── Title box (right of mode box) ───────────────────────────────
let title_center_x = title_x + title_w / 2; let title_center_x = title_x + title_w / 2;
// Row 0: "DongLoRa v{version}" // Row 0: "DongLoRa v{version}"
buf.clear(); buf.clear();
let _ = write!(buf, "DongLoRa v{}", board.version); let _ = write!(buf, "DongLoRa v{}", board.version);
@@ -354,7 +354,7 @@ mod render {
) )
.draw(target) .draw(target)
.ok(); .ok();
// Row 1: radio settings "{freq}/{bw}/{sf}/{cr}" // Row 1: radio settings "{freq}/{bw}/{sf}/{cr}"
if let Some(cfg) = status.config { if let Some(cfg) = status.config {
buf.clear(); buf.clear();
@@ -386,16 +386,16 @@ mod render {
.draw(target) .draw(target)
.ok(); .ok();
} }
// ── First separator line ──────────────────────────────────────── // ── First separator line ────────────────────────────────────────
Line::new(Point::new(0, sep1_y), Point::new(w - 1, sep1_y)) Line::new(Point::new(0, sep1_y), Point::new(w - 1, sep1_y))
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1)) .into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
.draw(target) .draw(target)
.ok(); .ok();
// ── Info rows (centered) ──────────────────────────────────────── // ── Info rows (centered) ────────────────────────────────────────
let center_x = w / 2; let center_x = w / 2;
// Row 2: packet counters // Row 2: packet counters
buf.clear(); buf.clear();
let _ = write!( let _ = write!(
@@ -412,7 +412,7 @@ mod render {
) )
.draw(target) .draw(target)
.ok(); .ok();
// Row 3: RSSI + SNR // Row 3: RSSI + SNR
buf.clear(); buf.clear();
match (status.last_rssi, status.last_snr) { match (status.last_rssi, status.last_snr) {
@@ -434,13 +434,13 @@ mod render {
) )
.draw(target) .draw(target)
.ok(); .ok();
// ── Second separator line ─────────────────────────────────────── // ── Second separator line ───────────────────────────────────────
Line::new(Point::new(0, sep2_y), Point::new(w - 1, sep2_y)) Line::new(Point::new(0, sep2_y), Point::new(w - 1, sep2_y))
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1)) .into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
.draw(target) .draw(target)
.ok(); .ok();
// ── Sparkline bar graph ───────────────────────────────────────── // ── Sparkline bar graph ─────────────────────────────────────────
if spark_h > 0 { if spark_h > 0 {
rssi_sparkline( rssi_sparkline(
@@ -456,19 +456,19 @@ mod render {
); );
} }
} }
/// Render the splash/waiting screen (shown when idle or no config). /// Render the splash/waiting screen (shown when idle or no config).
pub fn splash(target: &mut impl DrawTarget<Color = BinaryColor>, board: &BoardInfo) { pub fn splash(target: &mut impl DrawTarget<Color = BinaryColor>, board: &BoardInfo) {
let bb = target.bounding_box(); let bb = target.bounding_box();
let w = bb.size.width as i32; let w = bb.size.width as i32;
let _ = target.clear(BinaryColor::Off); let _ = target.clear(BinaryColor::Off);
let style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On); let style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On);
let title_style = MonoTextStyle::new(&FONT_9X15_BOLD, BinaryColor::On); let title_style = MonoTextStyle::new(&FONT_9X15_BOLD, BinaryColor::On);
let mut buf: String<32> = String::new(); let mut buf: String<32> = String::new();
let center_x = w / 2; let center_x = w / 2;
// Row 1: "DongLoRa" bold left, version small right // Row 1: "DongLoRa" bold left, version small right
Text::new("DongLoRa", Point::new(4, 15), title_style) Text::new("DongLoRa", Point::new(4, 15), title_style)
.draw(target) .draw(target)
@@ -478,7 +478,7 @@ mod render {
Text::with_alignment(&buf, Point::new(w - 2, 15), style, Alignment::Right) Text::with_alignment(&buf, Point::new(w - 2, 15), style, Alignment::Right)
.draw(target) .draw(target)
.ok(); .ok();
// Row 2: board name // Row 2: board name
Text::with_alignment( Text::with_alignment(
board.name, board.name,
@@ -488,7 +488,7 @@ mod render {
) )
.draw(target) .draw(target)
.ok(); .ok();
// Row 3: MAC address // Row 3: MAC address
Text::with_alignment( Text::with_alignment(
board.mac, board.mac,
@@ -498,7 +498,7 @@ mod render {
) )
.draw(target) .draw(target)
.ok(); .ok();
// Row 4: status // Row 4: status
Text::with_alignment( Text::with_alignment(
"Waiting for host...", "Waiting for host...",
@@ -509,7 +509,7 @@ mod render {
.draw(target) .draw(target)
.ok(); .ok();
} }
/// Format a u32 count compactly for display. /// Format a u32 count compactly for display.
fn compact_count(n: u32) -> String<10> { fn compact_count(n: u32) -> String<10> {
let mut s: String<10> = String::new(); let mut s: String<10> = String::new();
@@ -522,7 +522,7 @@ mod render {
} }
s s
} }
/// Render the RSSI history as a bar-chart sparkline. /// Render the RSSI history as a bar-chart sparkline.
/// ///
/// The current (uncommitted) slot is rendered at the rightmost position /// The current (uncommitted) slot is rendered at the rightmost position
@@ -547,13 +547,13 @@ mod render {
committed.min(visible_bars) committed.min(visible_bars)
}; };
let total = hist_slots + if live { 1 } else { 0 }; let total = hist_slots + if live { 1 } else { 0 };
if total == 0 { if total == 0 {
return; return;
} }
let fill = PrimitiveStyle::with_fill(BinaryColor::On); let fill = PrimitiveStyle::with_fill(BinaryColor::On);
// Draw committed history // Draw committed history
for i in 0..hist_slots { for i in 0..hist_slots {
let idx = if count <= RSSI_HISTORY_LEN { let idx = if count <= RSSI_HISTORY_LEN {
@@ -563,19 +563,22 @@ mod render {
i + committed.saturating_sub(effective_bars) i + committed.saturating_sub(effective_bars)
} else { } else {
let start = count - RSSI_HISTORY_LEN; let start = count - RSSI_HISTORY_LEN;
let skip = let skip = RSSI_HISTORY_LEN.saturating_sub(if live {
RSSI_HISTORY_LEN.saturating_sub(if live { visible_bars - 1 } else { visible_bars }); visible_bars - 1
} else {
visible_bars
});
(start + skip + i) % RSSI_HISTORY_LEN (start + skip + i) % RSSI_HISTORY_LEN
}; };
let is_tx = tx_history[idx]; let is_tx = tx_history[idx];
let rssi = history[idx]; let rssi = history[idx];
if let Some(bar_h) = bar_height(rssi, is_tx, spark_h) { if let Some(bar_h) = bar_height(rssi, is_tx, spark_h) {
let x = (visible_bars - total + i) as i32 * 2; let x = (visible_bars - total + i) as i32 * 2;
draw_bar(target, x, bar_h, is_tx, spark_top, spark_h, &fill); draw_bar(target, x, bar_h, is_tx, spark_top, spark_h, &fill);
} }
} }
// Draw live (current) bar at the rightmost position // Draw live (current) bar at the rightmost position
if live { if live {
if let Some(bar_h) = bar_height(current_rssi, current_tx, spark_h) { if let Some(bar_h) = bar_height(current_rssi, current_tx, spark_h) {
@@ -584,7 +587,7 @@ mod render {
} }
} }
} }
/// Compute the pixel height for a sparkline bar, or None to skip. /// Compute the pixel height for a sparkline bar, or None to skip.
fn bar_height(rssi: i16, is_tx: bool, spark_h: i32) -> Option<i32> { fn bar_height(rssi: i16, is_tx: bool, spark_h: i32) -> Option<i32> {
let h = if rssi <= RSSI_MIN { let h = if rssi <= RSSI_MIN {
@@ -603,7 +606,7 @@ mod render {
Some(h) Some(h)
} }
} }
/// Draw a single sparkline bar (solid for RX, dotted for TX). /// Draw a single sparkline bar (solid for RX, dotted for TX).
fn draw_bar( fn draw_bar(
target: &mut impl DrawTarget<Color = BinaryColor>, target: &mut impl DrawTarget<Color = BinaryColor>,
@@ -631,7 +634,7 @@ mod render {
.ok(); .ok();
} }
} }
/// Clear the display (display-off state). /// Clear the display (display-off state).
pub fn blank(target: &mut impl DrawTarget<Color = BinaryColor>) { pub fn blank(target: &mut impl DrawTarget<Color = BinaryColor>) {
let _ = target.clear(BinaryColor::Off); let _ = target.clear(BinaryColor::Off);
+8 -1
View File
@@ -1,5 +1,12 @@
#[cfg(any(feature = "wio_tracker_l1", feature = "elecrow_thinknode_m2"))] #[cfg(any(feature = "wio_tracker_l1", feature = "elecrow_thinknode_m2"))]
pub mod sh1106; pub mod sh1106;
#[cfg(any(feature = "heltec_v3", feature = "heltec_v3_uart", feature = "heltec_v4", feature = "elecrow_thinknode_m2", feature = "wio_tracker_l1", feature = "waveshare_rp2040_lora"))] #[cfg(any(
feature = "heltec_v3",
feature = "heltec_v3_uart",
feature = "heltec_v4",
feature = "elecrow_thinknode_m2",
feature = "wio_tracker_l1",
feature = "waveshare_rp2040_lora"
))]
pub mod simple_led; pub mod simple_led;
+3 -4
View File
@@ -73,9 +73,9 @@ impl<I: I2c> Sh1106<I> {
for page in 0..PAGES { for page in 0..PAGES {
// Set page address and column start (with SH1106 2-column offset). // Set page address and column start (with SH1106 2-column offset).
self.cmd(&[ self.cmd(&[
0xB0 | page as u8, // page address 0xB0 | page as u8, // page address
COL_OFFSET & 0x0F, // column low nibble COL_OFFSET & 0x0F, // column low nibble
0x10 | (COL_OFFSET >> 4), // column high nibble 0x10 | (COL_OFFSET >> 4), // column high nibble
]) ])
.await?; .await?;
@@ -90,7 +90,6 @@ impl<I: I2c> Sh1106<I> {
pub async fn set_brightness(&mut self, value: u8) -> Result<(), I::Error> { pub async fn set_brightness(&mut self, value: u8) -> Result<(), I::Error> {
self.cmd(&[0x81, value]).await self.cmd(&[0x81, value]).await
} }
} }
impl<I: I2c> DrawTarget for Sh1106<I> { impl<I: I2c> DrawTarget for Sh1106<I> {
+6 -1
View File
@@ -3,7 +3,12 @@
//! Each submodule provides low-level bus and peripheral init for one MCU family. //! Each submodule provides low-level bus and peripheral init for one MCU family.
//! Board files call these with specific pins and construct higher-level drivers. //! Board files call these with specific pins and construct higher-level drivers.
#[cfg(any(feature = "heltec_v3", feature = "heltec_v3_uart", feature = "heltec_v4", feature = "elecrow_thinknode_m2"))] #[cfg(any(
feature = "heltec_v3",
feature = "heltec_v3_uart",
feature = "heltec_v4",
feature = "elecrow_thinknode_m2"
))]
pub mod esp32s3; pub mod esp32s3;
#[cfg(any(feature = "rak_wisblock_4631", feature = "wio_tracker_l1"))] #[cfg(any(feature = "rak_wisblock_4631", feature = "wio_tracker_l1"))]
+1 -3
View File
@@ -25,9 +25,7 @@ pub type UsbNrfDriver =
// ── SPI bus sharing ───────────────────────────────────────────────── // ── SPI bus sharing ─────────────────────────────────────────────────
/// Wrap an initialized SPI peripheral in a shared bus (StaticCell + Mutex). /// Wrap an initialized SPI peripheral in a shared bus (StaticCell + Mutex).
pub fn share_spi_bus( pub fn share_spi_bus(spi: SpiBus) -> &'static embassy_sync::mutex::Mutex<NoopRawMutex, SpiBus> {
spi: SpiBus,
) -> &'static embassy_sync::mutex::Mutex<NoopRawMutex, SpiBus> {
static SPI_BUS: StaticCell<embassy_sync::mutex::Mutex<NoopRawMutex, SpiBus>> = static SPI_BUS: StaticCell<embassy_sync::mutex::Mutex<NoopRawMutex, SpiBus>> =
StaticCell::new(); StaticCell::new();
SPI_BUS.init(embassy_sync::mutex::Mutex::new(spi)) SPI_BUS.init(embassy_sync::mutex::Mutex::new(spi))
+10 -8
View File
@@ -17,15 +17,14 @@ bind_interrupts!(pub struct Irqs {
// ── MCU-level types ──────────────────────────────────────────────── // ── MCU-level types ────────────────────────────────────────────────
pub type SpiBus = embassy_rp::spi::Spi<'static, embassy_rp::peripherals::SPI1, embassy_rp::spi::Async>; pub type SpiBus =
embassy_rp::spi::Spi<'static, embassy_rp::peripherals::SPI1, embassy_rp::spi::Async>;
pub type UsbRpDriver = embassy_rp::usb::Driver<'static, embassy_rp::peripherals::USB>; pub type UsbRpDriver = embassy_rp::usb::Driver<'static, embassy_rp::peripherals::USB>;
// ── SPI bus sharing ──────────────────────────────────────────────── // ── SPI bus sharing ────────────────────────────────────────────────
/// Wrap an initialized SPI peripheral in a shared bus (StaticCell + Mutex). /// Wrap an initialized SPI peripheral in a shared bus (StaticCell + Mutex).
pub fn share_spi_bus( pub fn share_spi_bus(spi: SpiBus) -> &'static embassy_sync::mutex::Mutex<NoopRawMutex, SpiBus> {
spi: SpiBus,
) -> &'static embassy_sync::mutex::Mutex<NoopRawMutex, SpiBus> {
static SPI_BUS: StaticCell<embassy_sync::mutex::Mutex<NoopRawMutex, SpiBus>> = static SPI_BUS: StaticCell<embassy_sync::mutex::Mutex<NoopRawMutex, SpiBus>> =
StaticCell::new(); StaticCell::new();
SPI_BUS.init(embassy_sync::mutex::Mutex::new(spi)) SPI_BUS.init(embassy_sync::mutex::Mutex::new(spi))
@@ -42,7 +41,12 @@ static mut MAC_REF: Option<&'static [u8; 6]> = None;
/// ///
/// Must be called once during board init before `mac_address()`. /// Must be called once during board init before `mac_address()`.
pub fn init_mac<const FLASH_SIZE: usize>( pub fn init_mac<const FLASH_SIZE: usize>(
flash: &mut embassy_rp::flash::Flash<'_, embassy_rp::peripherals::FLASH, embassy_rp::flash::Blocking, FLASH_SIZE>, flash: &mut embassy_rp::flash::Flash<
'_,
embassy_rp::peripherals::FLASH,
embassy_rp::flash::Blocking,
FLASH_SIZE,
>,
) { ) {
let mut uid = [0u8; 8]; let mut uid = [0u8; 8];
flash.blocking_unique_id(&mut uid).expect("flash unique ID"); flash.blocking_unique_id(&mut uid).expect("flash unique ID");
@@ -72,7 +76,5 @@ pub fn init_mac<const FLASH_SIZE: usize>(
pub fn mac_address() -> [u8; 6] { pub fn mac_address() -> [u8; 6] {
// SAFETY: MAC_REF is set once during init before the executor starts, // SAFETY: MAC_REF is set once during init before the executor starts,
// and only read (never written) after that. // and only read (never written) after that.
unsafe { unsafe { *MAC_REF.expect("init_mac() must be called before mac_address()") }
*MAC_REF.expect("init_mac() must be called before mac_address()")
}
} }
+1 -2
View File
@@ -44,8 +44,7 @@ impl CobsDecoder {
// End of COBS frame — decode and dispatch // End of COBS frame — decode and dispatch
if self.len > 0 { if self.len > 0 {
let mut decode_buf = [0u8; MAX_FRAME]; let mut decode_buf = [0u8; MAX_FRAME];
if let Some(decoded_len) = if let Some(decoded_len) = ucobs::decode(&self.buf[..self.len], &mut decode_buf)
ucobs::decode(&self.buf[..self.len], &mut decode_buf)
{ {
if let Some(cmd) = Command::from_bytes(&decode_buf[..decoded_len]) { if let Some(cmd) = Command::from_bytes(&decode_buf[..decoded_len]) {
on_command(cmd); on_command(cmd);
+7 -2
View File
@@ -6,8 +6,8 @@ use defmt::warn;
use embassy_executor::task; use embassy_executor::task;
use embedded_io_async::Write; use embedded_io_async::Write;
use crate::channel::{CommandChannel, DisplayCommand, DisplayCommandChannel, ResponseChannel};
use super::framing::{self, CobsDecoder, MAX_FRAME}; use super::framing::{self, CobsDecoder, MAX_FRAME};
use crate::channel::{CommandChannel, DisplayCommand, DisplayCommandChannel, ResponseChannel};
#[task] #[task]
pub async fn host_task( pub async fn host_task(
@@ -56,7 +56,12 @@ pub async fn host_task(
}); });
for cmd in cmds { for cmd in cmds {
framing::route_command( framing::route_command(
cmd, commands, responses, display_commands, has_display, mac, cmd,
commands,
responses,
display_commands,
has_display,
mac,
) )
.await; .await;
} }
+16 -3
View File
@@ -7,9 +7,9 @@ use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State};
use embassy_usb::Builder; use embassy_usb::Builder;
use static_cell::StaticCell; use static_cell::StaticCell;
use super::framing::{self, CobsDecoder, MAX_FRAME};
use crate::channel::{CommandChannel, DisplayCommand, DisplayCommandChannel, ResponseChannel}; use crate::channel::{CommandChannel, DisplayCommand, DisplayCommandChannel, ResponseChannel};
use crate::protocol::Command; use crate::protocol::Command;
use super::framing::{self, CobsDecoder, MAX_FRAME};
const MAX_PACKET: usize = 64; const MAX_PACKET: usize = 64;
@@ -59,7 +59,15 @@ pub async fn host_task(
join( join(
usb_dev.run(), usb_dev.run(),
protocol_loop(sender, receiver, commands, responses, display_commands, has_display, mac), protocol_loop(
sender,
receiver,
commands,
responses,
display_commands,
has_display,
mac,
),
) )
.await; .await;
} }
@@ -106,7 +114,12 @@ async fn protocol_loop<'d, D: embassy_usb_driver::Driver<'d>>(
}); });
for cmd in cmds { for cmd in cmds {
framing::route_command( framing::route_command(
cmd, commands, responses, display_commands, has_display, mac, cmd,
commands,
responses,
display_commands,
has_display,
mac,
) )
.await; .await;
} }
+1 -2
View File
@@ -80,8 +80,7 @@ async fn run(spawner: Spawner) {
let parts = board.into_parts(); let parts = board.into_parts();
spawner.spawn( spawner.spawn(
radio::radio_task(parts.radio, &COMMANDS, &RESPONSES, &STATUS) radio::radio_task(parts.radio, &COMMANDS, &RESPONSES, &STATUS).expect("spawn radio_task"),
.expect("spawn radio_task"),
); );
spawner.spawn( spawner.spawn(