refactor: extract hal/ and driver/ modules

- Created hal/esp32s3.rs with MCU primitives (SPI, I2C, USB, UART, timer, MAC)
- Created hal/nrf52840.rs with shared nRF boilerplate (interrupts, bus wrapping, VBUS, MAC)
- Moved SH1106 OLED driver from display/ to driver/ (clear separation from app code)
- Board esp32s3.rs now delegates MCU init to hal, keeps only peripheral wiring (RadioParts)
- nRF board files use hal for shared StaticCell patterns, mac_address, interrupt bindings
- Board files still own pin assignments and peripheral construction
This commit is contained in:
swaits committed 2026-04-07 09:38:56 -06:00
1 parent 8bdaa72bc3
commit fb334118fd
13 files changed
+300 -252

No files matched your search

+23 -59
View File
@@ -1,40 +1,27 @@
use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull};
use embassy_nrf::spim::{self, Spim};
use embassy_nrf::twim::{self, Twim};
use embassy_nrf::twim;
use embassy_nrf::usb::Driver;
use embassy_nrf::{bind_interrupts, Peripherals};
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_time::Delay;
use static_cell::StaticCell;
use lora_phy::iv::GenericSx126xInterfaceVariant;
use lora_phy::sx126x::{self, Sx1262, Sx126x};
use super::traits::LoRaBoard;
use crate::hal::nrf52840 as mcu;
bind_interrupts!(struct Irqs {
USBD => embassy_nrf::usb::InterruptHandler<embassy_nrf::peripherals::USBD>;
SPIM3 => embassy_nrf::spim::InterruptHandler<embassy_nrf::peripherals::SPI3>;
TWISPI0 => embassy_nrf::twim::InterruptHandler<embassy_nrf::peripherals::TWISPI0>;
});
// ── Concrete peripheral types ───────────────────────────────────────
// ── Concrete peripheral types ────────────────────────────────────────
type SpiBus = Spim<'static>;
type Nss = Output<'static>;
type Iv = GenericSx126xInterfaceVariant<Output<'static>, Input<'static>>;
type RadioSpiDevice = SpiDevice<'static, NoopRawMutex, SpiBus, Nss>;
/// SX126x driver (pre-LoRa construction — LoRa::new is async).
type RadioSpiDevice = SpiDevice<'static, NoopRawMutex, mcu::SpiBus, Nss>;
pub type RadioDriver = Sx126x<RadioSpiDevice, Iv, Sx1262>;
pub type UsbDriver = mcu::UsbNrfDriver;
pub type DisplayI2c = mcu::I2cBus;
/// USB driver for CDC-ACM.
pub type UsbDriver = Driver<'static, &'static embassy_nrf::usb::vbus_detect::SoftwareVbusDetect>;
/// I2C bus for SH1106 OLED.
pub type DisplayI2c = Twim<'static>;
// ── Peripheral bundles ───────────────────────────────────────────────
// ── Peripheral bundles ──────────────────────────────────────────────
pub struct RadioParts {
pub driver: RadioDriver,
@@ -50,13 +37,13 @@ pub struct DisplayParts {
pub mac: [u8; 6],
}
// ── Board init ───────────────────────────────────────────────────────
// ── Board init ──────────────────────────────────────────────────────
pub struct Board {
p: Peripherals,
p: embassy_nrf::Peripherals,
}
impl super::traits::LoRaBoard for Board {
impl LoRaBoard for Board {
const NAME: &'static str = "Wio Tracker L1";
const TX_POWER_RANGE: (i8, i8) = (-9, 22);
@@ -69,23 +56,7 @@ impl super::traits::LoRaBoard for Board {
}
fn mac_address() -> [u8; 6] {
// SAFETY: 0x10000000 is the nRF52840 FICR (Factory Information Configuration
// Registers) base address. Offsets 0x0A4 and 0x0A8 are the DEVICEADDR[0] and
// DEVICEADDR[1] registers containing the factory-programmed device address.
// read_volatile is correct for memory-mapped hardware registers.
unsafe {
let ficr = 0x10000000 as *const u32;
let addr0 = core::ptr::read_volatile(ficr.byte_add(0x0A4));
let addr1 = core::ptr::read_volatile(ficr.byte_add(0x0A8));
[
addr0 as u8,
(addr0 >> 8) as u8,
(addr0 >> 16) as u8,
(addr0 >> 24) as u8,
addr1 as u8,
(addr1 >> 8) as u8,
]
}
mcu::mac_address()
}
}
@@ -93,19 +64,16 @@ impl Board {
pub fn into_parts(self) -> (RadioParts, UsbParts, Option<DisplayParts>) {
let p = self.p;
// ── SPI bus for SX1262 ───────────────────────────────────
// ── SPI bus for SX1262 ──────────────────────────────────
let mut spi_cfg = spim::Config::default();
spi_cfg.frequency = spim::Frequency::M1;
let spi = Spim::new(p.SPI3, Irqs, p.P0_30, p.P0_03, p.P0_28, spi_cfg);
static SPI_BUS: StaticCell<embassy_sync::mutex::Mutex<NoopRawMutex, SpiBus>> =
StaticCell::new();
let spi_bus = SPI_BUS.init(embassy_sync::mutex::Mutex::new(spi));
let spi = Spim::new(p.SPI3, mcu::Irqs, p.P0_30, p.P0_03, p.P0_28, spi_cfg);
let spi_bus = mcu::share_spi_bus(spi);
let nss = Output::new(p.P1_14, Level::High, OutputDrive::Standard);
let spi_device = SpiDevice::new(spi_bus, nss);
// ── SX1262 control pins ──────────────────────────────────
// ── SX1262 control pins ─────────────────────────────────
let reset = Output::new(p.P1_07, Level::High, OutputDrive::Standard);
let dio1 = Input::new(p.P0_07, Pull::Down);
let busy = Input::new(p.P1_10, Pull::Down);
@@ -127,25 +95,21 @@ impl Board {
delay: Delay,
};
// ── USB ──────────────────────────────────────────────────
static VBUS: StaticCell<embassy_nrf::usb::vbus_detect::SoftwareVbusDetect> =
StaticCell::new();
let vbus = VBUS.init(embassy_nrf::usb::vbus_detect::SoftwareVbusDetect::new(
true, true,
));
// ── USB ─────────────────────────────────────────────────
let vbus = mcu::alloc_vbus_detect(true, true);
let usb = UsbParts {
driver: Driver::new(p.USBD, Irqs, vbus),
driver: Driver::new(p.USBD, mcu::Irqs, vbus),
};
// ── BAT_CTL: P0.04, active HIGH — enables power to OLED and battery sense
let bat_ctl = Output::new(p.P0_04, Level::High, OutputDrive::Standard);
core::mem::forget(bat_ctl);
// ── Display (SH1106 OLED on I2C) ─────────────────────────
static TWIM_BUF: StaticCell<[u8; 256]> = StaticCell::new();
let twim_buf = TWIM_BUF.init([0u8; 256]);
let i2c_cfg = twim::Config::default();
let i2c = Twim::new(p.TWISPI0, Irqs, p.P0_06, p.P0_05, i2c_cfg, twim_buf);
// ── Display (SH1106 OLED on I2C) ────────────────────────
let twim_buf = mcu::alloc_i2c_buffer();
let i2c = embassy_nrf::twim::Twim::new(
p.TWISPI0, mcu::Irqs, p.P0_06, p.P0_05, twim::Config::default(), twim_buf,
);
let mac = Self::mac_address();
let display = Some(DisplayParts { i2c, mac });