aboutsummaryrefslogtreecommitdiff
path: root/embassy-nrf/src/spis.rs
diff options
context:
space:
mode:
authorHenrik Alsér <[email protected]>2022-11-05 00:15:43 +0100
committerHenrik Alsér <[email protected]>2022-11-05 00:15:43 +0100
commit1920e90dcdbebc1e2f86001f1491a9f28eb0f0f3 (patch)
tree5cb6408c92df3b3bf09d4271e800caef85620579 /embassy-nrf/src/spis.rs
parentb99533607ceed225dd12ae73aaa9a0d969a7365e (diff)
embassy-nrf: Add SPIS module
Diffstat (limited to 'embassy-nrf/src/spis.rs')
-rw-r--r--embassy-nrf/src/spis.rs516
1 files changed, 516 insertions, 0 deletions
diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs
new file mode 100644
index 000000000..32c0b6f93
--- /dev/null
+++ b/embassy-nrf/src/spis.rs
@@ -0,0 +1,516 @@
1#![macro_use]
2
3use core::future::poll_fn;
4use core::sync::atomic::{compiler_fence, Ordering};
5use core::task::Poll;
6
7use embassy_embedded_hal::SetConfig;
8use embassy_hal_common::{into_ref, PeripheralRef};
9pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
10
11use crate::chip::FORCE_COPY_BUFFER_SIZE;
12use crate::gpio::sealed::Pin as _;
13use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
14use crate::interrupt::{Interrupt, InterruptExt};
15use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut};
16use crate::{pac, Peripheral};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20#[non_exhaustive]
21pub enum Error {
22 TxBufferTooLong,
23 RxBufferTooLong,
24 /// EasyDMA can only read from data memory, read only buffers in flash will fail.
25 DMABufferNotInDataMemory,
26}
27
28/// Interface for the SPIS peripheral using EasyDMA to offload the transmission and reception workload.
29///
30/// For more details about EasyDMA, consult the module documentation.
31pub struct Spis<'d, T: Instance> {
32 _p: PeripheralRef<'d, T>,
33}
34
35#[non_exhaustive]
36pub struct Config {
37 pub mode: Mode,
38 pub orc: u8,
39 pub def: u8,
40 pub auto_acquire: bool,
41}
42
43impl Default for Config {
44 fn default() -> Self {
45 Self {
46 mode: MODE_0,
47 orc: 0x00,
48 def: 0x00,
49 auto_acquire: true,
50 }
51 }
52}
53
54impl<'d, T: Instance> Spis<'d, T> {
55 pub fn new(
56 spis: impl Peripheral<P = T> + 'd,
57 irq: impl Peripheral<P = T::Interrupt> + 'd,
58 cs: impl Peripheral<P = impl GpioPin> + 'd,
59 sck: impl Peripheral<P = impl GpioPin> + 'd,
60 miso: impl Peripheral<P = impl GpioPin> + 'd,
61 mosi: impl Peripheral<P = impl GpioPin> + 'd,
62 config: Config,
63 ) -> Self {
64 into_ref!(cs, sck, miso, mosi);
65 Self::new_inner(
66 spis,
67 irq,
68 cs.map_into(),
69 sck.map_into(),
70 Some(miso.map_into()),
71 Some(mosi.map_into()),
72 config,
73 )
74 }
75
76 pub fn new_txonly(
77 spis: impl Peripheral<P = T> + 'd,
78 irq: impl Peripheral<P = T::Interrupt> + 'd,
79 cs: impl Peripheral<P = impl GpioPin> + 'd,
80 sck: impl Peripheral<P = impl GpioPin> + 'd,
81 mosi: impl Peripheral<P = impl GpioPin> + 'd,
82 config: Config,
83 ) -> Self {
84 into_ref!(cs, sck, mosi);
85 Self::new_inner(spis, irq, cs.map_into(), sck.map_into(), None, Some(mosi.map_into()), config)
86 }
87
88 pub fn new_rxonly(
89 spis: impl Peripheral<P = T> + 'd,
90 irq: impl Peripheral<P = T::Interrupt> + 'd,
91 cs: impl Peripheral<P = impl GpioPin> + 'd,
92 sck: impl Peripheral<P = impl GpioPin> + 'd,
93 miso: impl Peripheral<P = impl GpioPin> + 'd,
94 config: Config,
95 ) -> Self {
96 into_ref!(cs, sck, miso);
97 Self::new_inner(spis, irq, cs.map_into(), sck.map_into(), Some(miso.map_into()), None, config)
98 }
99
100 fn new_inner(
101 spis: impl Peripheral<P = T> + 'd,
102 irq: impl Peripheral<P = T::Interrupt> + 'd,
103 cs: PeripheralRef<'d, AnyPin>,
104 sck: PeripheralRef<'d, AnyPin>,
105 miso: Option<PeripheralRef<'d, AnyPin>>,
106 mosi: Option<PeripheralRef<'d, AnyPin>>,
107 config: Config,
108 ) -> Self {
109 into_ref!(cs, spis, irq);
110
111 let r = T::regs();
112
113 // Configure pins
114 sck.conf().write(|w| w.input().connect().drive().h0h1());
115 cs.conf().write(|w| w.input().connect().drive().h0h1());
116 if let Some(mosi) = &mosi {
117 mosi.conf().write(|w| w.input().connect().drive().h0h1());
118 }
119 if let Some(miso) = &miso {
120 miso.conf().write(|w| w.dir().output().drive().h0h1());
121 }
122
123 match config.mode.polarity {
124 Polarity::IdleHigh => {
125 sck.set_high();
126 if let Some(mosi) = &mosi {
127 mosi.set_high();
128 }
129 }
130 Polarity::IdleLow => {
131 sck.set_low();
132 if let Some(mosi) = &mosi {
133 mosi.set_low();
134 }
135 }
136 }
137
138 if config.auto_acquire {
139 r.shorts.write(|w| w.end_acquire().bit(true));
140 }
141
142 // Select pins.
143 r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) });
144 r.psel.csn.write(|w| unsafe { w.bits(cs.psel_bits()) });
145 r.psel.mosi.write(|w| unsafe { w.bits(mosi.psel_bits()) });
146 r.psel.miso.write(|w| unsafe { w.bits(miso.psel_bits()) });
147
148 // Enable SPIS instance.
149 r.enable.write(|w| w.enable().enabled());
150
151 // Configure mode.
152 let mode = config.mode;
153 r.config.write(|w| {
154 match mode {
155 MODE_0 => {
156 w.order().msb_first();
157 w.cpol().active_high();
158 w.cpha().leading();
159 }
160 MODE_1 => {
161 w.order().msb_first();
162 w.cpol().active_high();
163 w.cpha().trailing();
164 }
165 MODE_2 => {
166 w.order().msb_first();
167 w.cpol().active_low();
168 w.cpha().leading();
169 }
170 MODE_3 => {
171 w.order().msb_first();
172 w.cpol().active_low();
173 w.cpha().trailing();
174 }
175 }
176
177 w
178 });
179
180 // Set over-read character
181 let orc = config.orc;
182 r.orc.write(|w| unsafe { w.orc().bits(orc) });
183
184 // Set default character
185 let def = config.def;
186 r.def.write(|w| unsafe { w.def().bits(def) });
187
188 // Disable all events interrupts
189 r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
190
191 irq.set_handler(Self::on_interrupt);
192 irq.unpend();
193 irq.enable();
194
195 Self { _p: spis }
196 }
197
198 fn on_interrupt(_: *mut ()) {
199 let r = T::regs();
200 let s = T::state();
201
202 if r.events_end.read().bits() != 0 {
203 s.end_waker.wake();
204 r.intenclr.write(|w| w.end().clear());
205 }
206
207 if r.events_acquired.read().bits() != 0 {
208 s.acquire_waker.wake();
209 r.intenclr.write(|w| w.acquired().clear());
210 }
211 }
212
213 fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
214 slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?;
215 // NOTE: RAM slice check for rx is not necessary, as a mutable
216 // slice can only be built from data located in RAM.
217
218 compiler_fence(Ordering::SeqCst);
219
220 let r = T::regs();
221
222 // Set up the DMA write.
223 let (ptr, len) = slice_ptr_parts(tx);
224 r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
225 r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
226
227 // Set up the DMA read.
228 let (ptr, len) = slice_ptr_parts_mut(rx);
229 r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
230 r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
231
232 // Reset and enable the end event
233 r.events_end.reset();
234 r.intenset.write(|w| w.end().set());
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 self.prepare(rx, tx)?;
244 let r = T::regs();
245
246 // Wait for 'end' event.
247 while r.events_end.read().bits() == 0 {}
248
249 let n_rx = r.rxd.amount.read().bits() as usize;
250 let n_tx = r.txd.amount.read().bits() as usize;
251
252 compiler_fence(Ordering::SeqCst);
253
254 Ok((n_rx, n_tx))
255 }
256
257 fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> {
258 match self.blocking_inner_from_ram(rx, tx) {
259 Ok(n) => Ok(n),
260 Err(Error::DMABufferNotInDataMemory) => {
261 trace!("Copying SPIS tx buffer into RAM for DMA");
262 let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
263 tx_ram_buf.copy_from_slice(tx);
264 self.blocking_inner_from_ram(rx, tx_ram_buf)
265 }
266 Err(error) => Err(error),
267 }
268 }
269
270 async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> {
271 let r = T::regs();
272 let s = T::state();
273
274 if r.semstat.read().bits() != 1 {
275 // Reset and enable the acquire event
276 r.events_acquired.reset();
277 r.intenset.write(|w| w.acquired().set());
278
279 // Requests acquiring the SPIS semaphore
280 r.tasks_acquire.write(|w| unsafe { w.bits(1) });
281
282 // Wait for 'acquire' event.
283 poll_fn(|cx| {
284 s.acquire_waker.register(cx.waker());
285 if r.events_acquired.read().bits() != 0 {
286 return Poll::Ready(());
287 }
288 Poll::Pending
289 })
290 .await;
291 }
292
293 self.prepare(rx, tx)?;
294
295 // Wait for 'end' event.
296 poll_fn(|cx| {
297 s.end_waker.register(cx.waker());
298 if r.events_end.read().bits() != 0 {
299 return Poll::Ready(());
300 }
301
302 Poll::Pending
303 })
304 .await;
305
306 let n_rx = r.rxd.amount.read().bits() as usize;
307 let n_tx = r.txd.amount.read().bits() as usize;
308
309 compiler_fence(Ordering::SeqCst);
310
311 Ok((n_rx, n_tx))
312 }
313
314 async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> {
315 match self.async_inner_from_ram(rx, tx).await {
316 Ok(n) => Ok(n),
317 Err(Error::DMABufferNotInDataMemory) => {
318 trace!("Copying SPIS tx buffer into RAM for DMA");
319 let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
320 tx_ram_buf.copy_from_slice(tx);
321 self.async_inner_from_ram(rx, tx_ram_buf).await
322 }
323 Err(error) => Err(error),
324 }
325 }
326
327 /// Reads data from the SPI bus without sending anything. Blocks until the buffer has been filled.
328 /// Returns number of bytes read
329 pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<usize, Error> {
330 self.blocking_inner(data, &[]).map(|n| n.0)
331 }
332
333 /// Simultaneously sends and receives data. Blocks until the transmission is completed.
334 /// If necessary, the write buffer will be copied into RAM (see struct description for detail).
335 /// Returns number of bytes transferred `(n_rx, n_tx)`
336 pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
337 self.blocking_inner(read, write)
338 }
339
340 /// Same as [`blocking_transfer`](Spis::blocking_transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
341 /// Returns number of bytes transferred `(n_rx, n_tx)`
342 pub fn blocking_transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
343 self.blocking_inner(read, write)
344 }
345
346 /// Simultaneously sends and receives data.
347 /// Places the received data into the same buffer and blocks until the transmission is completed.
348 /// Returns number of bytes transferred
349 pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<usize, Error> {
350 self.blocking_inner_from_ram(data, data).map(|n| n.0)
351 }
352
353 /// Sends data, discarding any received data. Blocks until the transmission is completed.
354 /// If necessary, the write buffer will be copied into RAM (see struct description for detail).
355 /// Returns number of bytes written
356 pub fn blocking_write(&mut self, data: &[u8]) -> Result<usize, Error> {
357 self.blocking_inner(&mut [], data).map(|n| n.1)
358 }
359
360 /// Same as [`blocking_write`](Spis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
361 /// Returns number of bytes written
362 pub fn blocking_write_from_ram(&mut self, data: &[u8]) -> Result<usize, Error> {
363 self.blocking_inner(&mut [], data).map(|n| n.1)
364 }
365
366 /// Reads data from the SPI bus without sending anything.
367 /// Returns number of bytes read
368 pub async fn read(&mut self, data: &mut [u8]) -> Result<usize, Error> {
369 self.async_inner(data, &[]).await.map(|n| n.0)
370 }
371
372 /// Simultaneously sends and receives data.
373 /// If necessary, the write buffer will be copied into RAM (see struct description for detail).
374 /// Returns number of bytes transferred `(n_rx, n_tx)`
375 pub async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
376 self.async_inner(read, write).await
377 }
378
379 /// Same as [`transfer`](Spis::transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
380 /// Returns number of bytes transferred `(n_rx, n_tx)`
381 pub async fn transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
382 self.async_inner_from_ram(read, write).await
383 }
384
385 /// Simultaneously sends and receives data. Places the received data into the same buffer.
386 /// Returns number of bytes transferred
387 pub async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<usize, Error> {
388 self.async_inner_from_ram(data, data).await.map(|n| n.0)
389 }
390
391 /// Sends data, discarding any received data.
392 /// If necessary, the write buffer will be copied into RAM (see struct description for detail).
393 /// Returns number of bytes written
394 pub async fn write(&mut self, data: &[u8]) -> Result<usize, Error> {
395 self.async_inner(&mut [], data).await.map(|n| n.1)
396 }
397
398 /// Same as [`write`](Spis::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
399 /// Returns number of bytes written
400 pub async fn write_from_ram(&mut self, data: &[u8]) -> Result<usize, Error> {
401 self.async_inner_from_ram(&mut [], data).await.map(|n| n.1)
402 }
403}
404
405impl<'d, T: Instance> Drop for Spis<'d, T> {
406 fn drop(&mut self) {
407 trace!("spis drop");
408
409 // Disable
410 let r = T::regs();
411 r.enable.write(|w| w.enable().disabled());
412
413 gpio::deconfigure_pin(r.psel.sck.read().bits());
414 gpio::deconfigure_pin(r.psel.csn.read().bits());
415 gpio::deconfigure_pin(r.psel.miso.read().bits());
416 gpio::deconfigure_pin(r.psel.mosi.read().bits());
417
418 trace!("spis drop: done");
419 }
420}
421
422pub(crate) mod sealed {
423 use embassy_sync::waitqueue::AtomicWaker;
424
425 use super::*;
426
427 pub struct State {
428 pub end_waker: AtomicWaker,
429 pub acquire_waker: AtomicWaker,
430 }
431
432 impl State {
433 pub const fn new() -> Self {
434 Self {
435 end_waker: AtomicWaker::new(),
436 acquire_waker: AtomicWaker::new(),
437 }
438 }
439 }
440
441 pub trait Instance {
442 fn regs() -> &'static pac::spis0::RegisterBlock;
443 fn state() -> &'static State;
444 }
445}
446
447pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
448 type Interrupt: Interrupt;
449}
450
451macro_rules! impl_spis {
452 ($type:ident, $pac_type:ident, $irq:ident) => {
453 impl crate::spis::sealed::Instance for peripherals::$type {
454 fn regs() -> &'static pac::spis0::RegisterBlock {
455 unsafe { &*pac::$pac_type::ptr() }
456 }
457 fn state() -> &'static crate::spis::sealed::State {
458 static STATE: crate::spis::sealed::State = crate::spis::sealed::State::new();
459 &STATE
460 }
461 }
462 impl crate::spis::Instance for peripherals::$type {
463 type Interrupt = crate::interrupt::$irq;
464 }
465 };
466}
467
468// ====================
469
470impl<'d, T: Instance> SetConfig for Spis<'d, T> {
471 type Config = Config;
472 fn set_config(&mut self, config: &Self::Config) {
473 let r = T::regs();
474 // Configure mode.
475 let mode = config.mode;
476 r.config.write(|w| {
477 match mode {
478 MODE_0 => {
479 w.order().msb_first();
480 w.cpol().active_high();
481 w.cpha().leading();
482 }
483 MODE_1 => {
484 w.order().msb_first();
485 w.cpol().active_high();
486 w.cpha().trailing();
487 }
488 MODE_2 => {
489 w.order().msb_first();
490 w.cpol().active_low();
491 w.cpha().leading();
492 }
493 MODE_3 => {
494 w.order().msb_first();
495 w.cpol().active_low();
496 w.cpha().trailing();
497 }
498 }
499
500 w
501 });
502
503 // Set over-read character
504 let orc = config.orc;
505 r.orc.write(|w| unsafe { w.orc().bits(orc) });
506
507 // Set default character
508 let def = config.def;
509 r.def.write(|w| unsafe { w.def().bits(def) });
510
511 // Set auto acquire
512 let auto_acquire = config.auto_acquire;
513 r.shorts.write(|w| w.end_acquire().bit(auto_acquire));
514
515 }
516}