aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-nrf-examples/src/bin/spim.rs116
-rw-r--r--embassy-nrf/src/lib.rs40
-rw-r--r--embassy-nrf/src/spim.rs272
3 files changed, 428 insertions, 0 deletions
diff --git a/embassy-nrf-examples/src/bin/spim.rs b/embassy-nrf-examples/src/bin/spim.rs
new file mode 100644
index 000000000..0a284dc89
--- /dev/null
+++ b/embassy-nrf-examples/src/bin/spim.rs
@@ -0,0 +1,116 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5#[path = "../example_common.rs"]
6mod example_common;
7use example_common::*;
8
9use cortex_m_rt::entry;
10use defmt::panic;
11use embassy::executor::{task, Executor};
12use embassy::util::Forever;
13use embedded_hal::digital::v2::*;
14use futures::pin_mut;
15use nrf52840_hal::clocks;
16use nrf52840_hal::gpio;
17
18use embassy_nrf::{interrupt, pac, rtc, spim};
19
20#[task]
21async fn run() {
22 info!("running!");
23
24 let p = unsafe { embassy_nrf::pac::Peripherals::steal() };
25 let p0 = gpio::p0::Parts::new(p.P0);
26
27 let pins = spim::Pins {
28 sck: p0.p0_29.into_push_pull_output(gpio::Level::Low).degrade(),
29 miso: Some(p0.p0_28.into_floating_input().degrade()),
30 mosi: Some(p0.p0_30.into_push_pull_output(gpio::Level::Low).degrade()),
31 };
32 let config = spim::Config {
33 pins,
34 frequency: spim::Frequency::M16,
35 mode: spim::MODE_0,
36 orc: 0x00,
37 };
38
39 let mut ncs = p0.p0_31.into_push_pull_output(gpio::Level::High);
40 let spim = spim::Spim::new(p.SPIM3, interrupt::take!(SPIM3), config);
41 pin_mut!(spim);
42
43 // Example on how to talk to an ENC28J60 chip
44
45 // softreset
46 cortex_m::asm::delay(10);
47 ncs.set_low().unwrap();
48 cortex_m::asm::delay(5);
49 let tx = [0xFF];
50 unwrap!(spim.as_mut().send_receive(&tx, &mut []).await);
51 cortex_m::asm::delay(10);
52 ncs.set_high().unwrap();
53
54 cortex_m::asm::delay(100000);
55
56 let mut rx = [0; 2];
57
58 // read ESTAT
59 cortex_m::asm::delay(5000);
60 ncs.set_low().unwrap();
61 cortex_m::asm::delay(5000);
62 let tx = [0b000_11101, 0];
63 unwrap!(spim.as_mut().send_receive(&tx, &mut rx).await);
64 cortex_m::asm::delay(5000);
65 ncs.set_high().unwrap();
66 info!("estat: {=[?]}", rx);
67
68 // Switch to bank 3
69 cortex_m::asm::delay(10);
70 ncs.set_low().unwrap();
71 cortex_m::asm::delay(5);
72 let tx = [0b100_11111, 0b11];
73 unwrap!(spim.as_mut().send_receive(&tx, &mut rx).await);
74 cortex_m::asm::delay(10);
75 ncs.set_high().unwrap();
76
77 // read EREVID
78 cortex_m::asm::delay(10);
79 ncs.set_low().unwrap();
80 cortex_m::asm::delay(5);
81 let tx = [0b000_10010, 0];
82 unwrap!(spim.as_mut().send_receive(&tx, &mut rx).await);
83 cortex_m::asm::delay(10);
84 ncs.set_high().unwrap();
85
86 info!("erevid: {=[?]}", rx);
87}
88
89static RTC: Forever<rtc::RTC<pac::RTC1>> = Forever::new();
90static ALARM: Forever<rtc::Alarm<pac::RTC1>> = Forever::new();
91static EXECUTOR: Forever<Executor> = Forever::new();
92
93#[entry]
94fn main() -> ! {
95 info!("Hello World!");
96
97 let p = unwrap!(embassy_nrf::pac::Peripherals::take());
98
99 clocks::Clocks::new(p.CLOCK)
100 .enable_ext_hfosc()
101 .set_lfclk_src_external(clocks::LfOscConfiguration::NoExternalNoBypass)
102 .start_lfclk();
103
104 let rtc = RTC.put(rtc::RTC::new(p.RTC1, interrupt::take!(RTC1)));
105 rtc.start();
106
107 unsafe { embassy::time::set_clock(rtc) };
108
109 let alarm = ALARM.put(rtc.alarm0());
110 let executor = EXECUTOR.put(Executor::new());
111 executor.set_alarm(alarm);
112
113 executor.run(|spawner| {
114 unwrap!(spawner.spawn(run()));
115 });
116}
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index 59bb0e0cf..3de6299e9 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -49,6 +49,45 @@ pub use nrf52833_hal as hal;
49#[cfg(feature = "52840")] 49#[cfg(feature = "52840")]
50pub use nrf52840_hal as hal; 50pub use nrf52840_hal as hal;
51 51
52/// Length of Nordic EasyDMA differs for MCUs
53#[cfg(any(
54 feature = "52810",
55 feature = "52811",
56 feature = "52832",
57 feature = "51"
58))]
59pub mod target_constants {
60 // NRF52832 8 bits1..0xFF
61 pub const EASY_DMA_SIZE: usize = 255;
62 // Easy DMA can only read from data ram
63 pub const SRAM_LOWER: usize = 0x2000_0000;
64 pub const SRAM_UPPER: usize = 0x3000_0000;
65}
66#[cfg(any(feature = "52840", feature = "52833", feature = "9160"))]
67pub mod target_constants {
68 // NRF52840 and NRF9160 16 bits 1..0xFFFF
69 pub const EASY_DMA_SIZE: usize = 65535;
70 // Limits for Easy DMA - it can only read from data ram
71 pub const SRAM_LOWER: usize = 0x2000_0000;
72 pub const SRAM_UPPER: usize = 0x3000_0000;
73}
74
75/// Does this slice reside entirely within RAM?
76pub(crate) fn slice_in_ram(slice: &[u8]) -> bool {
77 let ptr = slice.as_ptr() as usize;
78 ptr >= target_constants::SRAM_LOWER && (ptr + slice.len()) < target_constants::SRAM_UPPER
79}
80
81/// Return an error if slice is not in RAM.
82#[cfg(not(feature = "51"))]
83pub(crate) fn slice_in_ram_or<T>(slice: &[u8], err: T) -> Result<(), T> {
84 if slice.len() == 0 || slice_in_ram(slice) {
85 Ok(())
86 } else {
87 Err(err)
88 }
89}
90
52// This mod MUST go first, so that the others see its macros. 91// This mod MUST go first, so that the others see its macros.
53pub(crate) mod fmt; 92pub(crate) mod fmt;
54pub(crate) mod util; 93pub(crate) mod util;
@@ -59,4 +98,5 @@ pub mod interrupt;
59#[cfg(feature = "52840")] 98#[cfg(feature = "52840")]
60pub mod qspi; 99pub mod qspi;
61pub mod rtc; 100pub mod rtc;
101pub mod spim;
62pub mod uarte; 102pub mod uarte;
diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs
new file mode 100644
index 000000000..3c0f6e047
--- /dev/null
+++ b/embassy-nrf/src/spim.rs
@@ -0,0 +1,272 @@
1use core::future::Future;
2use core::pin::Pin;
3use core::sync::atomic::{compiler_fence, Ordering};
4use core::task::Poll;
5use embassy::util::WakerRegistration;
6use futures::future::poll_fn;
7
8use crate::hal::gpio::Port as GpioPort;
9use crate::interrupt::{self, Interrupt};
10use crate::util::peripheral::{PeripheralMutex, PeripheralState};
11use crate::{pac, slice_in_ram_or};
12
13pub use crate::hal::spim::{
14 Frequency, Mode, Phase, Pins, Polarity, MODE_0, MODE_1, MODE_2, MODE_3,
15};
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
27struct State<T: Instance> {
28 spim: T,
29 waker: WakerRegistration,
30}
31
32pub struct Spim<T: Instance> {
33 inner: PeripheralMutex<State<T>>,
34}
35
36#[cfg(any(feature = "52833", feature = "52840"))]
37fn port_bit(port: GpioPort) -> bool {
38 match port {
39 GpioPort::Port0 => false,
40 GpioPort::Port1 => true,
41 }
42}
43
44pub struct Config {
45 pub pins: Pins,
46 pub frequency: Frequency,
47 pub mode: Mode,
48 pub orc: u8,
49}
50
51impl<T: Instance> Spim<T> {
52 pub fn new(mut spim: T, irq: T::Interrupt, config: Config) -> Self {
53 let r = spim.regs();
54
55 // Select pins.
56 r.psel.sck.write(|w| {
57 let w = unsafe { w.pin().bits(config.pins.sck.pin()) };
58 #[cfg(any(feature = "52833", feature = "52840"))]
59 let w = w.port().bit(port_bit(config.pins.sck.port()));
60 w.connect().connected()
61 });
62
63 match config.pins.mosi {
64 Some(mosi) => r.psel.mosi.write(|w| {
65 let w = unsafe { w.pin().bits(mosi.pin()) };
66 #[cfg(any(feature = "52833", feature = "52840"))]
67 let w = w.port().bit(port_bit(mosi.port()));
68 w.connect().connected()
69 }),
70 None => r.psel.mosi.write(|w| w.connect().disconnected()),
71 }
72 match config.pins.miso {
73 Some(miso) => r.psel.miso.write(|w| {
74 let w = unsafe { w.pin().bits(miso.pin()) };
75 #[cfg(any(feature = "52833", feature = "52840"))]
76 let w = w.port().bit(port_bit(miso.port()));
77 w.connect().connected()
78 }),
79 None => r.psel.miso.write(|w| w.connect().disconnected()),
80 }
81
82 // Enable SPIM instance.
83 r.enable.write(|w| w.enable().enabled());
84
85 // Configure mode.
86 let mode = config.mode;
87 r.config.write(|w| {
88 // Can't match on `mode` due to embedded-hal, see https://github.com/rust-embedded/embedded-hal/pull/126
89 if mode == MODE_0 {
90 w.order().msb_first();
91 w.cpol().active_high();
92 w.cpha().leading();
93 } else if mode == MODE_1 {
94 w.order().msb_first();
95 w.cpol().active_high();
96 w.cpha().trailing();
97 } else if mode == MODE_2 {
98 w.order().msb_first();
99 w.cpol().active_low();
100 w.cpha().leading();
101 } else {
102 w.order().msb_first();
103 w.cpol().active_low();
104 w.cpha().trailing();
105 }
106 w
107 });
108
109 // Configure frequency.
110 let frequency = config.frequency;
111 r.frequency.write(|w| w.frequency().variant(frequency));
112
113 // Set over-read character
114 let orc = config.orc;
115 r.orc.write(|w|
116 // The ORC field is 8 bits long, so any u8 is a valid value to write.
117 unsafe { w.orc().bits(orc) });
118
119 // Disable all events interrupts
120 r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
121
122 Self {
123 inner: PeripheralMutex::new(
124 State {
125 spim,
126 waker: WakerRegistration::new(),
127 },
128 irq,
129 ),
130 }
131 }
132
133 fn inner(self: Pin<&mut Self>) -> Pin<&mut PeripheralMutex<State<T>>> {
134 unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().inner) }
135 }
136
137 pub fn free(self: Pin<&mut Self>) -> (T, T::Interrupt) {
138 let (state, irq) = self.inner().free();
139 (state.spim, irq)
140 }
141
142 pub fn send_receive<'a>(
143 mut self: Pin<&'a mut Self>,
144 tx: &'a [u8],
145 rx: &'a mut [u8],
146 ) -> impl Future<Output = Result<(), Error>> + 'a {
147 async move {
148 slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?;
149 slice_in_ram_or(rx, Error::DMABufferNotInDataMemory)?;
150
151 self.as_mut().inner().with(|s, _irq| {
152 // Conservative compiler fence to prevent optimizations that do not
153 // take in to account actions by DMA. The fence has been placed here,
154 // before any DMA action has started.
155 compiler_fence(Ordering::SeqCst);
156
157 let r = s.spim.regs();
158
159 // Set up the DMA write.
160 r.txd
161 .ptr
162 .write(|w| unsafe { w.ptr().bits(tx.as_ptr() as u32) });
163 r.txd
164 .maxcnt
165 .write(|w| unsafe { w.maxcnt().bits(tx.len() as _) });
166
167 // Set up the DMA read.
168 r.rxd
169 .ptr
170 .write(|w| unsafe { w.ptr().bits(rx.as_mut_ptr() as u32) });
171 r.rxd
172 .maxcnt
173 .write(|w| unsafe { w.maxcnt().bits(rx.len() as _) });
174
175 // Reset and enable the event
176 r.events_end.reset();
177 r.intenset.write(|w| w.end().set());
178
179 // Start SPI transaction.
180 r.tasks_start.write(|w| unsafe { w.bits(1) });
181
182 // Conservative compiler fence to prevent optimizations that do not
183 // take in to account actions by DMA. The fence has been placed here,
184 // after all possible DMA actions have completed.
185 compiler_fence(Ordering::SeqCst);
186 });
187
188 // Wait for 'end' event.
189 poll_fn(|cx| {
190 self.as_mut().inner().with(|s, _irq| {
191 let r = s.spim.regs();
192 if r.events_end.read().bits() != 0 {
193 return Poll::Ready(());
194 }
195 s.waker.register(cx.waker());
196 Poll::Pending
197 })
198 })
199 .await;
200
201 Ok(())
202 }
203 }
204}
205
206impl<U: Instance> PeripheralState for State<U> {
207 type Interrupt = U::Interrupt;
208 fn on_interrupt(&mut self) {
209 if self.spim.regs().events_end.read().bits() != 0 {
210 self.spim.regs().intenclr.write(|w| w.end().clear());
211 self.waker.wake()
212 }
213 }
214}
215
216mod sealed {
217 pub trait Instance {}
218}
219
220pub trait Instance: sealed::Instance {
221 type Interrupt: Interrupt;
222 fn regs(&mut self) -> &pac::spim0::RegisterBlock;
223}
224
225impl sealed::Instance for pac::SPIM0 {}
226impl Instance for pac::SPIM0 {
227 #[cfg(feature = "52810")]
228 type Interrupt = interrupt::SPIM0_SPIS0_SPI0;
229 #[cfg(not(feature = "52810"))]
230 type Interrupt = interrupt::SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0;
231 fn regs(&mut self) -> &pac::spim0::RegisterBlock {
232 self
233 }
234}
235
236#[cfg(any(feature = "52832", feature = "52833", feature = "52840"))]
237impl sealed::Instance for pac::SPIM1 {}
238#[cfg(any(feature = "52832", feature = "52833", feature = "52840"))]
239impl Instance for pac::SPIM1 {
240 type Interrupt = interrupt::SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1;
241 fn regs(&mut self) -> &pac::spim0::RegisterBlock {
242 self
243 }
244}
245
246#[cfg(any(feature = "52832", feature = "52833", feature = "52840"))]
247impl sealed::Instance for pac::SPIM2 {}
248#[cfg(any(feature = "52832", feature = "52833", feature = "52840"))]
249impl Instance for pac::SPIM2 {
250 type Interrupt = interrupt::SPIM2_SPIS2_SPI2;
251 fn regs(&mut self) -> &pac::spim0::RegisterBlock {
252 self
253 }
254}
255
256#[cfg(any(feature = "52833", feature = "52840"))]
257impl sealed::Instance for pac::SPIM3 {}
258#[cfg(any(feature = "52833", feature = "52840"))]
259impl Instance for pac::SPIM3 {
260 type Interrupt = interrupt::SPIM3;
261 fn regs(&mut self) -> &pac::spim0::RegisterBlock {
262 self
263 }
264}
265
266impl<T: sealed::Instance> sealed::Instance for &mut T {}
267impl<T: Instance> Instance for &mut T {
268 type Interrupt = T::Interrupt;
269 fn regs(&mut self) -> &pac::spim0::RegisterBlock {
270 T::regs(*self)
271 }
272}