commit
						282f00e705
					
				
							
								
								
									
										116
									
								
								embassy-nrf-examples/src/bin/spim.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								embassy-nrf-examples/src/bin/spim.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,116 @@ | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![feature(type_alias_impl_trait)] | ||||
| 
 | ||||
| #[path = "../example_common.rs"] | ||||
| mod example_common; | ||||
| use example_common::*; | ||||
| 
 | ||||
| use cortex_m_rt::entry; | ||||
| use defmt::panic; | ||||
| use embassy::executor::{task, Executor}; | ||||
| use embassy::util::Forever; | ||||
| use embedded_hal::digital::v2::*; | ||||
| use futures::pin_mut; | ||||
| use nrf52840_hal::clocks; | ||||
| use nrf52840_hal::gpio; | ||||
| 
 | ||||
| use embassy_nrf::{interrupt, pac, rtc, spim}; | ||||
| 
 | ||||
| #[task] | ||||
| async fn run() { | ||||
|     info!("running!"); | ||||
| 
 | ||||
|     let p = unsafe { embassy_nrf::pac::Peripherals::steal() }; | ||||
|     let p0 = gpio::p0::Parts::new(p.P0); | ||||
| 
 | ||||
|     let pins = spim::Pins { | ||||
|         sck: p0.p0_29.into_push_pull_output(gpio::Level::Low).degrade(), | ||||
|         miso: Some(p0.p0_28.into_floating_input().degrade()), | ||||
|         mosi: Some(p0.p0_30.into_push_pull_output(gpio::Level::Low).degrade()), | ||||
|     }; | ||||
|     let config = spim::Config { | ||||
|         pins, | ||||
|         frequency: spim::Frequency::M16, | ||||
|         mode: spim::MODE_0, | ||||
|         orc: 0x00, | ||||
|     }; | ||||
| 
 | ||||
|     let mut ncs = p0.p0_31.into_push_pull_output(gpio::Level::High); | ||||
|     let spim = spim::Spim::new(p.SPIM3, interrupt::take!(SPIM3), config); | ||||
|     pin_mut!(spim); | ||||
| 
 | ||||
|     // Example on how to talk to an ENC28J60 chip
 | ||||
| 
 | ||||
|     // softreset
 | ||||
|     cortex_m::asm::delay(10); | ||||
|     ncs.set_low().unwrap(); | ||||
|     cortex_m::asm::delay(5); | ||||
|     let tx = [0xFF]; | ||||
|     unwrap!(spim.as_mut().send_receive(&tx, &mut []).await); | ||||
|     cortex_m::asm::delay(10); | ||||
|     ncs.set_high().unwrap(); | ||||
| 
 | ||||
|     cortex_m::asm::delay(100000); | ||||
| 
 | ||||
|     let mut rx = [0; 2]; | ||||
| 
 | ||||
|     // read ESTAT
 | ||||
|     cortex_m::asm::delay(5000); | ||||
|     ncs.set_low().unwrap(); | ||||
|     cortex_m::asm::delay(5000); | ||||
|     let tx = [0b000_11101, 0]; | ||||
|     unwrap!(spim.as_mut().send_receive(&tx, &mut rx).await); | ||||
|     cortex_m::asm::delay(5000); | ||||
|     ncs.set_high().unwrap(); | ||||
|     info!("estat: {=[?]}", rx); | ||||
| 
 | ||||
|     // Switch to bank 3
 | ||||
|     cortex_m::asm::delay(10); | ||||
|     ncs.set_low().unwrap(); | ||||
|     cortex_m::asm::delay(5); | ||||
|     let tx = [0b100_11111, 0b11]; | ||||
|     unwrap!(spim.as_mut().send_receive(&tx, &mut rx).await); | ||||
|     cortex_m::asm::delay(10); | ||||
|     ncs.set_high().unwrap(); | ||||
| 
 | ||||
|     // read EREVID
 | ||||
|     cortex_m::asm::delay(10); | ||||
|     ncs.set_low().unwrap(); | ||||
|     cortex_m::asm::delay(5); | ||||
|     let tx = [0b000_10010, 0]; | ||||
|     unwrap!(spim.as_mut().send_receive(&tx, &mut rx).await); | ||||
|     cortex_m::asm::delay(10); | ||||
|     ncs.set_high().unwrap(); | ||||
| 
 | ||||
|     info!("erevid: {=[?]}", rx); | ||||
| } | ||||
| 
 | ||||
| static RTC: Forever<rtc::RTC<pac::RTC1>> = Forever::new(); | ||||
| static ALARM: Forever<rtc::Alarm<pac::RTC1>> = Forever::new(); | ||||
| static EXECUTOR: Forever<Executor> = Forever::new(); | ||||
| 
 | ||||
| #[entry] | ||||
| fn main() -> ! { | ||||
|     info!("Hello World!"); | ||||
| 
 | ||||
|     let p = unwrap!(embassy_nrf::pac::Peripherals::take()); | ||||
| 
 | ||||
|     clocks::Clocks::new(p.CLOCK) | ||||
|         .enable_ext_hfosc() | ||||
|         .set_lfclk_src_external(clocks::LfOscConfiguration::NoExternalNoBypass) | ||||
|         .start_lfclk(); | ||||
| 
 | ||||
|     let rtc = RTC.put(rtc::RTC::new(p.RTC1, interrupt::take!(RTC1))); | ||||
|     rtc.start(); | ||||
| 
 | ||||
|     unsafe { embassy::time::set_clock(rtc) }; | ||||
| 
 | ||||
|     let alarm = ALARM.put(rtc.alarm0()); | ||||
|     let executor = EXECUTOR.put(Executor::new()); | ||||
|     executor.set_alarm(alarm); | ||||
| 
 | ||||
|     executor.run(|spawner| { | ||||
|         unwrap!(spawner.spawn(run())); | ||||
|     }); | ||||
| } | ||||
| @ -49,6 +49,45 @@ pub use nrf52833_hal as hal; | ||||
| #[cfg(feature = "52840")] | ||||
| pub use nrf52840_hal as hal; | ||||
| 
 | ||||
| /// Length of Nordic EasyDMA differs for MCUs
 | ||||
| #[cfg(any(
 | ||||
|     feature = "52810", | ||||
|     feature = "52811", | ||||
|     feature = "52832", | ||||
|     feature = "51" | ||||
| ))] | ||||
| pub mod target_constants { | ||||
|     // NRF52832 8 bits1..0xFF
 | ||||
|     pub const EASY_DMA_SIZE: usize = 255; | ||||
|     // Easy DMA can only read from data ram
 | ||||
|     pub const SRAM_LOWER: usize = 0x2000_0000; | ||||
|     pub const SRAM_UPPER: usize = 0x3000_0000; | ||||
| } | ||||
| #[cfg(any(feature = "52840", feature = "52833", feature = "9160"))] | ||||
| pub mod target_constants { | ||||
|     // NRF52840 and NRF9160 16 bits 1..0xFFFF
 | ||||
|     pub const EASY_DMA_SIZE: usize = 65535; | ||||
|     // Limits for Easy DMA - it can only read from data ram
 | ||||
|     pub const SRAM_LOWER: usize = 0x2000_0000; | ||||
|     pub const SRAM_UPPER: usize = 0x3000_0000; | ||||
| } | ||||
| 
 | ||||
| /// Does this slice reside entirely within RAM?
 | ||||
| pub(crate) fn slice_in_ram(slice: &[u8]) -> bool { | ||||
|     let ptr = slice.as_ptr() as usize; | ||||
|     ptr >= target_constants::SRAM_LOWER && (ptr + slice.len()) < target_constants::SRAM_UPPER | ||||
| } | ||||
| 
 | ||||
| /// Return an error if slice is not in RAM.
 | ||||
| #[cfg(not(feature = "51"))] | ||||
| pub(crate) fn slice_in_ram_or<T>(slice: &[u8], err: T) -> Result<(), T> { | ||||
|     if slice.len() == 0 || slice_in_ram(slice) { | ||||
|         Ok(()) | ||||
|     } else { | ||||
|         Err(err) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // This mod MUST go first, so that the others see its macros.
 | ||||
| pub(crate) mod fmt; | ||||
| pub(crate) mod util; | ||||
| @ -59,4 +98,5 @@ pub mod interrupt; | ||||
| #[cfg(feature = "52840")] | ||||
| pub mod qspi; | ||||
| pub mod rtc; | ||||
| pub mod spim; | ||||
| pub mod uarte; | ||||
|  | ||||
							
								
								
									
										272
									
								
								embassy-nrf/src/spim.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								embassy-nrf/src/spim.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,272 @@ | ||||
| use core::future::Future; | ||||
| use core::pin::Pin; | ||||
| use core::sync::atomic::{compiler_fence, Ordering}; | ||||
| use core::task::Poll; | ||||
| use embassy::util::WakerRegistration; | ||||
| use futures::future::poll_fn; | ||||
| 
 | ||||
| use crate::hal::gpio::Port as GpioPort; | ||||
| use crate::interrupt::{self, Interrupt}; | ||||
| use crate::util::peripheral::{PeripheralMutex, PeripheralState}; | ||||
| use crate::{pac, slice_in_ram_or}; | ||||
| 
 | ||||
| pub use crate::hal::spim::{ | ||||
|     Frequency, Mode, Phase, Pins, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, | ||||
| }; | ||||
| 
 | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| #[non_exhaustive] | ||||
| pub enum Error { | ||||
|     TxBufferTooLong, | ||||
|     RxBufferTooLong, | ||||
|     /// EasyDMA can only read from data memory, read only buffers in flash will fail.
 | ||||
|     DMABufferNotInDataMemory, | ||||
| } | ||||
| 
 | ||||
| struct State<T: Instance> { | ||||
|     spim: T, | ||||
|     waker: WakerRegistration, | ||||
| } | ||||
| 
 | ||||
| pub struct Spim<T: Instance> { | ||||
|     inner: PeripheralMutex<State<T>>, | ||||
| } | ||||
| 
 | ||||
| #[cfg(any(feature = "52833", feature = "52840"))] | ||||
| fn port_bit(port: GpioPort) -> bool { | ||||
|     match port { | ||||
|         GpioPort::Port0 => false, | ||||
|         GpioPort::Port1 => true, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct Config { | ||||
|     pub pins: Pins, | ||||
|     pub frequency: Frequency, | ||||
|     pub mode: Mode, | ||||
|     pub orc: u8, | ||||
| } | ||||
| 
 | ||||
| impl<T: Instance> Spim<T> { | ||||
|     pub fn new(mut spim: T, irq: T::Interrupt, config: Config) -> Self { | ||||
|         let r = spim.regs(); | ||||
| 
 | ||||
|         // Select pins.
 | ||||
|         r.psel.sck.write(|w| { | ||||
|             let w = unsafe { w.pin().bits(config.pins.sck.pin()) }; | ||||
|             #[cfg(any(feature = "52833", feature = "52840"))] | ||||
|             let w = w.port().bit(port_bit(config.pins.sck.port())); | ||||
|             w.connect().connected() | ||||
|         }); | ||||
| 
 | ||||
|         match config.pins.mosi { | ||||
|             Some(mosi) => r.psel.mosi.write(|w| { | ||||
|                 let w = unsafe { w.pin().bits(mosi.pin()) }; | ||||
|                 #[cfg(any(feature = "52833", feature = "52840"))] | ||||
|                 let w = w.port().bit(port_bit(mosi.port())); | ||||
|                 w.connect().connected() | ||||
|             }), | ||||
|             None => r.psel.mosi.write(|w| w.connect().disconnected()), | ||||
|         } | ||||
|         match config.pins.miso { | ||||
|             Some(miso) => r.psel.miso.write(|w| { | ||||
|                 let w = unsafe { w.pin().bits(miso.pin()) }; | ||||
|                 #[cfg(any(feature = "52833", feature = "52840"))] | ||||
|                 let w = w.port().bit(port_bit(miso.port())); | ||||
|                 w.connect().connected() | ||||
|             }), | ||||
|             None => r.psel.miso.write(|w| w.connect().disconnected()), | ||||
|         } | ||||
| 
 | ||||
|         // Enable SPIM instance.
 | ||||
|         r.enable.write(|w| w.enable().enabled()); | ||||
| 
 | ||||
|         // Configure mode.
 | ||||
|         let mode = config.mode; | ||||
|         r.config.write(|w| { | ||||
|             // Can't match on `mode` due to embedded-hal, see https://github.com/rust-embedded/embedded-hal/pull/126
 | ||||
|             if mode == MODE_0 { | ||||
|                 w.order().msb_first(); | ||||
|                 w.cpol().active_high(); | ||||
|                 w.cpha().leading(); | ||||
|             } else if mode == MODE_1 { | ||||
|                 w.order().msb_first(); | ||||
|                 w.cpol().active_high(); | ||||
|                 w.cpha().trailing(); | ||||
|             } else if mode == MODE_2 { | ||||
|                 w.order().msb_first(); | ||||
|                 w.cpol().active_low(); | ||||
|                 w.cpha().leading(); | ||||
|             } else { | ||||
|                 w.order().msb_first(); | ||||
|                 w.cpol().active_low(); | ||||
|                 w.cpha().trailing(); | ||||
|             } | ||||
|             w | ||||
|         }); | ||||
| 
 | ||||
|         // Configure frequency.
 | ||||
|         let frequency = config.frequency; | ||||
|         r.frequency.write(|w| w.frequency().variant(frequency)); | ||||
| 
 | ||||
|         // Set over-read character
 | ||||
|         let orc = config.orc; | ||||
|         r.orc.write(|w| | ||||
|             // The ORC field is 8 bits long, so any u8 is a valid value to write.
 | ||||
|             unsafe { w.orc().bits(orc) }); | ||||
| 
 | ||||
|         // Disable all events interrupts
 | ||||
|         r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); | ||||
| 
 | ||||
|         Self { | ||||
|             inner: PeripheralMutex::new( | ||||
|                 State { | ||||
|                     spim, | ||||
|                     waker: WakerRegistration::new(), | ||||
|                 }, | ||||
|                 irq, | ||||
|             ), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn inner(self: Pin<&mut Self>) -> Pin<&mut PeripheralMutex<State<T>>> { | ||||
|         unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().inner) } | ||||
|     } | ||||
| 
 | ||||
|     pub fn free(self: Pin<&mut Self>) -> (T, T::Interrupt) { | ||||
|         let (state, irq) = self.inner().free(); | ||||
|         (state.spim, irq) | ||||
|     } | ||||
| 
 | ||||
|     pub fn send_receive<'a>( | ||||
|         mut self: Pin<&'a mut Self>, | ||||
|         tx: &'a [u8], | ||||
|         rx: &'a mut [u8], | ||||
|     ) -> impl Future<Output = Result<(), Error>> + 'a { | ||||
|         async move { | ||||
|             slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?; | ||||
|             slice_in_ram_or(rx, Error::DMABufferNotInDataMemory)?; | ||||
| 
 | ||||
|             self.as_mut().inner().with(|s, _irq| { | ||||
|                 // Conservative compiler fence to prevent optimizations that do not
 | ||||
|                 // take in to account actions by DMA. The fence has been placed here,
 | ||||
|                 // before any DMA action has started.
 | ||||
|                 compiler_fence(Ordering::SeqCst); | ||||
| 
 | ||||
|                 let r = s.spim.regs(); | ||||
| 
 | ||||
|                 // Set up the DMA write.
 | ||||
|                 r.txd | ||||
|                     .ptr | ||||
|                     .write(|w| unsafe { w.ptr().bits(tx.as_ptr() as u32) }); | ||||
|                 r.txd | ||||
|                     .maxcnt | ||||
|                     .write(|w| unsafe { w.maxcnt().bits(tx.len() as _) }); | ||||
| 
 | ||||
|                 // Set up the DMA read.
 | ||||
|                 r.rxd | ||||
|                     .ptr | ||||
|                     .write(|w| unsafe { w.ptr().bits(rx.as_mut_ptr() as u32) }); | ||||
|                 r.rxd | ||||
|                     .maxcnt | ||||
|                     .write(|w| unsafe { w.maxcnt().bits(rx.len() as _) }); | ||||
| 
 | ||||
|                 // Reset and enable the event
 | ||||
|                 r.events_end.reset(); | ||||
|                 r.intenset.write(|w| w.end().set()); | ||||
| 
 | ||||
|                 // Start SPI transaction.
 | ||||
|                 r.tasks_start.write(|w| unsafe { w.bits(1) }); | ||||
| 
 | ||||
|                 // Conservative compiler fence to prevent optimizations that do not
 | ||||
|                 // take in to account actions by DMA. The fence has been placed here,
 | ||||
|                 // after all possible DMA actions have completed.
 | ||||
|                 compiler_fence(Ordering::SeqCst); | ||||
|             }); | ||||
| 
 | ||||
|             // Wait for 'end' event.
 | ||||
|             poll_fn(|cx| { | ||||
|                 self.as_mut().inner().with(|s, _irq| { | ||||
|                     let r = s.spim.regs(); | ||||
|                     if r.events_end.read().bits() != 0 { | ||||
|                         return Poll::Ready(()); | ||||
|                     } | ||||
|                     s.waker.register(cx.waker()); | ||||
|                     Poll::Pending | ||||
|                 }) | ||||
|             }) | ||||
|             .await; | ||||
| 
 | ||||
|             Ok(()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<U: Instance> PeripheralState for State<U> { | ||||
|     type Interrupt = U::Interrupt; | ||||
|     fn on_interrupt(&mut self) { | ||||
|         if self.spim.regs().events_end.read().bits() != 0 { | ||||
|             self.spim.regs().intenclr.write(|w| w.end().clear()); | ||||
|             self.waker.wake() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| mod sealed { | ||||
|     pub trait Instance {} | ||||
| } | ||||
| 
 | ||||
| pub trait Instance: sealed::Instance { | ||||
|     type Interrupt: Interrupt; | ||||
|     fn regs(&mut self) -> &pac::spim0::RegisterBlock; | ||||
| } | ||||
| 
 | ||||
| impl sealed::Instance for pac::SPIM0 {} | ||||
| impl Instance for pac::SPIM0 { | ||||
|     #[cfg(feature = "52810")] | ||||
|     type Interrupt = interrupt::SPIM0_SPIS0_SPI0; | ||||
|     #[cfg(not(feature = "52810"))] | ||||
|     type Interrupt = interrupt::SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0; | ||||
|     fn regs(&mut self) -> &pac::spim0::RegisterBlock { | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] | ||||
| impl sealed::Instance for pac::SPIM1 {} | ||||
| #[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] | ||||
| impl Instance for pac::SPIM1 { | ||||
|     type Interrupt = interrupt::SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1; | ||||
|     fn regs(&mut self) -> &pac::spim0::RegisterBlock { | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] | ||||
| impl sealed::Instance for pac::SPIM2 {} | ||||
| #[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] | ||||
| impl Instance for pac::SPIM2 { | ||||
|     type Interrupt = interrupt::SPIM2_SPIS2_SPI2; | ||||
|     fn regs(&mut self) -> &pac::spim0::RegisterBlock { | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(any(feature = "52833", feature = "52840"))] | ||||
| impl sealed::Instance for pac::SPIM3 {} | ||||
| #[cfg(any(feature = "52833", feature = "52840"))] | ||||
| impl Instance for pac::SPIM3 { | ||||
|     type Interrupt = interrupt::SPIM3; | ||||
|     fn regs(&mut self) -> &pac::spim0::RegisterBlock { | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: sealed::Instance> sealed::Instance for &mut T {} | ||||
| impl<T: Instance> Instance for &mut T { | ||||
|     type Interrupt = T::Interrupt; | ||||
|     fn regs(&mut self) -> &pac::spim0::RegisterBlock { | ||||
|         T::regs(*self) | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user