aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-nrf/src/chips/nrf52805.rs2
-rw-r--r--embassy-nrf/src/chips/nrf52810.rs2
-rw-r--r--embassy-nrf/src/chips/nrf52811.rs3
-rw-r--r--embassy-nrf/src/chips/nrf52820.rs3
-rw-r--r--embassy-nrf/src/chips/nrf52832.rs4
-rw-r--r--embassy-nrf/src/chips/nrf52833.rs4
-rw-r--r--embassy-nrf/src/chips/nrf52840.rs4
-rw-r--r--embassy-nrf/src/chips/nrf5340_app.rs5
-rw-r--r--embassy-nrf/src/chips/nrf5340_net.rs1
-rw-r--r--embassy-nrf/src/chips/nrf9160.rs5
-rw-r--r--embassy-nrf/src/lib.rs1
-rw-r--r--embassy-nrf/src/spis.rs539
-rw-r--r--examples/nrf/src/bin/spis.rs27
13 files changed, 600 insertions, 0 deletions
diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs
index dec31a84c..11a6840c8 100644
--- a/embassy-nrf/src/chips/nrf52805.rs
+++ b/embassy-nrf/src/chips/nrf52805.rs
@@ -131,6 +131,8 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
131 131
132impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0); 132impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0);
133 133
134impl_spis!(SPI0, SPIS0, SPIM0_SPIS0_SPI0);
135
134impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0); 136impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0);
135 137
136impl_timer!(TIMER0, TIMER0, TIMER0); 138impl_timer!(TIMER0, TIMER0, TIMER0);
diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs
index e57a4a383..3614cd229 100644
--- a/embassy-nrf/src/chips/nrf52810.rs
+++ b/embassy-nrf/src/chips/nrf52810.rs
@@ -137,6 +137,8 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
137 137
138impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0); 138impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0);
139 139
140impl_spis!(SPI0, SPIS0, SPIM0_SPIS0_SPI0);
141
140impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0); 142impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0);
141 143
142impl_pwm!(PWM0, PWM0, PWM0); 144impl_pwm!(PWM0, PWM0, PWM0);
diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs
index 918404cf1..dc4a8660e 100644
--- a/embassy-nrf/src/chips/nrf52811.rs
+++ b/embassy-nrf/src/chips/nrf52811.rs
@@ -138,6 +138,9 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
138impl_spim!(TWISPI0, SPIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); 138impl_spim!(TWISPI0, SPIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
139impl_spim!(SPI1, SPIM1, SPIM1_SPIS1_SPI1); 139impl_spim!(SPI1, SPIM1, SPIM1_SPIS1_SPI1);
140 140
141impl_spis!(TWISPI0, SPIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
142impl_spis!(SPI1, SPIS1, SPIM1_SPIS1_SPI1);
143
141impl_twim!(TWISPI0, TWIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); 144impl_twim!(TWISPI0, TWIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
142 145
143impl_pwm!(PWM0, PWM0, PWM0); 146impl_pwm!(PWM0, PWM0, PWM0);
diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs
index dba033b0f..7668920bd 100644
--- a/embassy-nrf/src/chips/nrf52820.rs
+++ b/embassy-nrf/src/chips/nrf52820.rs
@@ -136,6 +136,9 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
136impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); 136impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
137impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); 137impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
138 138
139impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
140impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
141
139impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); 142impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
140impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); 143impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
141 144
diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs
index 81e66c193..851643b55 100644
--- a/embassy-nrf/src/chips/nrf52832.rs
+++ b/embassy-nrf/src/chips/nrf52832.rs
@@ -146,6 +146,10 @@ impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
146impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); 146impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
147impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2); 147impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2);
148 148
149impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
150impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
151impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2);
152
149impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); 153impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
150impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); 154impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
151 155
diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs
index 92499e3c9..5342ba8c2 100644
--- a/embassy-nrf/src/chips/nrf52833.rs
+++ b/embassy-nrf/src/chips/nrf52833.rs
@@ -174,6 +174,10 @@ impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
174impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2); 174impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2);
175impl_spim!(SPI3, SPIM3, SPIM3); 175impl_spim!(SPI3, SPIM3, SPIM3);
176 176
177impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
178impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
179impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2);
180
177impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); 181impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
178impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); 182impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
179 183
diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs
index 4beadfba8..a330aef8b 100644
--- a/embassy-nrf/src/chips/nrf52840.rs
+++ b/embassy-nrf/src/chips/nrf52840.rs
@@ -177,6 +177,10 @@ impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
177impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2); 177impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2);
178impl_spim!(SPI3, SPIM3, SPIM3); 178impl_spim!(SPI3, SPIM3, SPIM3);
179 179
180impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
181impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
182impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2);
183
180impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); 184impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
181impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); 185impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
182 186
diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs
index 7845d4a8e..1c027ec02 100644
--- a/embassy-nrf/src/chips/nrf5340_app.rs
+++ b/embassy-nrf/src/chips/nrf5340_app.rs
@@ -361,6 +361,11 @@ impl_spim!(UARTETWISPI1, SPIM1, SERIAL1);
361impl_spim!(UARTETWISPI2, SPIM2, SERIAL2); 361impl_spim!(UARTETWISPI2, SPIM2, SERIAL2);
362impl_spim!(UARTETWISPI3, SPIM3, SERIAL3); 362impl_spim!(UARTETWISPI3, SPIM3, SERIAL3);
363 363
364impl_spis!(UARTETWISPI0, SPIS0, SERIAL0);
365impl_spis!(UARTETWISPI1, SPIS1, SERIAL1);
366impl_spis!(UARTETWISPI2, SPIS2, SERIAL2);
367impl_spis!(UARTETWISPI3, SPIS3, SERIAL3);
368
364impl_twim!(UARTETWISPI0, TWIM0, SERIAL0); 369impl_twim!(UARTETWISPI0, TWIM0, SERIAL0);
365impl_twim!(UARTETWISPI1, TWIM1, SERIAL1); 370impl_twim!(UARTETWISPI1, TWIM1, SERIAL1);
366impl_twim!(UARTETWISPI2, TWIM2, SERIAL2); 371impl_twim!(UARTETWISPI2, TWIM2, SERIAL2);
diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs
index ae136e09d..3bcd44fcb 100644
--- a/embassy-nrf/src/chips/nrf5340_net.rs
+++ b/embassy-nrf/src/chips/nrf5340_net.rs
@@ -238,6 +238,7 @@ embassy_hal_common::peripherals! {
238 238
239impl_uarte!(UARTETWISPI0, UARTE0, SERIAL0); 239impl_uarte!(UARTETWISPI0, UARTE0, SERIAL0);
240impl_spim!(UARTETWISPI0, SPIM0, SERIAL0); 240impl_spim!(UARTETWISPI0, SPIM0, SERIAL0);
241impl_spis!(UARTETWISPI0, SPIS0, SERIAL0);
241impl_twim!(UARTETWISPI0, TWIM0, SERIAL0); 242impl_twim!(UARTETWISPI0, TWIM0, SERIAL0);
242 243
243impl_timer!(TIMER0, TIMER0, TIMER0); 244impl_timer!(TIMER0, TIMER0, TIMER0);
diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs
index b5a53ed80..0dfa112fe 100644
--- a/embassy-nrf/src/chips/nrf9160.rs
+++ b/embassy-nrf/src/chips/nrf9160.rs
@@ -275,6 +275,11 @@ impl_spim!(UARTETWISPI1, SPIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
275impl_spim!(UARTETWISPI2, SPIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); 275impl_spim!(UARTETWISPI2, SPIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
276impl_spim!(UARTETWISPI3, SPIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); 276impl_spim!(UARTETWISPI3, SPIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
277 277
278impl_spis!(UARTETWISPI0, SPIS0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
279impl_spis!(UARTETWISPI1, SPIS1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
280impl_spis!(UARTETWISPI2, SPIS2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
281impl_spis!(UARTETWISPI3, SPIS3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
282
278impl_twim!(UARTETWISPI0, TWIM0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); 283impl_twim!(UARTETWISPI0, TWIM0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
279impl_twim!(UARTETWISPI1, TWIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); 284impl_twim!(UARTETWISPI1, TWIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
280impl_twim!(UARTETWISPI2, TWIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); 285impl_twim!(UARTETWISPI2, TWIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index b6fe046cf..5726f1181 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -96,6 +96,7 @@ pub mod rng;
96#[cfg(not(any(feature = "nrf52820", feature = "_nrf5340-net")))] 96#[cfg(not(any(feature = "nrf52820", feature = "_nrf5340-net")))]
97pub mod saadc; 97pub mod saadc;
98pub mod spim; 98pub mod spim;
99pub mod spis;
99#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] 100#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
100pub mod temp; 101pub mod temp;
101pub mod timer; 102pub mod timer;
diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs
new file mode 100644
index 000000000..44af61a19
--- /dev/null
+++ b/embassy-nrf/src/spis.rs
@@ -0,0 +1,539 @@
1#![macro_use]
2use core::future::poll_fn;
3use core::sync::atomic::{compiler_fence, Ordering};
4use core::task::Poll;
5
6use embassy_embedded_hal::SetConfig;
7use embassy_hal_common::{into_ref, PeripheralRef};
8pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
9
10use crate::chip::FORCE_COPY_BUFFER_SIZE;
11use crate::gpio::sealed::Pin as _;
12use crate::gpio::{self, AnyPin, Pin as GpioPin};
13use crate::interrupt::{Interrupt, InterruptExt};
14use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut};
15use crate::{pac, Peripheral};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19#[non_exhaustive]
20pub enum Error {
21 TxBufferTooLong,
22 RxBufferTooLong,
23 /// EasyDMA can only read from data memory, read only buffers in flash will fail.
24 DMABufferNotInDataMemory,
25}
26
27/// Interface for the SPIS peripheral using EasyDMA to offload the transmission and reception workload.
28///
29/// For more details about EasyDMA, consult the module documentation.
30pub struct Spis<'d, T: Instance> {
31 _p: PeripheralRef<'d, T>,
32}
33
34#[non_exhaustive]
35pub struct Config {
36 pub mode: Mode,
37 pub orc: u8,
38 pub def: u8,
39 pub auto_acquire: bool,
40}
41
42impl Default for Config {
43 fn default() -> Self {
44 Self {
45 mode: MODE_0,
46 orc: 0x00,
47 def: 0x00,
48 auto_acquire: true,
49 }
50 }
51}
52
53impl<'d, T: Instance> Spis<'d, T> {
54 pub fn new(
55 spis: impl Peripheral<P = T> + 'd,
56 irq: impl Peripheral<P = T::Interrupt> + 'd,
57 cs: impl Peripheral<P = impl GpioPin> + 'd,
58 sck: impl Peripheral<P = impl GpioPin> + 'd,
59 miso: impl Peripheral<P = impl GpioPin> + 'd,
60 mosi: impl Peripheral<P = impl GpioPin> + 'd,
61 config: Config,
62 ) -> Self {
63 into_ref!(cs, sck, miso, mosi);
64 Self::new_inner(
65 spis,
66 irq,
67 cs.map_into(),
68 sck.map_into(),
69 Some(miso.map_into()),
70 Some(mosi.map_into()),
71 config,
72 )
73 }
74
75 pub fn new_txonly(
76 spis: impl Peripheral<P = T> + 'd,
77 irq: impl Peripheral<P = T::Interrupt> + 'd,
78 cs: impl Peripheral<P = impl GpioPin> + 'd,
79 sck: impl Peripheral<P = impl GpioPin> + 'd,
80 miso: impl Peripheral<P = impl GpioPin> + 'd,
81 config: Config,
82 ) -> Self {
83 into_ref!(cs, sck, miso);
84 Self::new_inner(
85 spis,
86 irq,
87 cs.map_into(),
88 sck.map_into(),
89 Some(miso.map_into()),
90 None,
91 config,
92 )
93 }
94
95 pub fn new_rxonly(
96 spis: impl Peripheral<P = T> + 'd,
97 irq: impl Peripheral<P = T::Interrupt> + 'd,
98 cs: impl Peripheral<P = impl GpioPin> + 'd,
99 sck: impl Peripheral<P = impl GpioPin> + 'd,
100 mosi: impl Peripheral<P = impl GpioPin> + 'd,
101 config: Config,
102 ) -> Self {
103 into_ref!(cs, sck, mosi);
104 Self::new_inner(
105 spis,
106 irq,
107 cs.map_into(),
108 sck.map_into(),
109 None,
110 Some(mosi.map_into()),
111 config,
112 )
113 }
114
115 fn new_inner(
116 spis: impl Peripheral<P = T> + 'd,
117 irq: impl Peripheral<P = T::Interrupt> + 'd,
118 cs: PeripheralRef<'d, AnyPin>,
119 sck: PeripheralRef<'d, AnyPin>,
120 miso: Option<PeripheralRef<'d, AnyPin>>,
121 mosi: Option<PeripheralRef<'d, AnyPin>>,
122 config: Config,
123 ) -> Self {
124 compiler_fence(Ordering::SeqCst);
125
126 into_ref!(spis, irq, cs, sck);
127
128 let r = T::regs();
129
130 // Configure pins.
131 sck.conf().write(|w| w.input().connect().drive().h0h1());
132 r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) });
133 cs.conf().write(|w| w.input().connect().drive().h0h1());
134 r.psel.csn.write(|w| unsafe { w.bits(cs.psel_bits()) });
135 if let Some(mosi) = &mosi {
136 mosi.conf().write(|w| w.input().connect().drive().h0h1());
137 r.psel.mosi.write(|w| unsafe { w.bits(mosi.psel_bits()) });
138 }
139 if let Some(miso) = &miso {
140 miso.conf().write(|w| w.dir().output().drive().h0h1());
141 r.psel.miso.write(|w| unsafe { w.bits(miso.psel_bits()) });
142 }
143
144 // Enable SPIS instance.
145 r.enable.write(|w| w.enable().enabled());
146
147 // Configure mode.
148 let mode = config.mode;
149 r.config.write(|w| {
150 match mode {
151 MODE_0 => {
152 w.order().msb_first();
153 w.cpol().active_high();
154 w.cpha().leading();
155 }
156 MODE_1 => {
157 w.order().msb_first();
158 w.cpol().active_high();
159 w.cpha().trailing();
160 }
161 MODE_2 => {
162 w.order().msb_first();
163 w.cpol().active_low();
164 w.cpha().leading();
165 }
166 MODE_3 => {
167 w.order().msb_first();
168 w.cpol().active_low();
169 w.cpha().trailing();
170 }
171 }
172
173 w
174 });
175
176 // Set over-read character.
177 let orc = config.orc;
178 r.orc.write(|w| unsafe { w.orc().bits(orc) });
179
180 // Set default character.
181 let def = config.def;
182 r.def.write(|w| unsafe { w.def().bits(def) });
183
184 // Configure auto-acquire on 'transfer end' event.
185 if config.auto_acquire {
186 r.shorts.write(|w| w.end_acquire().bit(true));
187 }
188
189 // Disable all events interrupts.
190 r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
191
192 irq.set_handler(Self::on_interrupt);
193 irq.unpend();
194 irq.enable();
195
196 Self { _p: spis }
197 }
198
199 fn on_interrupt(_: *mut ()) {
200 let r = T::regs();
201 let s = T::state();
202
203 if r.events_end.read().bits() != 0 {
204 s.waker.wake();
205 r.intenclr.write(|w| w.end().clear());
206 }
207
208 if r.events_acquired.read().bits() != 0 {
209 s.waker.wake();
210 r.intenclr.write(|w| w.acquired().clear());
211 }
212 }
213
214 fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
215 slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?;
216 // NOTE: RAM slice check for rx is not necessary, as a mutable
217 // slice can only be built from data located in RAM.
218
219 compiler_fence(Ordering::SeqCst);
220
221 let r = T::regs();
222
223 // Set up the DMA write.
224 let (ptr, len) = slice_ptr_parts(tx);
225 r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
226 r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
227
228 // Set up the DMA read.
229 let (ptr, len) = slice_ptr_parts_mut(rx);
230 r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
231 r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
232
233 // Reset end event.
234 r.events_end.reset();
235
236 // Release the semaphore.
237 r.tasks_release.write(|w| unsafe { w.bits(1) });
238
239 Ok(())
240 }
241
242 fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> {
243 compiler_fence(Ordering::SeqCst);
244 let r = T::regs();
245
246 // Acquire semaphore.
247 if r.semstat.read().bits() != 1 {
248 r.events_acquired.reset();
249 r.tasks_acquire.write(|w| unsafe { w.bits(1) });
250 // Wait until CPU has acquired the semaphore.
251 while r.semstat.read().bits() != 1 {}
252 }
253
254 self.prepare(rx, tx)?;
255
256 // Wait for 'end' event.
257 while r.events_end.read().bits() == 0 {}
258
259 let n_rx = r.rxd.amount.read().bits() as usize;
260 let n_tx = r.txd.amount.read().bits() as usize;
261
262 compiler_fence(Ordering::SeqCst);
263
264 Ok((n_rx, n_tx))
265 }
266
267 fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> {
268 match self.blocking_inner_from_ram(rx, tx) {
269 Ok(n) => Ok(n),
270 Err(Error::DMABufferNotInDataMemory) => {
271 trace!("Copying SPIS tx buffer into RAM for DMA");
272 let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
273 tx_ram_buf.copy_from_slice(tx);
274 self.blocking_inner_from_ram(rx, tx_ram_buf)
275 }
276 Err(error) => Err(error),
277 }
278 }
279
280 async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> {
281 let r = T::regs();
282 let s = T::state();
283
284 // Clear status register.
285 r.status.write(|w| w.overflow().clear().overread().clear());
286
287 // Acquire semaphore.
288 if r.semstat.read().bits() != 1 {
289 // Reset and enable the acquire event.
290 r.events_acquired.reset();
291 r.intenset.write(|w| w.acquired().set());
292
293 // Request acquiring the SPIS semaphore.
294 r.tasks_acquire.write(|w| unsafe { w.bits(1) });
295
296 // Wait until CPU has acquired the semaphore.
297 poll_fn(|cx| {
298 s.waker.register(cx.waker());
299 if r.events_acquired.read().bits() == 1 {
300 r.events_acquired.reset();
301 return Poll::Ready(());
302 }
303 Poll::Pending
304 })
305 .await;
306 }
307
308 self.prepare(rx, tx)?;
309
310 // Wait for 'end' event.
311 r.intenset.write(|w| w.end().set());
312 poll_fn(|cx| {
313 s.waker.register(cx.waker());
314 if r.events_end.read().bits() != 0 {
315 r.events_end.reset();
316 return Poll::Ready(());
317 }
318 Poll::Pending
319 })
320 .await;
321
322 let n_rx = r.rxd.amount.read().bits() as usize;
323 let n_tx = r.txd.amount.read().bits() as usize;
324
325 compiler_fence(Ordering::SeqCst);
326
327 Ok((n_rx, n_tx))
328 }
329
330 async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> {
331 match self.async_inner_from_ram(rx, tx).await {
332 Ok(n) => Ok(n),
333 Err(Error::DMABufferNotInDataMemory) => {
334 trace!("Copying SPIS tx buffer into RAM for DMA");
335 let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
336 tx_ram_buf.copy_from_slice(tx);
337 self.async_inner_from_ram(rx, tx_ram_buf).await
338 }
339 Err(error) => Err(error),
340 }
341 }
342
343 /// Reads data from the SPI bus without sending anything. Blocks until `cs` is deasserted.
344 /// Returns number of bytes read.
345 pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<usize, Error> {
346 self.blocking_inner(data, &[]).map(|n| n.0)
347 }
348
349 /// Simultaneously sends and receives data. Blocks until the transmission is completed.
350 /// If necessary, the write buffer will be copied into RAM (see struct description for detail).
351 /// Returns number of bytes transferred `(n_rx, n_tx)`.
352 pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
353 self.blocking_inner(read, write)
354 }
355
356 /// Same as [`blocking_transfer`](Spis::blocking_transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
357 /// Returns number of bytes transferred `(n_rx, n_tx)`.
358 pub fn blocking_transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
359 self.blocking_inner_from_ram(read, write)
360 }
361
362 /// Simultaneously sends and receives data.
363 /// Places the received data into the same buffer and blocks until the transmission is completed.
364 /// Returns number of bytes transferred.
365 pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<usize, Error> {
366 self.blocking_inner_from_ram(data, data).map(|n| n.0)
367 }
368
369 /// Sends data, discarding any received data. Blocks until the transmission is completed.
370 /// If necessary, the write buffer will be copied into RAM (see struct description for detail).
371 /// Returns number of bytes written.
372 pub fn blocking_write(&mut self, data: &[u8]) -> Result<usize, Error> {
373 self.blocking_inner(&mut [], data).map(|n| n.1)
374 }
375
376 /// Same as [`blocking_write`](Spis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
377 /// Returns number of bytes written.
378 pub fn blocking_write_from_ram(&mut self, data: &[u8]) -> Result<usize, Error> {
379 self.blocking_inner_from_ram(&mut [], data).map(|n| n.1)
380 }
381
382 /// Reads data from the SPI bus without sending anything.
383 /// Returns number of bytes read.
384 pub async fn read(&mut self, data: &mut [u8]) -> Result<usize, Error> {
385 self.async_inner(data, &[]).await.map(|n| n.0)
386 }
387
388 /// Simultaneously sends and receives data.
389 /// If necessary, the write buffer will be copied into RAM (see struct description for detail).
390 /// Returns number of bytes transferred `(n_rx, n_tx)`.
391 pub async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
392 self.async_inner(read, write).await
393 }
394
395 /// Same as [`transfer`](Spis::transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
396 /// Returns number of bytes transferred `(n_rx, n_tx)`.
397 pub async fn transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
398 self.async_inner_from_ram(read, write).await
399 }
400
401 /// Simultaneously sends and receives data. Places the received data into the same buffer.
402 /// Returns number of bytes transferred.
403 pub async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<usize, Error> {
404 self.async_inner_from_ram(data, data).await.map(|n| n.0)
405 }
406
407 /// Sends data, discarding any received data.
408 /// If necessary, the write buffer will be copied into RAM (see struct description for detail).
409 /// Returns number of bytes written.
410 pub async fn write(&mut self, data: &[u8]) -> Result<usize, Error> {
411 self.async_inner(&mut [], data).await.map(|n| n.1)
412 }
413
414 /// Same as [`write`](Spis::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
415 /// Returns number of bytes written.
416 pub async fn write_from_ram(&mut self, data: &[u8]) -> Result<usize, Error> {
417 self.async_inner_from_ram(&mut [], data).await.map(|n| n.1)
418 }
419
420 /// Checks if last transaction overread.
421 pub fn is_overread(&mut self) -> bool {
422 T::regs().status.read().overread().is_present()
423 }
424
425 /// Checks if last transaction overflowed.
426 pub fn is_overflow(&mut self) -> bool {
427 T::regs().status.read().overflow().is_present()
428 }
429}
430
431impl<'d, T: Instance> Drop for Spis<'d, T> {
432 fn drop(&mut self) {
433 trace!("spis drop");
434
435 // Disable
436 let r = T::regs();
437 r.enable.write(|w| w.enable().disabled());
438
439 gpio::deconfigure_pin(r.psel.sck.read().bits());
440 gpio::deconfigure_pin(r.psel.csn.read().bits());
441 gpio::deconfigure_pin(r.psel.miso.read().bits());
442 gpio::deconfigure_pin(r.psel.mosi.read().bits());
443
444 trace!("spis drop: done");
445 }
446}
447
448pub(crate) mod sealed {
449 use embassy_sync::waitqueue::AtomicWaker;
450
451 use super::*;
452
453 pub struct State {
454 pub waker: AtomicWaker,
455 }
456
457 impl State {
458 pub const fn new() -> Self {
459 Self {
460 waker: AtomicWaker::new(),
461 }
462 }
463 }
464
465 pub trait Instance {
466 fn regs() -> &'static pac::spis0::RegisterBlock;
467 fn state() -> &'static State;
468 }
469}
470
471pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
472 type Interrupt: Interrupt;
473}
474
475macro_rules! impl_spis {
476 ($type:ident, $pac_type:ident, $irq:ident) => {
477 impl crate::spis::sealed::Instance for peripherals::$type {
478 fn regs() -> &'static pac::spis0::RegisterBlock {
479 unsafe { &*pac::$pac_type::ptr() }
480 }
481 fn state() -> &'static crate::spis::sealed::State {
482 static STATE: crate::spis::sealed::State = crate::spis::sealed::State::new();
483 &STATE
484 }
485 }
486 impl crate::spis::Instance for peripherals::$type {
487 type Interrupt = crate::interrupt::$irq;
488 }
489 };
490}
491
492// ====================
493
494impl<'d, T: Instance> SetConfig for Spis<'d, T> {
495 type Config = Config;
496 fn set_config(&mut self, config: &Self::Config) {
497 let r = T::regs();
498 // Configure mode.
499 let mode = config.mode;
500 r.config.write(|w| {
501 match mode {
502 MODE_0 => {
503 w.order().msb_first();
504 w.cpol().active_high();
505 w.cpha().leading();
506 }
507 MODE_1 => {
508 w.order().msb_first();
509 w.cpol().active_high();
510 w.cpha().trailing();
511 }
512 MODE_2 => {
513 w.order().msb_first();
514 w.cpol().active_low();
515 w.cpha().leading();
516 }
517 MODE_3 => {
518 w.order().msb_first();
519 w.cpol().active_low();
520 w.cpha().trailing();
521 }
522 }
523
524 w
525 });
526
527 // Set over-read character.
528 let orc = config.orc;
529 r.orc.write(|w| unsafe { w.orc().bits(orc) });
530
531 // Set default character.
532 let def = config.def;
533 r.def.write(|w| unsafe { w.def().bits(def) });
534
535 // Configure auto-acquire on 'transfer end' event.
536 let auto_acquire = config.auto_acquire;
537 r.shorts.write(|w| w.end_acquire().bit(auto_acquire));
538 }
539}
diff --git a/examples/nrf/src/bin/spis.rs b/examples/nrf/src/bin/spis.rs
new file mode 100644
index 000000000..fe3b0c53d
--- /dev/null
+++ b/examples/nrf/src/bin/spis.rs
@@ -0,0 +1,27 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::info;
6use embassy_executor::Spawner;
7use embassy_nrf::interrupt;
8use embassy_nrf::spis::{Config, Spis};
9use {defmt_rtt as _, panic_probe as _};
10
11#[embassy_executor::main]
12async fn main(_spawner: Spawner) {
13 let p = embassy_nrf::init(Default::default());
14 info!("Running!");
15
16 let irq = interrupt::take!(SPIM2_SPIS2_SPI2);
17 let mut spis = Spis::new(p.SPI2, irq, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default());
18
19 loop {
20 let mut rx_buf = [0_u8; 64];
21 let tx_buf = [1_u8, 2, 3, 4, 5, 6, 7, 8];
22 if let Ok((n_rx, n_tx)) = spis.transfer(&mut rx_buf, &tx_buf).await {
23 info!("RX: {:?}", rx_buf[..n_rx]);
24 info!("TX: {:?}", tx_buf[..n_tx]);
25 }
26 }
27}