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:
13 files changed
+300
-252
No files matched your search
+14
-113
@@ -1,42 +1,30 @@
|
|||||||
//! Shared types and initialization helpers for ESP32-S3 boards.
|
//! Shared types and peripheral wiring for ESP32-S3 boards.
|
||||||
//!
|
//!
|
||||||
//! All ESP32-S3 boards in this project share the same SPI/DMA, USB OTG,
|
//! Delegates MCU-level init to `hal::esp32s3` and defines peripheral-level
|
||||||
//! and I2C peripheral wiring. This module provides the common type aliases,
|
//! types (RadioDriver, Parts structs) used by ESP32-S3 board files.
|
||||||
//! peripheral bundle structs, and init functions. Each board file re-exports
|
|
||||||
//! these types and calls the helpers from its own `Board::into_parts()`.
|
|
||||||
|
|
||||||
use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
|
use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
|
||||||
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||||
use embassy_time::Delay;
|
use embassy_time::Delay;
|
||||||
use esp_hal::dma::{DmaRxBuf, DmaTxBuf};
|
|
||||||
use esp_hal::gpio::{Input, InputConfig, Level, Output, OutputConfig, Pull};
|
use esp_hal::gpio::{Input, InputConfig, Level, Output, OutputConfig, Pull};
|
||||||
use esp_hal::i2c::master::{Config as I2cConfig, I2c};
|
|
||||||
use esp_hal::spi::master::{Config as SpiConfig, Spi, SpiDmaBus};
|
|
||||||
use esp_hal::spi::Mode as SpiMode;
|
|
||||||
use esp_hal::time::Rate;
|
|
||||||
use esp_hal::timer::timg::TimerGroup;
|
|
||||||
use static_cell::StaticCell;
|
|
||||||
|
|
||||||
use lora_phy::iv::GenericSx126xInterfaceVariant;
|
use lora_phy::iv::GenericSx126xInterfaceVariant;
|
||||||
use lora_phy::sx126x::{self, Sx1262, Sx126x};
|
use lora_phy::sx126x::{self, Sx1262, Sx126x};
|
||||||
|
|
||||||
// ── Concrete peripheral types ────────────────────────────────────────
|
use crate::hal::esp32s3 as mcu;
|
||||||
|
|
||||||
|
// ── Re-export MCU types ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
pub use mcu::{I2cBus as DisplayI2c, SpiBus, UartDriver, UsbOtgDriver as UsbDriver};
|
||||||
|
|
||||||
|
// ── Concrete peripheral types ───────────────────────────────────────
|
||||||
|
|
||||||
type SpiBus = SpiDmaBus<'static, esp_hal::Async>;
|
|
||||||
type Nss = Output<'static>;
|
type Nss = Output<'static>;
|
||||||
type Iv = GenericSx126xInterfaceVariant<Output<'static>, Input<'static>>;
|
type Iv = GenericSx126xInterfaceVariant<Output<'static>, Input<'static>>;
|
||||||
type RadioSpiDevice = SpiDevice<'static, NoopRawMutex, SpiBus, Nss>;
|
type RadioSpiDevice = SpiDevice<'static, NoopRawMutex, SpiBus, Nss>;
|
||||||
pub type RadioDriver = Sx126x<RadioSpiDevice, Iv, Sx1262>;
|
pub type RadioDriver = Sx126x<RadioSpiDevice, Iv, Sx1262>;
|
||||||
|
|
||||||
#[cfg(not(feature = "heltec_v3"))]
|
// ── Peripheral bundles ──────────────────────────────────────────────
|
||||||
pub type UsbDriver = esp_hal::otg_fs::asynch::Driver<'static>;
|
|
||||||
|
|
||||||
#[cfg(feature = "heltec_v3")]
|
|
||||||
pub type UartDriver = esp_hal::uart::Uart<'static, esp_hal::Async>;
|
|
||||||
|
|
||||||
pub type DisplayI2c = I2c<'static, esp_hal::Async>;
|
|
||||||
|
|
||||||
// ── Peripheral bundles ───────────────────────────────────────────────
|
|
||||||
|
|
||||||
pub struct RadioParts {
|
pub struct RadioParts {
|
||||||
pub driver: RadioDriver,
|
pub driver: RadioDriver,
|
||||||
@@ -58,53 +46,16 @@ pub struct DisplayParts {
|
|||||||
pub mac: [u8; 6],
|
pub mac: [u8; 6],
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Shared initialization helpers ───────────────────────────────────
|
// ── Shared peripheral init helpers ──────────────────────────────────
|
||||||
|
|
||||||
/// Start the ESP-RTOS scheduler and Embassy time driver.
|
/// Construct SX1262 radio from an initialized SPI bus.
|
||||||
pub fn start_timer(timg0: esp_hal::peripherals::TIMG0<'static>) {
|
|
||||||
let timg0 = TimerGroup::new(timg0);
|
|
||||||
esp_rtos::start(timg0.timer0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize SPI + DMA + SX1262 radio.
|
|
||||||
///
|
|
||||||
/// All current ESP32-S3 boards use the same SPI/radio pin mapping:
|
|
||||||
/// SCK=GPIO9, MOSI=GPIO10, MISO=GPIO11, NSS=GPIO8,
|
|
||||||
/// Reset=GPIO12, DIO1=GPIO14, BUSY=GPIO13.
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn init_radio(
|
pub fn init_radio(
|
||||||
spi2: esp_hal::peripherals::SPI2<'static>,
|
spi_bus: &'static embassy_sync::mutex::Mutex<NoopRawMutex, SpiBus>,
|
||||||
dma_ch0: esp_hal::peripherals::DMA_CH0<'static>,
|
|
||||||
sck: esp_hal::peripherals::GPIO9<'static>,
|
|
||||||
mosi: esp_hal::peripherals::GPIO10<'static>,
|
|
||||||
miso: esp_hal::peripherals::GPIO11<'static>,
|
|
||||||
nss_pin: esp_hal::peripherals::GPIO8<'static>,
|
nss_pin: esp_hal::peripherals::GPIO8<'static>,
|
||||||
reset_pin: esp_hal::peripherals::GPIO12<'static>,
|
reset_pin: esp_hal::peripherals::GPIO12<'static>,
|
||||||
dio1_pin: esp_hal::peripherals::GPIO14<'static>,
|
dio1_pin: esp_hal::peripherals::GPIO14<'static>,
|
||||||
busy_pin: esp_hal::peripherals::GPIO13<'static>,
|
busy_pin: esp_hal::peripherals::GPIO13<'static>,
|
||||||
) -> RadioParts {
|
) -> RadioParts {
|
||||||
let spi = Spi::new(
|
|
||||||
spi2,
|
|
||||||
SpiConfig::default()
|
|
||||||
.with_frequency(Rate::from_mhz(1))
|
|
||||||
.with_mode(SpiMode::_0),
|
|
||||||
)
|
|
||||||
.expect("SPI init")
|
|
||||||
.with_sck(sck)
|
|
||||||
.with_mosi(mosi)
|
|
||||||
.with_miso(miso)
|
|
||||||
.with_dma(dma_ch0);
|
|
||||||
|
|
||||||
let (rx_buf, rx_desc, tx_buf, tx_desc) = esp_hal::dma_buffers!(256);
|
|
||||||
let dma_rx = DmaRxBuf::new(rx_desc, rx_buf).expect("DMA RX buf");
|
|
||||||
let dma_tx = DmaTxBuf::new(tx_desc, tx_buf).expect("DMA TX buf");
|
|
||||||
|
|
||||||
let spi = spi.with_buffers(dma_rx, dma_tx).into_async();
|
|
||||||
|
|
||||||
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 nss = Output::new(nss_pin, Level::High, OutputConfig::default());
|
let nss = Output::new(nss_pin, Level::High, OutputConfig::default());
|
||||||
let spi_device = SpiDevice::new(spi_bus, nss);
|
let spi_device = SpiDevice::new(spi_bus, nss);
|
||||||
|
|
||||||
@@ -127,53 +78,3 @@ pub fn init_radio(
|
|||||||
delay: Delay,
|
delay: Delay,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "heltec_v3"))]
|
|
||||||
/// Initialize USB OTG CDC-ACM driver.
|
|
||||||
pub fn init_usb(
|
|
||||||
usb0: esp_hal::peripherals::USB0<'static>,
|
|
||||||
dp: esp_hal::peripherals::GPIO20<'static>,
|
|
||||||
dm: esp_hal::peripherals::GPIO19<'static>,
|
|
||||||
) -> UsbParts {
|
|
||||||
let usb_inst = esp_hal::otg_fs::Usb::new(usb0, dp, dm);
|
|
||||||
static EP_OUT_BUF: StaticCell<[u8; 1024]> = StaticCell::new();
|
|
||||||
let ep_out_buf = EP_OUT_BUF.init([0u8; 1024]);
|
|
||||||
UsbParts {
|
|
||||||
driver: esp_hal::otg_fs::asynch::Driver::new(
|
|
||||||
usb_inst,
|
|
||||||
ep_out_buf,
|
|
||||||
esp_hal::otg_fs::asynch::Config::default(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "heltec_v3")]
|
|
||||||
/// Initialize UART0 for boards with USB-UART bridge chips (e.g. CP2102).
|
|
||||||
pub fn init_uart(
|
|
||||||
uart0: esp_hal::peripherals::UART0<'static>,
|
|
||||||
tx: esp_hal::peripherals::GPIO43<'static>,
|
|
||||||
rx: esp_hal::peripherals::GPIO44<'static>,
|
|
||||||
) -> UartParts {
|
|
||||||
use esp_hal::uart::{Config as UartConfig, Uart};
|
|
||||||
|
|
||||||
let uart = Uart::new(uart0, UartConfig::default())
|
|
||||||
.expect("UART init")
|
|
||||||
.with_tx(tx)
|
|
||||||
.with_rx(rx)
|
|
||||||
.into_async();
|
|
||||||
|
|
||||||
UartParts { driver: uart }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize I2C bus for display.
|
|
||||||
pub fn init_display_i2c(
|
|
||||||
i2c0: esp_hal::peripherals::I2C0<'static>,
|
|
||||||
sda: esp_hal::peripherals::GPIO17<'static>,
|
|
||||||
scl: esp_hal::peripherals::GPIO18<'static>,
|
|
||||||
) -> DisplayI2c {
|
|
||||||
I2c::new(i2c0, I2cConfig::default())
|
|
||||||
.expect("I2C init")
|
|
||||||
.with_sda(sda)
|
|
||||||
.with_scl(scl)
|
|
||||||
.into_async()
|
|
||||||
}
|
|
||||||
@@ -2,11 +2,12 @@ use esp_hal::gpio::{Level, Output, OutputConfig};
|
|||||||
|
|
||||||
use super::esp32s3;
|
use super::esp32s3;
|
||||||
use super::traits::LoRaBoard;
|
use super::traits::LoRaBoard;
|
||||||
|
use crate::hal::esp32s3 as mcu;
|
||||||
|
|
||||||
#[allow(unused_imports)] // Re-exported for other modules (display, radio, uart tasks)
|
#[allow(unused_imports)] // Re-exported for other modules (display, radio, uart tasks)
|
||||||
pub use super::esp32s3::{DisplayI2c, DisplayParts, RadioDriver, RadioParts, UartDriver, UartParts};
|
pub use super::esp32s3::{DisplayI2c, DisplayParts, RadioDriver, RadioParts, UartDriver, UartParts};
|
||||||
|
|
||||||
// ── Board init ───────────────────────────────────────────────────────
|
// ── Board init ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
pub struct Board {
|
pub struct Board {
|
||||||
p: esp_hal::peripherals::Peripherals,
|
p: esp_hal::peripherals::Peripherals,
|
||||||
@@ -22,7 +23,7 @@ impl LoRaBoard for Board {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn mac_address() -> [u8; 6] {
|
fn mac_address() -> [u8; 6] {
|
||||||
esp_hal::efuse::Efuse::mac_address()
|
mcu::mac_address()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,16 +31,17 @@ impl Board {
|
|||||||
pub fn into_parts(self) -> (RadioParts, UartParts, Option<DisplayParts>) {
|
pub fn into_parts(self) -> (RadioParts, UartParts, Option<DisplayParts>) {
|
||||||
let p = self.p;
|
let p = self.p;
|
||||||
|
|
||||||
esp32s3::start_timer(p.TIMG0);
|
mcu::start_timer(p.TIMG0);
|
||||||
|
|
||||||
// Vext power: GPIO36, active LOW to enable peripherals
|
// Vext power: GPIO36, active LOW to enable peripherals
|
||||||
let vext = Output::new(p.GPIO36, Level::Low, OutputConfig::default());
|
let vext = Output::new(p.GPIO36, Level::Low, OutputConfig::default());
|
||||||
core::mem::forget(vext); // hold pin low permanently; drop would reset it
|
core::mem::forget(vext); // hold pin low permanently; drop would reset it
|
||||||
|
|
||||||
let radio = esp32s3::init_radio(
|
let spi_bus = mcu::init_spi(p.SPI2, p.DMA_CH0, p.GPIO9, p.GPIO10, p.GPIO11);
|
||||||
p.SPI2, p.DMA_CH0, p.GPIO9, p.GPIO10, p.GPIO11, p.GPIO8, p.GPIO12, p.GPIO14, p.GPIO13,
|
let radio = esp32s3::init_radio(spi_bus, p.GPIO8, p.GPIO12, p.GPIO14, p.GPIO13);
|
||||||
);
|
let uart = UartParts {
|
||||||
let uart = esp32s3::init_uart(p.UART0, p.GPIO43, p.GPIO44);
|
driver: mcu::init_uart(p.UART0, p.GPIO43, p.GPIO44),
|
||||||
|
};
|
||||||
|
|
||||||
// SSD1306 display reset: pulse GPIO21 low->high before I2C init
|
// SSD1306 display reset: pulse GPIO21 low->high before I2C init
|
||||||
let mut display_rst = Output::new(p.GPIO21, Level::Low, OutputConfig::default());
|
let mut display_rst = Output::new(p.GPIO21, Level::Low, OutputConfig::default());
|
||||||
@@ -48,7 +50,7 @@ impl Board {
|
|||||||
esp_hal::delay::Delay::new().delay_millis(10);
|
esp_hal::delay::Delay::new().delay_millis(10);
|
||||||
core::mem::forget(display_rst); // hold reset high permanently
|
core::mem::forget(display_rst); // hold reset high permanently
|
||||||
|
|
||||||
let i2c = esp32s3::init_display_i2c(p.I2C0, p.GPIO17, p.GPIO18);
|
let i2c = mcu::init_i2c(p.I2C0, p.GPIO17, p.GPIO18);
|
||||||
let mac = Self::mac_address();
|
let mac = Self::mac_address();
|
||||||
let display = Some(DisplayParts { i2c, mac });
|
let display = Some(DisplayParts { i2c, mac });
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use esp_hal::gpio::{Level, Output, OutputConfig};
|
|||||||
|
|
||||||
use super::esp32s3;
|
use super::esp32s3;
|
||||||
use super::traits::LoRaBoard;
|
use super::traits::LoRaBoard;
|
||||||
|
use crate::hal::esp32s3 as mcu;
|
||||||
|
|
||||||
#[allow(unused_imports)] // Re-exported for other modules (display, radio, usb tasks)
|
#[allow(unused_imports)] // Re-exported for other modules (display, radio, usb tasks)
|
||||||
pub use super::esp32s3::{DisplayI2c, DisplayParts, RadioDriver, RadioParts, UsbDriver, UsbParts};
|
pub use super::esp32s3::{DisplayI2c, DisplayParts, RadioDriver, RadioParts, UsbDriver, UsbParts};
|
||||||
@@ -22,7 +23,7 @@ impl LoRaBoard for Board {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn mac_address() -> [u8; 6] {
|
fn mac_address() -> [u8; 6] {
|
||||||
esp_hal::efuse::Efuse::mac_address()
|
mcu::mac_address()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,19 +31,20 @@ impl Board {
|
|||||||
pub fn into_parts(self) -> (RadioParts, UsbParts, Option<DisplayParts>) {
|
pub fn into_parts(self) -> (RadioParts, UsbParts, Option<DisplayParts>) {
|
||||||
let p = self.p;
|
let p = self.p;
|
||||||
|
|
||||||
esp32s3::start_timer(p.TIMG0);
|
mcu::start_timer(p.TIMG0);
|
||||||
|
|
||||||
// Vext power: GPIO36, active LOW to enable peripherals
|
// Vext power: GPIO36, active LOW to enable peripherals
|
||||||
let vext = Output::new(p.GPIO36, Level::Low, OutputConfig::default());
|
let vext = Output::new(p.GPIO36, Level::Low, OutputConfig::default());
|
||||||
core::mem::forget(vext); // hold pin low permanently; drop would reset it
|
core::mem::forget(vext); // hold pin low permanently; drop would reset it
|
||||||
|
|
||||||
let radio = esp32s3::init_radio(
|
let spi_bus = mcu::init_spi(p.SPI2, p.DMA_CH0, p.GPIO9, p.GPIO10, p.GPIO11);
|
||||||
p.SPI2, p.DMA_CH0, p.GPIO9, p.GPIO10, p.GPIO11, p.GPIO8, p.GPIO12, p.GPIO14, p.GPIO13,
|
let radio = esp32s3::init_radio(spi_bus, p.GPIO8, p.GPIO12, p.GPIO14, p.GPIO13);
|
||||||
);
|
|
||||||
|
|
||||||
// Note: switches internal USB PHY from Serial-JTAG to OTG.
|
// Note: switches internal USB PHY from Serial-JTAG to OTG.
|
||||||
// espflash --monitor will stop working after this point.
|
// espflash --monitor will stop working after this point.
|
||||||
let usb = esp32s3::init_usb(p.USB0, p.GPIO20, p.GPIO19);
|
let usb = UsbParts {
|
||||||
|
driver: mcu::init_usb(p.USB0, p.GPIO20, p.GPIO19),
|
||||||
|
};
|
||||||
|
|
||||||
// SSD1315 display reset: pulse GPIO21 low->high before I2C init
|
// SSD1315 display reset: pulse GPIO21 low->high before I2C init
|
||||||
let mut display_rst = Output::new(p.GPIO21, Level::Low, OutputConfig::default());
|
let mut display_rst = Output::new(p.GPIO21, Level::Low, OutputConfig::default());
|
||||||
@@ -51,7 +53,7 @@ impl Board {
|
|||||||
esp_hal::delay::Delay::new().delay_millis(10);
|
esp_hal::delay::Delay::new().delay_millis(10);
|
||||||
core::mem::forget(display_rst); // hold reset high permanently
|
core::mem::forget(display_rst); // hold reset high permanently
|
||||||
|
|
||||||
let i2c = esp32s3::init_display_i2c(p.I2C0, p.GPIO17, p.GPIO18);
|
let i2c = mcu::init_i2c(p.I2C0, p.GPIO17, p.GPIO18);
|
||||||
let mac = Self::mac_address();
|
let mac = Self::mac_address();
|
||||||
let display = Some(DisplayParts { i2c, mac });
|
let display = Some(DisplayParts { i2c, mac });
|
||||||
|
|
||||||
|
|||||||
@@ -1,40 +1,27 @@
|
|||||||
use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
|
use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
|
||||||
use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull};
|
use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull};
|
||||||
use embassy_nrf::spim::{self, Spim};
|
use embassy_nrf::spim::{self, Spim};
|
||||||
use embassy_nrf::twim::{self, Twim};
|
use embassy_nrf::twim;
|
||||||
use embassy_nrf::usb::Driver;
|
use embassy_nrf::usb::Driver;
|
||||||
use embassy_nrf::{bind_interrupts, Peripherals};
|
|
||||||
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||||
use embassy_time::Delay;
|
use embassy_time::Delay;
|
||||||
use static_cell::StaticCell;
|
|
||||||
|
|
||||||
use lora_phy::iv::GenericSx126xInterfaceVariant;
|
use lora_phy::iv::GenericSx126xInterfaceVariant;
|
||||||
use lora_phy::sx126x::{self, Sx1262, Sx126x};
|
use lora_phy::sx126x::{self, Sx1262, Sx126x};
|
||||||
|
|
||||||
use super::traits::LoRaBoard;
|
use super::traits::LoRaBoard;
|
||||||
|
use crate::hal::nrf52840 as mcu;
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
// ── Concrete peripheral types ───────────────────────────────────────
|
||||||
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 ────────────────────────────────────────
|
|
||||||
|
|
||||||
type SpiBus = Spim<'static>;
|
|
||||||
type Nss = Output<'static>;
|
type Nss = Output<'static>;
|
||||||
type Iv = GenericSx126xInterfaceVariant<Output<'static>, Input<'static>>;
|
type Iv = GenericSx126xInterfaceVariant<Output<'static>, Input<'static>>;
|
||||||
type RadioSpiDevice = SpiDevice<'static, NoopRawMutex, SpiBus, Nss>;
|
type RadioSpiDevice = SpiDevice<'static, NoopRawMutex, mcu::SpiBus, Nss>;
|
||||||
/// SX126x driver (pre-LoRa construction — LoRa::new is async).
|
|
||||||
pub type RadioDriver = Sx126x<RadioSpiDevice, Iv, Sx1262>;
|
pub type RadioDriver = Sx126x<RadioSpiDevice, Iv, Sx1262>;
|
||||||
|
pub type UsbDriver = mcu::UsbNrfDriver;
|
||||||
|
pub type DisplayI2c = mcu::I2cBus;
|
||||||
|
|
||||||
/// USB driver for CDC-ACM.
|
// ── Peripheral bundles ──────────────────────────────────────────────
|
||||||
pub type UsbDriver = Driver<'static, &'static embassy_nrf::usb::vbus_detect::SoftwareVbusDetect>;
|
|
||||||
|
|
||||||
/// I2C bus for an optional SSD1306 OLED.
|
|
||||||
pub type DisplayI2c = Twim<'static>;
|
|
||||||
|
|
||||||
// ── Peripheral bundles ───────────────────────────────────────────────
|
|
||||||
|
|
||||||
pub struct RadioParts {
|
pub struct RadioParts {
|
||||||
pub driver: RadioDriver,
|
pub driver: RadioDriver,
|
||||||
@@ -50,13 +37,13 @@ pub struct DisplayParts {
|
|||||||
pub mac: [u8; 6],
|
pub mac: [u8; 6],
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Board init ───────────────────────────────────────────────────────
|
// ── Board init ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
pub struct Board {
|
pub struct Board {
|
||||||
p: Peripherals,
|
p: embassy_nrf::Peripherals,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::traits::LoRaBoard for Board {
|
impl LoRaBoard for Board {
|
||||||
const NAME: &'static str = "RAK WisBlock 4631";
|
const NAME: &'static str = "RAK WisBlock 4631";
|
||||||
const TX_POWER_RANGE: (i8, i8) = (-9, 22);
|
const TX_POWER_RANGE: (i8, i8) = (-9, 22);
|
||||||
|
|
||||||
@@ -66,23 +53,7 @@ impl super::traits::LoRaBoard for Board {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn mac_address() -> [u8; 6] {
|
fn mac_address() -> [u8; 6] {
|
||||||
// SAFETY: 0x10000000 is the nRF52840 FICR (Factory Information Configuration
|
mcu::mac_address()
|
||||||
// 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,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,19 +61,16 @@ impl Board {
|
|||||||
pub fn into_parts(self) -> (RadioParts, UsbParts, Option<DisplayParts>) {
|
pub fn into_parts(self) -> (RadioParts, UsbParts, Option<DisplayParts>) {
|
||||||
let p = self.p;
|
let p = self.p;
|
||||||
|
|
||||||
// ── SPI bus for SX1262 ───────────────────────────────────
|
// ── SPI bus for SX1262 ──────────────────────────────────
|
||||||
let mut spi_cfg = spim::Config::default();
|
let mut spi_cfg = spim::Config::default();
|
||||||
spi_cfg.frequency = spim::Frequency::M1;
|
spi_cfg.frequency = spim::Frequency::M1;
|
||||||
let spi = Spim::new(p.SPI3, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_cfg);
|
let spi = Spim::new(p.SPI3, mcu::Irqs, p.P1_11, p.P1_13, p.P1_12, spi_cfg);
|
||||||
|
let spi_bus = mcu::share_spi_bus(spi);
|
||||||
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 nss = Output::new(p.P1_10, Level::High, OutputDrive::Standard);
|
let nss = Output::new(p.P1_10, Level::High, OutputDrive::Standard);
|
||||||
let spi_device = SpiDevice::new(spi_bus, nss);
|
let spi_device = SpiDevice::new(spi_bus, nss);
|
||||||
|
|
||||||
// ── SX1262 control pins ──────────────────────────────────
|
// ── SX1262 control pins ─────────────────────────────────
|
||||||
let reset = Output::new(p.P1_06, Level::High, OutputDrive::Standard);
|
let reset = Output::new(p.P1_06, Level::High, OutputDrive::Standard);
|
||||||
let dio1 = Input::new(p.P1_15, Pull::Down);
|
let dio1 = Input::new(p.P1_15, Pull::Down);
|
||||||
let busy = Input::new(p.P1_14, Pull::Down);
|
let busy = Input::new(p.P1_14, Pull::Down);
|
||||||
@@ -122,23 +90,17 @@ impl Board {
|
|||||||
delay: Delay,
|
delay: Delay,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── USB ──────────────────────────────────────────────────
|
// ── USB ─────────────────────────────────────────────────
|
||||||
static VBUS: StaticCell<embassy_nrf::usb::vbus_detect::SoftwareVbusDetect> =
|
let vbus = mcu::alloc_vbus_detect(true, false);
|
||||||
StaticCell::new();
|
|
||||||
let vbus = VBUS.init(embassy_nrf::usb::vbus_detect::SoftwareVbusDetect::new(
|
|
||||||
true, false,
|
|
||||||
));
|
|
||||||
let usb = UsbParts {
|
let usb = UsbParts {
|
||||||
driver: Driver::new(p.USBD, Irqs, vbus),
|
driver: Driver::new(p.USBD, mcu::Irqs, vbus),
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── Display (optional RAK1921 SSD1306 OLED on I2C) ──────
|
// ── Display (optional RAK1921 SSD1306 OLED on I2C) ─────
|
||||||
// Always provide the I2C bus; display_task detects presence
|
let twim_buf = mcu::alloc_i2c_buffer();
|
||||||
// via SSD1306 init (fails gracefully if no display attached).
|
let i2c = embassy_nrf::twim::Twim::new(
|
||||||
static TWIM_BUF: StaticCell<[u8; 256]> = StaticCell::new();
|
p.TWISPI0, mcu::Irqs, p.P0_13, p.P0_14, twim::Config::default(), twim_buf,
|
||||||
let twim_buf = TWIM_BUF.init([0u8; 256]);
|
);
|
||||||
let i2c_cfg = twim::Config::default();
|
|
||||||
let i2c = Twim::new(p.TWISPI0, Irqs, p.P0_13, p.P0_14, i2c_cfg, twim_buf);
|
|
||||||
let mac = Self::mac_address();
|
let mac = Self::mac_address();
|
||||||
let display = Some(DisplayParts { i2c, mac });
|
let display = Some(DisplayParts { i2c, mac });
|
||||||
|
|
||||||
|
|||||||
@@ -1,40 +1,27 @@
|
|||||||
use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
|
use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
|
||||||
use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull};
|
use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull};
|
||||||
use embassy_nrf::spim::{self, Spim};
|
use embassy_nrf::spim::{self, Spim};
|
||||||
use embassy_nrf::twim::{self, Twim};
|
use embassy_nrf::twim;
|
||||||
use embassy_nrf::usb::Driver;
|
use embassy_nrf::usb::Driver;
|
||||||
use embassy_nrf::{bind_interrupts, Peripherals};
|
|
||||||
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||||
use embassy_time::Delay;
|
use embassy_time::Delay;
|
||||||
use static_cell::StaticCell;
|
|
||||||
|
|
||||||
use lora_phy::iv::GenericSx126xInterfaceVariant;
|
use lora_phy::iv::GenericSx126xInterfaceVariant;
|
||||||
use lora_phy::sx126x::{self, Sx1262, Sx126x};
|
use lora_phy::sx126x::{self, Sx1262, Sx126x};
|
||||||
|
|
||||||
use super::traits::LoRaBoard;
|
use super::traits::LoRaBoard;
|
||||||
|
use crate::hal::nrf52840 as mcu;
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
// ── Concrete peripheral types ───────────────────────────────────────
|
||||||
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 ────────────────────────────────────────
|
|
||||||
|
|
||||||
type SpiBus = Spim<'static>;
|
|
||||||
type Nss = Output<'static>;
|
type Nss = Output<'static>;
|
||||||
type Iv = GenericSx126xInterfaceVariant<Output<'static>, Input<'static>>;
|
type Iv = GenericSx126xInterfaceVariant<Output<'static>, Input<'static>>;
|
||||||
type RadioSpiDevice = SpiDevice<'static, NoopRawMutex, SpiBus, Nss>;
|
type RadioSpiDevice = SpiDevice<'static, NoopRawMutex, mcu::SpiBus, Nss>;
|
||||||
/// SX126x driver (pre-LoRa construction — LoRa::new is async).
|
|
||||||
pub type RadioDriver = Sx126x<RadioSpiDevice, Iv, Sx1262>;
|
pub type RadioDriver = Sx126x<RadioSpiDevice, Iv, Sx1262>;
|
||||||
|
pub type UsbDriver = mcu::UsbNrfDriver;
|
||||||
|
pub type DisplayI2c = mcu::I2cBus;
|
||||||
|
|
||||||
/// USB driver for CDC-ACM.
|
// ── Peripheral bundles ──────────────────────────────────────────────
|
||||||
pub type UsbDriver = Driver<'static, &'static embassy_nrf::usb::vbus_detect::SoftwareVbusDetect>;
|
|
||||||
|
|
||||||
/// I2C bus for SH1106 OLED.
|
|
||||||
pub type DisplayI2c = Twim<'static>;
|
|
||||||
|
|
||||||
// ── Peripheral bundles ───────────────────────────────────────────────
|
|
||||||
|
|
||||||
pub struct RadioParts {
|
pub struct RadioParts {
|
||||||
pub driver: RadioDriver,
|
pub driver: RadioDriver,
|
||||||
@@ -50,13 +37,13 @@ pub struct DisplayParts {
|
|||||||
pub mac: [u8; 6],
|
pub mac: [u8; 6],
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Board init ───────────────────────────────────────────────────────
|
// ── Board init ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
pub struct Board {
|
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 NAME: &'static str = "Wio Tracker L1";
|
||||||
const TX_POWER_RANGE: (i8, i8) = (-9, 22);
|
const TX_POWER_RANGE: (i8, i8) = (-9, 22);
|
||||||
|
|
||||||
@@ -69,23 +56,7 @@ impl super::traits::LoRaBoard for Board {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn mac_address() -> [u8; 6] {
|
fn mac_address() -> [u8; 6] {
|
||||||
// SAFETY: 0x10000000 is the nRF52840 FICR (Factory Information Configuration
|
mcu::mac_address()
|
||||||
// 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,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,19 +64,16 @@ impl Board {
|
|||||||
pub fn into_parts(self) -> (RadioParts, UsbParts, Option<DisplayParts>) {
|
pub fn into_parts(self) -> (RadioParts, UsbParts, Option<DisplayParts>) {
|
||||||
let p = self.p;
|
let p = self.p;
|
||||||
|
|
||||||
// ── SPI bus for SX1262 ───────────────────────────────────
|
// ── SPI bus for SX1262 ──────────────────────────────────
|
||||||
let mut spi_cfg = spim::Config::default();
|
let mut spi_cfg = spim::Config::default();
|
||||||
spi_cfg.frequency = spim::Frequency::M1;
|
spi_cfg.frequency = spim::Frequency::M1;
|
||||||
let spi = Spim::new(p.SPI3, Irqs, p.P0_30, p.P0_03, p.P0_28, spi_cfg);
|
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);
|
||||||
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 nss = Output::new(p.P1_14, Level::High, OutputDrive::Standard);
|
let nss = Output::new(p.P1_14, Level::High, OutputDrive::Standard);
|
||||||
let spi_device = SpiDevice::new(spi_bus, nss);
|
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 reset = Output::new(p.P1_07, Level::High, OutputDrive::Standard);
|
||||||
let dio1 = Input::new(p.P0_07, Pull::Down);
|
let dio1 = Input::new(p.P0_07, Pull::Down);
|
||||||
let busy = Input::new(p.P1_10, Pull::Down);
|
let busy = Input::new(p.P1_10, Pull::Down);
|
||||||
@@ -127,25 +95,21 @@ impl Board {
|
|||||||
delay: Delay,
|
delay: Delay,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── USB ──────────────────────────────────────────────────
|
// ── USB ─────────────────────────────────────────────────
|
||||||
static VBUS: StaticCell<embassy_nrf::usb::vbus_detect::SoftwareVbusDetect> =
|
let vbus = mcu::alloc_vbus_detect(true, true);
|
||||||
StaticCell::new();
|
|
||||||
let vbus = VBUS.init(embassy_nrf::usb::vbus_detect::SoftwareVbusDetect::new(
|
|
||||||
true, true,
|
|
||||||
));
|
|
||||||
let usb = UsbParts {
|
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
|
// ── 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);
|
let bat_ctl = Output::new(p.P0_04, Level::High, OutputDrive::Standard);
|
||||||
core::mem::forget(bat_ctl);
|
core::mem::forget(bat_ctl);
|
||||||
|
|
||||||
// ── Display (SH1106 OLED on I2C) ─────────────────────────
|
// ── Display (SH1106 OLED on I2C) ────────────────────────
|
||||||
static TWIM_BUF: StaticCell<[u8; 256]> = StaticCell::new();
|
let twim_buf = mcu::alloc_i2c_buffer();
|
||||||
let twim_buf = TWIM_BUF.init([0u8; 256]);
|
let i2c = embassy_nrf::twim::Twim::new(
|
||||||
let i2c_cfg = twim::Config::default();
|
p.TWISPI0, mcu::Irqs, p.P0_06, p.P0_05, twim::Config::default(), twim_buf,
|
||||||
let i2c = Twim::new(p.TWISPI0, Irqs, p.P0_06, p.P0_05, i2c_cfg, twim_buf);
|
);
|
||||||
let mac = Self::mac_address();
|
let mac = Self::mac_address();
|
||||||
let display = Some(DisplayParts { i2c, mac });
|
let display = Some(DisplayParts { i2c, mac });
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
//! OLED display driver with radio status dashboard.
|
//! OLED display dashboard: radio status, sparkline, splash screen.
|
||||||
|
|
||||||
mod render;
|
mod render;
|
||||||
#[cfg(feature = "wio_tracker_l1")]
|
|
||||||
pub mod sh1106;
|
|
||||||
mod task;
|
mod task;
|
||||||
pub use task::display_task;
|
pub use task::display_task;
|
||||||
@@ -123,7 +123,7 @@ pub async fn display_task(
|
|||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(feature = "wio_tracker_l1")] {
|
if #[cfg(feature = "wio_tracker_l1")] {
|
||||||
let mut display = super::sh1106::Sh1106::new(parts.i2c, 0x3D);
|
let mut display = crate::driver::sh1106::Sh1106::new(parts.i2c, 0x3D);
|
||||||
|
|
||||||
Timer::after_millis(100).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#[allow(dead_code)]
|
||||||
|
pub mod sh1106;
|
||||||
File renamed without changes.
@@ -0,0 +1,121 @@
|
|||||||
|
//! ESP32-S3 MCU initialization primitives.
|
||||||
|
//!
|
||||||
|
//! Provides low-level bus and peripheral init for ESP32-S3 boards.
|
||||||
|
//! Board files call these with specific pins and construct higher-level
|
||||||
|
//! drivers (Sx126x, SSD1306, etc.) themselves.
|
||||||
|
|
||||||
|
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||||
|
use esp_hal::dma::{DmaRxBuf, DmaTxBuf};
|
||||||
|
use esp_hal::i2c::master::{Config as I2cConfig, I2c};
|
||||||
|
use esp_hal::spi::master::{Config as SpiConfig, Spi, SpiDmaBus};
|
||||||
|
use esp_hal::spi::Mode as SpiMode;
|
||||||
|
use esp_hal::time::Rate;
|
||||||
|
use esp_hal::timer::timg::TimerGroup;
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
|
// ── MCU-level types ─────────────────────────────────────────────────
|
||||||
|
|
||||||
|
pub type SpiBus = SpiDmaBus<'static, esp_hal::Async>;
|
||||||
|
pub type I2cBus = I2c<'static, esp_hal::Async>;
|
||||||
|
pub type UsbOtgDriver = esp_hal::otg_fs::asynch::Driver<'static>;
|
||||||
|
pub type UartDriver = esp_hal::uart::Uart<'static, esp_hal::Async>;
|
||||||
|
|
||||||
|
// ── Timer ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Start the ESP-RTOS scheduler and Embassy time driver.
|
||||||
|
pub fn start_timer(timg0: esp_hal::peripherals::TIMG0<'static>) {
|
||||||
|
let timg0 = TimerGroup::new(timg0);
|
||||||
|
esp_rtos::start(timg0.timer0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── SPI bus ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Initialize SPI2 with DMA and wrap in a shared bus (StaticCell + Mutex).
|
||||||
|
///
|
||||||
|
/// Returns a reference to the shared SPI bus suitable for `SpiDevice::new()`.
|
||||||
|
pub fn init_spi(
|
||||||
|
spi2: esp_hal::peripherals::SPI2<'static>,
|
||||||
|
dma_ch0: esp_hal::peripherals::DMA_CH0<'static>,
|
||||||
|
sck: esp_hal::peripherals::GPIO9<'static>,
|
||||||
|
mosi: esp_hal::peripherals::GPIO10<'static>,
|
||||||
|
miso: esp_hal::peripherals::GPIO11<'static>,
|
||||||
|
) -> &'static embassy_sync::mutex::Mutex<NoopRawMutex, SpiBus> {
|
||||||
|
let spi = Spi::new(
|
||||||
|
spi2,
|
||||||
|
SpiConfig::default()
|
||||||
|
.with_frequency(Rate::from_mhz(1))
|
||||||
|
.with_mode(SpiMode::_0),
|
||||||
|
)
|
||||||
|
.expect("SPI init")
|
||||||
|
.with_sck(sck)
|
||||||
|
.with_mosi(mosi)
|
||||||
|
.with_miso(miso)
|
||||||
|
.with_dma(dma_ch0);
|
||||||
|
|
||||||
|
let (rx_buf, rx_desc, tx_buf, tx_desc) = esp_hal::dma_buffers!(256);
|
||||||
|
let dma_rx = DmaRxBuf::new(rx_desc, rx_buf).expect("DMA RX buf");
|
||||||
|
let dma_tx = DmaTxBuf::new(tx_desc, tx_buf).expect("DMA TX buf");
|
||||||
|
|
||||||
|
let spi = spi.with_buffers(dma_rx, dma_tx).into_async();
|
||||||
|
|
||||||
|
static SPI_BUS: StaticCell<embassy_sync::mutex::Mutex<NoopRawMutex, SpiBus>> =
|
||||||
|
StaticCell::new();
|
||||||
|
SPI_BUS.init(embassy_sync::mutex::Mutex::new(spi))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── I2C bus ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Initialize I2C0 for peripherals (display, sensors, etc.).
|
||||||
|
pub fn init_i2c(
|
||||||
|
i2c0: esp_hal::peripherals::I2C0<'static>,
|
||||||
|
sda: esp_hal::peripherals::GPIO17<'static>,
|
||||||
|
scl: esp_hal::peripherals::GPIO18<'static>,
|
||||||
|
) -> I2cBus {
|
||||||
|
I2c::new(i2c0, I2cConfig::default())
|
||||||
|
.expect("I2C init")
|
||||||
|
.with_sda(sda)
|
||||||
|
.with_scl(scl)
|
||||||
|
.into_async()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── USB OTG ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Initialize USB OTG FS driver for CDC-ACM.
|
||||||
|
pub fn init_usb(
|
||||||
|
usb0: esp_hal::peripherals::USB0<'static>,
|
||||||
|
dp: esp_hal::peripherals::GPIO20<'static>,
|
||||||
|
dm: esp_hal::peripherals::GPIO19<'static>,
|
||||||
|
) -> UsbOtgDriver {
|
||||||
|
let usb_inst = esp_hal::otg_fs::Usb::new(usb0, dp, dm);
|
||||||
|
static EP_OUT_BUF: StaticCell<[u8; 1024]> = StaticCell::new();
|
||||||
|
let ep_out_buf = EP_OUT_BUF.init([0u8; 1024]);
|
||||||
|
esp_hal::otg_fs::asynch::Driver::new(
|
||||||
|
usb_inst,
|
||||||
|
ep_out_buf,
|
||||||
|
esp_hal::otg_fs::asynch::Config::default(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── UART ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Initialize UART0 for boards with USB-UART bridge chips (e.g. CP2102).
|
||||||
|
pub fn init_uart(
|
||||||
|
uart0: esp_hal::peripherals::UART0<'static>,
|
||||||
|
tx: esp_hal::peripherals::GPIO43<'static>,
|
||||||
|
rx: esp_hal::peripherals::GPIO44<'static>,
|
||||||
|
) -> UartDriver {
|
||||||
|
use esp_hal::uart::{Config as UartConfig, Uart};
|
||||||
|
|
||||||
|
Uart::new(uart0, UartConfig::default())
|
||||||
|
.expect("UART init")
|
||||||
|
.with_tx(tx)
|
||||||
|
.with_rx(rx)
|
||||||
|
.into_async()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── MAC address ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Read the factory-programmed MAC address from eFuse.
|
||||||
|
pub fn mac_address() -> [u8; 6] {
|
||||||
|
esp_hal::efuse::Efuse::mac_address()
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
//! MCU family initialization primitives.
|
||||||
|
//!
|
||||||
|
//! 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.
|
||||||
|
|
||||||
|
#[cfg(any(feature = "heltec_v3", feature = "heltec_v4"))]
|
||||||
|
pub mod esp32s3;
|
||||||
|
|
||||||
|
#[cfg(any(feature = "rak_wisblock_4631", feature = "wio_tracker_l1"))]
|
||||||
|
pub mod nrf52840;
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
//! nRF52840 MCU initialization primitives.
|
||||||
|
//!
|
||||||
|
//! Provides shared boilerplate for nRF52840 boards: interrupt bindings,
|
||||||
|
//! type aliases, StaticCell bus wrapping, USB init, and MAC address reading.
|
||||||
|
|
||||||
|
use embassy_nrf::bind_interrupts;
|
||||||
|
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
|
// ── Interrupts (shared by all nRF52840 boards) ─────────────────────
|
||||||
|
|
||||||
|
bind_interrupts!(pub 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>;
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── MCU-level types ─────────────────────────────────────────────────
|
||||||
|
|
||||||
|
pub type SpiBus = embassy_nrf::spim::Spim<'static>;
|
||||||
|
pub type I2cBus = embassy_nrf::twim::Twim<'static>;
|
||||||
|
pub type UsbNrfDriver =
|
||||||
|
embassy_nrf::usb::Driver<'static, &'static embassy_nrf::usb::vbus_detect::SoftwareVbusDetect>;
|
||||||
|
|
||||||
|
// ── SPI bus sharing ─────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Wrap an initialized SPI peripheral in a shared bus (StaticCell + Mutex).
|
||||||
|
pub fn share_spi_bus(
|
||||||
|
spi: SpiBus,
|
||||||
|
) -> &'static embassy_sync::mutex::Mutex<NoopRawMutex, SpiBus> {
|
||||||
|
static SPI_BUS: StaticCell<embassy_sync::mutex::Mutex<NoopRawMutex, SpiBus>> =
|
||||||
|
StaticCell::new();
|
||||||
|
SPI_BUS.init(embassy_sync::mutex::Mutex::new(spi))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── I2C DMA buffer ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Allocate a DMA-safe buffer for Twim I2C.
|
||||||
|
///
|
||||||
|
/// Every nRF52840 Twim user needs a `&'static mut [u8]` buffer.
|
||||||
|
/// This provides one via StaticCell so board files don't repeat the pattern.
|
||||||
|
pub fn alloc_i2c_buffer() -> &'static mut [u8; 256] {
|
||||||
|
static TWIM_BUF: StaticCell<[u8; 256]> = StaticCell::new();
|
||||||
|
TWIM_BUF.init([0u8; 256])
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── USB VBUS detection ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Allocate and initialize software VBUS detection.
|
||||||
|
pub fn alloc_vbus_detect(
|
||||||
|
vbus_detect: bool,
|
||||||
|
self_powered: bool,
|
||||||
|
) -> &'static embassy_nrf::usb::vbus_detect::SoftwareVbusDetect {
|
||||||
|
static VBUS: StaticCell<embassy_nrf::usb::vbus_detect::SoftwareVbusDetect> = StaticCell::new();
|
||||||
|
VBUS.init(embassy_nrf::usb::vbus_detect::SoftwareVbusDetect::new(
|
||||||
|
vbus_detect,
|
||||||
|
self_powered,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── MAC address ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Read the factory-programmed device address from FICR registers.
|
||||||
|
pub 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,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,10 @@ mod board;
|
|||||||
mod channel;
|
mod channel;
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
mod display;
|
mod display;
|
||||||
|
#[cfg(not(test))]
|
||||||
|
mod driver;
|
||||||
|
#[cfg(not(test))]
|
||||||
|
mod hal;
|
||||||
mod protocol;
|
mod protocol;
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
mod radio;
|
mod radio;
|
||||||
|
|||||||
Reference in new issue
Block a user