Skip to content

Commit e401f39

Browse files
committed
Adding SPI support
Signed-off-by: Dmitry Tarnyagin <[email protected]> Signed-off-by: Vegard Eriksen <[email protected]> Signed-off-by: Tyson Lawrence <[email protected]>
1 parent 63ade38 commit e401f39

File tree

7 files changed

+326
-22
lines changed

7 files changed

+326
-22
lines changed

embassy-nrf/src/chips/nrf54l15_app.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,14 +199,14 @@ pub mod pac {
199199

200200
/// The maximum buffer size that the EasyDMA can send/recv in one operation.
201201
pub const EASY_DMA_SIZE: usize = (1 << 16) - 1;
202-
//pub const FORCE_COPY_BUFFER_SIZE: usize = 1024;
202+
pub const FORCE_COPY_BUFFER_SIZE: usize = 1024;
203203

204204
// 1.5 MB NVM
205205
#[allow(unused)]
206206
pub const FLASH_SIZE: usize = 1536 * 1024;
207207

208208
embassy_hal_internal::peripherals! {
209-
// GPIO port 0
209+
// GPIO port P0
210210
P0_00,
211211
P0_01,
212212
P0_02,
@@ -215,7 +215,7 @@ embassy_hal_internal::peripherals! {
215215
P0_05,
216216
P0_06,
217217

218-
// GPIO port 1
218+
// GPIO port P1
219219
P1_00,
220220
P1_01,
221221
P1_02,
@@ -234,8 +234,7 @@ embassy_hal_internal::peripherals! {
234234
P1_15,
235235
P1_16,
236236

237-
238-
// GPIO port 2
237+
// GPIO port P2
239238
P2_00,
240239
P2_01,
241240
P2_02,
@@ -255,6 +254,13 @@ embassy_hal_internal::peripherals! {
255254
// TEMP
256255
TEMP,
257256

257+
// TWI/SPI
258+
SPI00,
259+
TWISPI20,
260+
TWISPI21,
261+
TWISPI22,
262+
TWISPI30,
263+
258264
// WDT
259265
#[cfg(feature = "_ns")]
260266
WDT,
@@ -302,6 +308,20 @@ impl_pin!(P2_08, 2, 8);
302308
impl_pin!(P2_09, 2, 9);
303309
impl_pin!(P2_10, 2, 10);
304310

311+
// SPIM
312+
impl_spim!(SPI00, SPIM00, SERIAL00);
313+
impl_spim!(TWISPI20, SPIM20, SERIAL20);
314+
impl_spim!(TWISPI21, SPIM21, SERIAL21);
315+
impl_spim!(TWISPI22, SPIM22, SERIAL22);
316+
impl_spim!(TWISPI30, SPIM30, SERIAL30);
317+
318+
// SPIS
319+
impl_spis!(SPI00, SPIS00, SERIAL00);
320+
impl_spis!(TWISPI20, SPIS20, SERIAL20);
321+
impl_spis!(TWISPI21, SPIS21, SERIAL21);
322+
impl_spis!(TWISPI22, SPIS22, SERIAL22);
323+
impl_spis!(TWISPI30, SPIS30, SERIAL30);
324+
305325
#[cfg(feature = "_ns")]
306326
impl_wdt!(WDT, WDT31, WDT31, 0);
307327
#[cfg(feature = "_s")]

embassy-nrf/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,8 @@ pub mod rng;
146146
#[cfg(not(feature = "_nrf54l"))] // TODO
147147
#[cfg(not(any(feature = "_nrf51", feature = "nrf52820", feature = "_nrf5340-net")))]
148148
pub mod saadc;
149-
#[cfg(not(feature = "_nrf54l"))] // TODO
150149
#[cfg(not(feature = "_nrf51"))]
151150
pub mod spim;
152-
#[cfg(not(feature = "_nrf54l"))] // TODO
153151
#[cfg(not(feature = "_nrf51"))]
154152
pub mod spis;
155153
#[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))]

embassy-nrf/src/spim.rs

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ use embassy_embedded_hal::SetConfig;
1313
use embassy_hal_internal::{Peri, PeripheralType};
1414
use embassy_sync::waitqueue::AtomicWaker;
1515
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
16+
#[cfg(feature = "_nrf54l")]
17+
pub use pac::spim::vals::Order as BitOrder;
18+
#[cfg(not(feature = "_nrf54l"))]
1619
pub use pac::spim::vals::{Frequency, Order as BitOrder};
1720

1821
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
@@ -36,9 +39,17 @@ pub enum Error {
3639
#[non_exhaustive]
3740
#[derive(Clone)]
3841
pub struct Config {
42+
#[cfg(not(feature = "_nrf54l"))]
3943
/// Frequency
4044
pub frequency: Frequency,
4145

46+
#[cfg(feature = "_nrf54l")]
47+
/// The prescaler is used to set the SPI frequency. The frequency is `core_clock/prescaler`.
48+
/// The core_clock in the MCU PD is 128 MHz. In the PERI and LP PDs, it is 16 MHz. The
49+
/// prescaler must be an *even* value in the range [4,126] for the MCD PD and [2,126] for the
50+
/// PERI and LP PDs.
51+
pub prescaler: u8,
52+
4253
/// SPI mode
4354
pub mode: Mode,
4455

@@ -58,10 +69,27 @@ pub struct Config {
5869
pub mosi_drive: OutputDrive,
5970
}
6071

72+
#[cfg(not(feature = "_nrf54l"))]
73+
/// SPIM configuration error
74+
pub type ConfigError = ();
75+
76+
#[cfg(feature = "_nrf54l")]
77+
/// SPIM configuration error
78+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
80+
#[non_exhaustive]
81+
pub enum ConfigError {
82+
/// Invalid prescaler
83+
InvalidPrescaler(u8),
84+
}
85+
6186
impl Default for Config {
6287
fn default() -> Self {
6388
Self {
89+
#[cfg(not(feature = "_nrf54l"))]
6490
frequency: Frequency::M1,
91+
#[cfg(feature = "_nrf54l")]
92+
prescaler: 16, // 8 MHz in MCU PD and 1 MHz in PERI and LP PDs
6593
mode: MODE_0,
6694
bit_order: BitOrder::MSB_FIRST,
6795
orc: 0x00,
@@ -230,13 +258,29 @@ impl<'d, T: Instance> Spim<'d, T> {
230258

231259
// Set up the DMA read.
232260
let (rx_ptr, rx_len) = xfer_params(rx as *mut u8 as _, rx.len() as _, offset, length);
233-
r.rxd().ptr().write_value(rx_ptr);
234-
r.rxd().maxcnt().write(|w| w.set_maxcnt(rx_len as _));
261+
#[cfg(not(feature = "_nrf54l"))]
262+
{
263+
r.rxd().ptr().write_value(rx_ptr);
264+
r.rxd().maxcnt().write(|w| w.set_maxcnt(rx_len as _));
265+
}
266+
#[cfg(feature = "_nrf54l")]
267+
{
268+
r.dma().rx().ptr().write_value(rx_ptr);
269+
r.dma().rx().maxcnt().write(|w| w.set_maxcnt(rx_len as _));
270+
}
235271

236272
// Set up the DMA write.
237273
let (tx_ptr, tx_len) = xfer_params(tx as *const u8 as _, tx.len() as _, offset, length);
238-
r.txd().ptr().write_value(tx_ptr);
239-
r.txd().maxcnt().write(|w| w.set_maxcnt(tx_len as _));
274+
#[cfg(not(feature = "_nrf54l"))]
275+
{
276+
r.txd().ptr().write_value(tx_ptr);
277+
r.txd().maxcnt().write(|w| w.set_maxcnt(tx_len as _));
278+
}
279+
#[cfg(feature = "_nrf54l")]
280+
{
281+
r.dma().tx().ptr().write_value(tx_ptr);
282+
r.dma().tx().maxcnt().write(|w| w.set_maxcnt(tx_len as _));
283+
}
240284

241285
/*
242286
trace!("XFER: offset: {}, length: {}", offset, length);
@@ -601,9 +645,18 @@ impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spim<'d, T> {
601645

602646
impl<'d, T: Instance> SetConfig for Spim<'d, T> {
603647
type Config = Config;
604-
type ConfigError = ();
648+
type ConfigError = ConfigError;
605649
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
606650
let r = T::regs();
651+
652+
#[cfg(feature = "_nrf54l")]
653+
{
654+
let min = if r.as_ptr() == pac::SPIM00.as_ptr() { 4 } else { 2 };
655+
if config.prescaler % 2 != 0 || config.prescaler < min || config.prescaler > 126 {
656+
return Err(Self::ConfigError::InvalidPrescaler(config.prescaler));
657+
}
658+
}
659+
607660
// Configure mode.
608661
let mode = config.mode;
609662
r.config().write(|w| {
@@ -629,8 +682,10 @@ impl<'d, T: Instance> SetConfig for Spim<'d, T> {
629682
});
630683

631684
// Configure frequency.
632-
let frequency = config.frequency;
633-
r.frequency().write(|w| w.set_frequency(frequency));
685+
#[cfg(not(feature = "_nrf54l"))]
686+
r.frequency().write(|w| w.set_frequency(config.frequency));
687+
#[cfg(feature = "_nrf54l")]
688+
r.prescaler().write(|w| w.set_divisor(config.prescaler));
634689

635690
// Set over-read character
636691
let orc = config.orc;

embassy-nrf/src/spis.rs

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -218,15 +218,31 @@ impl<'d, T: Instance> Spis<'d, T> {
218218
if tx.len() > EASY_DMA_SIZE {
219219
return Err(Error::TxBufferTooLong);
220220
}
221-
r.txd().ptr().write_value(tx as *const u8 as _);
222-
r.txd().maxcnt().write(|w| w.set_maxcnt(tx.len() as _));
221+
#[cfg(not(feature = "_nrf54l"))]
222+
{
223+
r.txd().ptr().write_value(tx as *const u8 as _);
224+
r.txd().maxcnt().write(|w| w.set_maxcnt(tx.len() as _));
225+
}
226+
#[cfg(feature = "_nrf54l")]
227+
{
228+
r.dma().tx().ptr().write_value(tx as *const u8 as _);
229+
r.dma().tx().maxcnt().write(|w| w.set_maxcnt(tx.len() as _));
230+
}
223231

224232
// Set up the DMA read.
225233
if rx.len() > EASY_DMA_SIZE {
226234
return Err(Error::RxBufferTooLong);
227235
}
228-
r.rxd().ptr().write_value(rx as *mut u8 as _);
229-
r.rxd().maxcnt().write(|w| w.set_maxcnt(rx.len() as _));
236+
#[cfg(not(feature = "_nrf54l"))]
237+
{
238+
r.rxd().ptr().write_value(rx as *mut u8 as _);
239+
r.rxd().maxcnt().write(|w| w.set_maxcnt(rx.len() as _));
240+
}
241+
#[cfg(feature = "_nrf54l")]
242+
{
243+
r.dma().rx().ptr().write_value(rx as *mut u8 as _);
244+
r.dma().rx().maxcnt().write(|w| w.set_maxcnt(rx.len() as _));
245+
}
230246

231247
// Reset end event.
232248
r.events_end().write_value(0);
@@ -254,8 +270,14 @@ impl<'d, T: Instance> Spis<'d, T> {
254270
// Wait for 'end' event.
255271
while r.events_end().read() == 0 {}
256272

257-
let n_rx = r.rxd().amount().read().0 as usize;
258-
let n_tx = r.txd().amount().read().0 as usize;
273+
#[cfg(not(feature = "_nrf54l"))]
274+
let (n_rx, n_tx) = (r.rxd().amount().read().0 as usize, r.txd().amount().read().0 as usize);
275+
276+
#[cfg(feature = "_nrf54l")]
277+
let (n_rx, n_tx) = (
278+
r.dma().rx().amount().read().0 as usize,
279+
r.dma().tx().amount().read().0 as usize,
280+
);
259281

260282
compiler_fence(Ordering::SeqCst);
261283

@@ -320,8 +342,14 @@ impl<'d, T: Instance> Spis<'d, T> {
320342
})
321343
.await;
322344

323-
let n_rx = r.rxd().amount().read().0 as usize;
324-
let n_tx = r.txd().amount().read().0 as usize;
345+
#[cfg(not(feature = "_nrf54l"))]
346+
let (n_rx, n_tx) = (r.rxd().amount().read().0 as usize, r.txd().amount().read().0 as usize);
347+
348+
#[cfg(feature = "_nrf54l")]
349+
let (n_rx, n_tx) = (
350+
r.dma().rx().amount().read().0 as usize,
351+
r.dma().tx().amount().read().0 as usize,
352+
);
325353

326354
compiler_fence(Ordering::SeqCst);
327355

examples/nrf54l15/src/bin/spim.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
use defmt::{info, unwrap};
5+
use embassy_executor::Spawner;
6+
use embassy_nrf::gpio::{Level, Output, OutputDrive};
7+
use embassy_nrf::{bind_interrupts, peripherals, spim};
8+
use {defmt_rtt as _, panic_probe as _};
9+
10+
bind_interrupts!(struct Irqs {
11+
SERIAL20 => spim::InterruptHandler<peripherals::TWISPI20>;
12+
});
13+
14+
#[embassy_executor::main]
15+
async fn main(_spawner: Spawner) {
16+
let p = embassy_nrf::init(Default::default());
17+
info!("Running!");
18+
19+
let mut config = spim::Config::default();
20+
config.prescaler = 16;
21+
22+
let mut spim = spim::Spim::new(p.TWISPI20, Irqs, p.P2_01, p.P2_02, p.P2_04, config);
23+
24+
let mut ncs = Output::new(p.P2_05, Level::High, OutputDrive::Standard);
25+
26+
// Example on how to talk to an ENC28J60 chip
27+
28+
// softreset
29+
cortex_m::asm::delay(10);
30+
ncs.set_low();
31+
cortex_m::asm::delay(5);
32+
let tx = [0xFF];
33+
unwrap!(spim.transfer(&mut [], &tx).await);
34+
cortex_m::asm::delay(10);
35+
ncs.set_high();
36+
37+
cortex_m::asm::delay(100000);
38+
39+
let mut rx = [0; 2];
40+
41+
// read ESTAT
42+
cortex_m::asm::delay(5000);
43+
ncs.set_low();
44+
cortex_m::asm::delay(5000);
45+
let tx = [0b000_11101, 0];
46+
unwrap!(spim.transfer(&mut rx, &tx).await);
47+
cortex_m::asm::delay(5000);
48+
ncs.set_high();
49+
info!("estat: {=[?]}", rx);
50+
51+
// Switch to bank 3
52+
cortex_m::asm::delay(10);
53+
ncs.set_low();
54+
cortex_m::asm::delay(5);
55+
let tx = [0b100_11111, 0b11];
56+
unwrap!(spim.transfer(&mut rx, &tx).await);
57+
cortex_m::asm::delay(10);
58+
ncs.set_high();
59+
60+
// read EREVID
61+
cortex_m::asm::delay(10);
62+
ncs.set_low();
63+
cortex_m::asm::delay(5);
64+
let tx = [0b000_10010, 0];
65+
unwrap!(spim.transfer(&mut rx, &tx).await);
66+
cortex_m::asm::delay(10);
67+
ncs.set_high();
68+
69+
info!("erevid: {=[?]}", rx);
70+
}

0 commit comments

Comments
 (0)