feat: add Waveshare RP2040-LoRa board support
First RP2040 board and third MCU family (after ESP32-S3 and nRF52840). Hardware: RP2040 + SX1262 radio, 850-930 MHz HF, PE4259 antenna switch, LED on GPIO25, no OLED, USB-C via FPC ribbon cable to adapter board. New files: - src/hal/rp2040.rs: MCU primitives (SPI bus, USB driver, flash unique ID MAC) - src/board/waveshare_rp2040_lora.rs: board implementation - ld/rp2040-memory.x: linker memory layout Also fixes cortex-m-rt/set-vtor (moved from global dep to per-board feature, since Cortex-M0+ has no VTOR register) and makes build.rs memory.x selection target-aware.
This commit is contained in:
14 files changed
+842
-14
No files matched your search
@@ -7,6 +7,7 @@
|
||||
Create `src/board/your_board.rs`. Use an existing board as a template:
|
||||
- ESP32-S3 boards: copy `heltec_v4.rs` (uses shared helpers from `board/esp32s3.rs` and `hal/esp32s3.rs`)
|
||||
- nRF52840 boards: copy `rak_wisblock_4631.rs` (uses shared helpers from `hal/nrf52840.rs`)
|
||||
- RP2040 boards: copy `waveshare_rp2040_lora.rs` (uses shared helpers from `hal/rp2040.rs`)
|
||||
- New MCU family: create a new `hal/<mcu>.rs` with MCU primitives first
|
||||
|
||||
`build.rs` auto-discovers your board file — any `.rs` file in `src/board/` that
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
|
||||
use embassy_rp::gpio::{Input, Level, Output, Pull};
|
||||
use embassy_rp::spi;
|
||||
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||
use embassy_time::Delay;
|
||||
|
||||
use lora_phy::iv::GenericSx126xInterfaceVariant;
|
||||
use lora_phy::sx126x::{self, Sx1262, Sx126x};
|
||||
|
||||
use super::traits::{BoardParts, LoRaBoard};
|
||||
use crate::hal::rp2040 as mcu;
|
||||
|
||||
// ── Concrete peripheral types ──────────────────────────────────────
|
||||
|
||||
type Nss = Output<'static>;
|
||||
type Iv = GenericSx126xInterfaceVariant<Output<'static>, Input<'static>>;
|
||||
type RadioSpiDevice = SpiDevice<'static, NoopRawMutex, mcu::SpiBus, Nss>;
|
||||
pub type RadioDriver = Sx126x<RadioSpiDevice, Iv, Sx1262>;
|
||||
pub type UsbDriver = mcu::UsbRpDriver;
|
||||
pub type LedDriver = crate::driver::simple_led::SimpleLed<Output<'static>>;
|
||||
|
||||
// Display types — never instantiated (no OLED on this board), but the
|
||||
// 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 DisplayDriver = ssd1306::Ssd1306Async<
|
||||
ssd1306::prelude::I2CInterface<DisplayI2c>,
|
||||
ssd1306::size::DisplaySize128x64,
|
||||
ssd1306::mode::BufferedGraphicsModeAsync<ssd1306::size::DisplaySize128x64>,
|
||||
>;
|
||||
|
||||
// ── Peripheral bundles ─────────────────────────────────────────────
|
||||
|
||||
pub struct RadioParts {
|
||||
pub driver: RadioDriver,
|
||||
pub delay: Delay,
|
||||
}
|
||||
|
||||
pub struct UsbParts {
|
||||
pub driver: UsbDriver,
|
||||
}
|
||||
|
||||
pub struct DisplayParts {
|
||||
pub i2c: DisplayI2c,
|
||||
}
|
||||
|
||||
// ── Display init ───────────────────────────────────────────────────
|
||||
|
||||
pub async fn create_display(_i2c: DisplayI2c) -> Option<DisplayDriver> {
|
||||
None
|
||||
}
|
||||
|
||||
// ── Board init ─────────────────────────────────────────────────────
|
||||
|
||||
pub struct Board {
|
||||
p: embassy_rp::Peripherals,
|
||||
}
|
||||
|
||||
impl LoRaBoard for Board {
|
||||
const NAME: &'static str = "Waveshare RP2040-LoRa";
|
||||
const TX_POWER_RANGE: (i8, i8) = (-9, 22);
|
||||
|
||||
type RadioParts = RadioParts;
|
||||
type CommParts = UsbParts;
|
||||
type DisplayParts = DisplayParts;
|
||||
type DisplayDriver = DisplayDriver;
|
||||
type LedDriver = LedDriver;
|
||||
|
||||
fn init() -> Self {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
Self { p }
|
||||
}
|
||||
|
||||
fn mac_address() -> [u8; 6] {
|
||||
mcu::mac_address()
|
||||
}
|
||||
|
||||
fn into_parts(self) -> BoardParts<RadioParts, UsbParts, DisplayParts, LedDriver> {
|
||||
let p = self.p;
|
||||
|
||||
// ── MAC address from flash unique ID ───────────────────
|
||||
let mut flash = embassy_rp::flash::Flash::<_, _, { 2 * 1024 * 1024 }>::new_blocking(p.FLASH);
|
||||
mcu::init_mac(&mut flash);
|
||||
|
||||
// ── SPI1 bus for SX1262 ────────────────────────────────
|
||||
//
|
||||
// Pin mapping from Meshtastic & MeshCore firmware (confirmed working):
|
||||
// SCK = GPIO14, MOSI = GPIO15, MISO = GPIO24, CS = GPIO13
|
||||
//
|
||||
// The Waveshare wiki (https://www.waveshare.com/wiki/RP2040-LoRa) lists
|
||||
// different pins (MOSI=11, MISO=8, CS=9) but those don't work in practice.
|
||||
// Both sets are valid SPI1 alternate functions on RP2040.
|
||||
let mut spi_cfg = spi::Config::default();
|
||||
spi_cfg.frequency = 1_000_000;
|
||||
let spi = spi::Spi::new(
|
||||
p.SPI1, p.PIN_14, p.PIN_15, p.PIN_24, p.DMA_CH0, p.DMA_CH1, spi_cfg,
|
||||
);
|
||||
let spi_bus = mcu::share_spi_bus(spi);
|
||||
|
||||
let nss = Output::new(p.PIN_13, Level::High);
|
||||
let spi_device = SpiDevice::new(spi_bus, nss);
|
||||
|
||||
let reset = Output::new(p.PIN_23, Level::High);
|
||||
let dio1 = Input::new(p.PIN_16, Pull::Down);
|
||||
let busy = Input::new(p.PIN_18, Pull::Down);
|
||||
|
||||
// PE4259 antenna switch: GPIO17 = !CTRL (rx_enable).
|
||||
// DIO2 is internally wired as CTRL (tx_enable), managed by the SX1262.
|
||||
let rx_enable = Output::new(p.PIN_17, Level::Low);
|
||||
let iv = GenericSx126xInterfaceVariant::new(reset, dio1, busy, Some(rx_enable), None)
|
||||
.expect("SX1262 interface init");
|
||||
|
||||
let sx_config = sx126x::Config {
|
||||
chip: Sx1262,
|
||||
tcxo_ctrl: None, // No TCXO on this board
|
||||
use_dcdc: true,
|
||||
rx_boost: false,
|
||||
};
|
||||
|
||||
let radio = RadioParts {
|
||||
driver: Sx126x::new(spi_device, iv, sx_config),
|
||||
delay: Delay,
|
||||
};
|
||||
|
||||
// ── USB ────────────────────────────────────────────────
|
||||
let host = UsbParts {
|
||||
driver: embassy_rp::usb::Driver::new(p.USB, mcu::Irqs),
|
||||
};
|
||||
|
||||
// ── LED on GPIO25 ──────────────────────────────────────
|
||||
let led_pin = Output::new(p.PIN_25, Level::Low);
|
||||
let led = crate::driver::simple_led::SimpleLed(led_pin);
|
||||
|
||||
BoardParts {
|
||||
radio,
|
||||
host,
|
||||
display: None,
|
||||
led,
|
||||
mac: Self::mac_address(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user