From ebdd9c346e8d8eea6df3b4759c1c4a373f01d9df Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Thu, 2 May 2019 10:16:51 +0200 Subject: [PATCH 1/8] Added interrupt driven UARTE --- nrf52-hal-common/Cargo.toml | 4 + nrf52-hal-common/src/uarte.rs | 340 ++++++++++++++++++++++++++++------ 2 files changed, 291 insertions(+), 53 deletions(-) diff --git a/nrf52-hal-common/Cargo.toml b/nrf52-hal-common/Cargo.toml index 10968b3d..95528c52 100644 --- a/nrf52-hal-common/Cargo.toml +++ b/nrf52-hal-common/Cargo.toml @@ -22,6 +22,10 @@ nb = "0.1.1" fpa = "0.1.0" rand_core = "0.4.0" +[dependencies.heapless] +version = "0.4.3" +features = ["min-const-fn"] + [dependencies.void] default-features = false version = "1.0.2" diff --git a/nrf52-hal-common/src/uarte.rs b/nrf52-hal-common/src/uarte.rs index 5ea57bda..7a141d26 100644 --- a/nrf52-hal-common/src/uarte.rs +++ b/nrf52-hal-common/src/uarte.rs @@ -4,32 +4,26 @@ //! //! - nrf52832: Section 35 //! - nrf52840: Section 6.34 +use core::fmt; use core::ops::Deref; use core::sync::atomic::{compiler_fence, Ordering::SeqCst}; -use core::fmt; -use crate::target::{ - uarte0, - UARTE0, -}; +use crate::target::{uarte0, UARTE0}; -use crate::target_constants::EASY_DMA_SIZE; +use crate::gpio::{Floating, Input, Output, Pin, PushPull}; use crate::prelude::*; -use crate::gpio::{ - Pin, - Output, - PushPull, - Input, - Floating, -}; +use crate::target_constants::EASY_DMA_SIZE; use crate::timer::{self, Timer}; -// Re-export SVD variants to allow user to directly set values -pub use crate::target::uarte0::{ - baudrate::BAUDRATEW as Baudrate, - config::PARITYW as Parity, +use heapless::{ + consts::*, + pool, + pool::singleton::{Box, Pool}, + spsc::{Consumer, Queue}, }; +// Re-export SVD variants to allow user to directly set values +pub use crate::target::uarte0::{baudrate::BAUDRATEW as Baudrate, config::PARITYW as Parity}; /// Interface to a UARTE instance /// @@ -41,7 +35,10 @@ pub use crate::target::uarte0::{ /// - nrf52840: Section 6.1.2 pub struct Uarte(T); -impl Uarte where T: Instance { +impl Uarte +where + T: Instance, +{ pub fn new(uarte: T, mut pins: Pins, parity: Parity, baudrate: Baudrate) -> Self { // Select pins uarte.psel.rxd.write(|w| { @@ -82,21 +79,16 @@ impl Uarte where T: Instance { }); // Enable UARTE instance - uarte.enable.write(|w| - w.enable().enabled() - ); + uarte.enable.write(|w| w.enable().enabled()); // Configure let hardware_flow_control = pins.rts.is_some() && pins.cts.is_some(); - uarte.config.write(|w| - w.hwfc().bit(hardware_flow_control) - .parity().variant(parity) - ); + uarte + .config + .write(|w| w.hwfc().bit(hardware_flow_control).parity().variant(parity)); // Configure frequency - uarte.baudrate.write(|w| - w.baudrate().variant(baudrate) - ); + uarte.baudrate.write(|w| w.baudrate().variant(baudrate)); Uarte(uarte) } @@ -107,11 +99,7 @@ impl Uarte where T: Instance { /// /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. - pub fn write(&mut self, - tx_buffer : &[u8], - ) - -> Result<(), Error> - { + pub fn write(&mut self, tx_buffer: &[u8]) -> Result<(), Error> { if tx_buffer.len() > EASY_DMA_SIZE { return Err(Error::TxBufferTooLong); } @@ -129,8 +117,7 @@ impl Uarte where T: Instance { // // The PTR field is a full 32 bits wide and accepts the full range // of values. - unsafe { w.ptr().bits(tx_buffer.as_ptr() as u32) } - ); + unsafe { w.ptr().bits(tx_buffer.as_ptr() as u32) }); self.0.txd.maxcnt.write(|w| // We're giving it the length of the buffer, so no danger of // accessing invalid memory. We have verified that the length of the @@ -169,11 +156,7 @@ impl Uarte where T: Instance { /// until the buffer is full. /// /// The buffer must have a length of at most 255 bytes - pub fn read(&mut self, - rx_buffer : &mut [u8], - ) - -> Result<(), Error> - { + pub fn read(&mut self, rx_buffer: &mut [u8]) -> Result<(), Error> { self.start_read(rx_buffer)?; // Wait for transmission to end @@ -206,8 +189,10 @@ impl Uarte where T: Instance { &mut self, rx_buffer: &mut [u8], timer: &mut Timer, - cycles: u32 - ) -> Result<(), Error> where I: timer::Instance + cycles: u32, + ) -> Result<(), Error> + where + I: timer::Instance, { // Start the read self.start_read(rx_buffer)?; @@ -270,8 +255,7 @@ impl Uarte where T: Instance { // // The PTR field is a full 32 bits wide and accepts the full range // of values. - unsafe { w.ptr().bits(rx_buffer.as_ptr() as u32) } - ); + unsafe { w.ptr().bits(rx_buffer.as_ptr() as u32) }); self.0.rxd.maxcnt.write(|w| // We're giving it the length of the buffer, so no danger of // accessing invalid memory. We have verified that the length of the @@ -303,8 +287,7 @@ impl Uarte where T: Instance { /// Stop an unfinished UART read transaction and flush FIFO to DMA buffer fn cancel_read(&mut self) { // Stop reception - self.0.tasks_stoprx.write(|w| - unsafe { w.bits(1) }); + self.0.tasks_stoprx.write(|w| unsafe { w.bits(1) }); // Wait for the reception to have stopped while self.0.events_rxto.read().bits() == 0 {} @@ -313,8 +296,7 @@ impl Uarte where T: Instance { self.0.events_rxto.write(|w| w); // Ask UART to flush FIFO to DMA buffer - self.0.tasks_flushrx.write(|w| - unsafe { w.bits(1) }); + self.0.tasks_flushrx.write(|w| unsafe { w.bits(1) }); // Wait for the flush to complete. while self.0.events_endrx.read().bits() == 0 {} @@ -326,9 +308,257 @@ impl Uarte where T: Instance { pub fn free(self) -> T { self.0 } + + /// Splits the UARTE into a transmitter and receiver for interrupt driven use + /// + /// In needs a `Queue` to place received DMA chunks, and a `Consumer` to place DMA chunks to + /// send + /// + /// Note: The act of splitting might not be needed on the nRF52 chips, as they map to the same + /// interrupt in the end. Kept as a split for now, but might be merged in the future. + pub fn split( + self, + rxq: Queue, U2>, + txc: Consumer<'static, Box, TXQSize>, + ) -> (UarteRX, UarteTX) { + let mut rx = UarteRX::::new(rxq); + rx.enable_interrupts(); + rx.prepare_read().unwrap(); + rx.start_read(); + + let tx = UarteTX::::new(txc); + tx.enable_interrupts(); + (rx, tx) + } +} + +/// DMA block size +pub const DMA_SIZE: usize = 16; +pool!(DMAPool: [u8; DMA_SIZE]); + +/// UARTE RX part, used in interrupt driven contexts +pub struct UarteRX { + rxq: Queue, U2>, // double buffering of DMA chunks + _marker: core::marker::PhantomData, +} + +/// Receive error in interrupt driven context +#[derive(Debug)] +pub enum RXError { + /// Out of memory error, global pool is depleted + /// + /// Potential causes: + /// 1. User code is saving the `Box` (and not dropping them) + /// 2. User code running `mem::forget` on the `Box` + /// 3. The pool is too small for the use case + OOM, } -impl fmt::Write for Uarte where T: Instance { +impl UarteRX +where + T: Instance, +{ + /// Construct new UARTE RX, hidden from users - used internally + fn new(rxq: Queue, U2>) -> Self { + Self { + rxq, + _marker: core::marker::PhantomData, + } + } + + /// Used internally to set up the proper interrupts + fn enable_interrupts(&self) { + // This operation is safe due to type-state programming guaranteeing that the RX and TX are + // unique within the driver + let uarte = unsafe { &*T::ptr() }; + + uarte + .inten + .modify(|_, w| w.endrx().set_bit().rxstarted().set_bit()); + } + + /// Start a UARTE read transaction + fn start_read(&mut self) { + // This operation is safe due to type-state programming guaranteeing that the RX and TX are + // unique within the driver + let uarte = unsafe { &*T::ptr() }; + + // Start UARTE Receive transaction + uarte.tasks_startrx.write(|w| + // `1` is a valid value to write to task registers. + unsafe { w.bits(1) }); + } + + /// Prepare UARTE read transaction + fn prepare_read(&mut self) -> Result<(), RXError> { + // This operation is safe due to type-state programming guaranteeing that the RX and TX are + // unique within the driver + let uarte = unsafe { &*T::ptr() }; + + let b = DMAPool::alloc().ok_or(RXError::OOM)?.freeze(); + compiler_fence(SeqCst); + + // setup start address + uarte + .rxd + .ptr + .write(|w| unsafe { w.ptr().bits(b.as_ptr() as u32) }); + // setup length + uarte + .rxd + .maxcnt + .write(|w| unsafe { w.maxcnt().bits(b.len() as _) }); + + if self.rxq.enqueue(b).is_err() { + panic!("Internal driver error, RX Queue Overflow"); + } else { + Ok(()) + } + } + + /// Main entry point for the RX driver - Must be called from the corresponding UARTE Interrupt + /// handler/task. + /// + /// Will return: + /// 1. `Ok(Some(Box))` if data was received + /// 2. `Ok(None)` if there was no data, i.e. UARTE interrupt was due to other events + /// 3. `Err(RXError::OOM)` if the memory pool was depleted, see `RXError` for mitigations + pub fn process_interrupt(&mut self) -> Result>, RXError> { + // This operation is safe due to type-state programming guaranteeing that the RX and TX are + // unique within the driver + let uarte = unsafe { &*T::ptr() }; + + // check if dma rx transaction has started + if uarte.events_rxstarted.read().bits() == 1 { + // DMA transaction has started + self.prepare_read()?; + + // Reset the event, otherwise it will always read `1` from now on. + uarte.events_rxstarted.write(|w| w); + } + + // check id dma transaction finished + if uarte.events_endrx.read().bits() == 1 { + // our transaction has finished + if let Some(ret_b) = self.rxq.dequeue() { + // Reset the event, otherwise it will always read `1` from now on. + uarte.events_endrx.write(|w| w); + + self.start_read(); + + return Ok(Some(ret_b)); // ok to return, rx started will be caught later + } else { + panic!("Internal driver error, RX Queue Underflow"); + } + } + + // the interrupt was not RXSTARTED or ENDRX, so no action + Ok(None) + } +} + +/// A transmit queue size of 4 `DMAPool` chunks for now +pub type TXQSize = U4; + +/// UARTE TX part, used in interrupt driven contexts +pub struct UarteTX { + txc: Consumer<'static, Box, TXQSize>, // chunks to transmit + current: Option>, + _marker: core::marker::PhantomData, +} + +impl UarteTX +where + T: Instance, +{ + /// Construct new UARTE TX, hidden from users - used internally + fn new(txc: Consumer<'static, Box, TXQSize>) -> Self { + Self { + txc, + current: None, + _marker: core::marker::PhantomData, + } + } + + /// Used internally to set up the proper interrupts + fn enable_interrupts(&self) { + // This operation is safe due to type-state programming guaranteeing that the RX and TX are + // unique within the driver + let uarte = unsafe { &*T::ptr() }; + + uarte.inten.modify(|_, w| w.endtx().set_bit()); + } + + /// Sets up the UARTE to send DMA chunk + fn start_write(&mut self, b: Box) { + // This operation is safe due to type-state programming guaranteeing that the RX and TX are + // unique within the driver + let uarte = unsafe { &*T::ptr() }; + + compiler_fence(SeqCst); + + // setup start address + uarte + .txd + .ptr + .write(|w| unsafe { w.ptr().bits(b.as_ptr() as u32) }); + + // setup length + uarte + .txd + .maxcnt + .write(|w| unsafe { w.maxcnt().bits(b.len() as _) }); + + // Start UARTE transmit transaction + uarte.tasks_starttx.write(|w| unsafe { w.bits(1) }); + self.current = Some(b); // drops the previous current package + } + + /// Main entry point for the TX driver - Must be called from the corresponding UARTE Interrupt + /// handler/task. + pub fn process_interrupt(&mut self) { + // This operation is safe due to type-state programming guaranteeing that the RX and TX are + // unique within the driver + let uarte = unsafe { &*T::ptr() }; + + // ENDTX event? (DMA transaction finished) + if uarte.events_endtx.read().bits() == 1 { + // our transaction has finished + match self.txc.dequeue() { + None => { + // a ENDTX without an started transaction is an error + if self.current.is_none() { + panic!("Internal error, ENDTX without current transaction.") + } + // we don't have any more to send, so drop the current buffer + self.current = None; + } + Some(b) => { + self.start_write(b); + } + } + + // Reset the event, otherwise it will always read `1` from now on. + uarte.events_endtx.write(|w| w); + } else { + if self.current.is_none() { + match self.txc.dequeue() { + Some(b) => + // we were idle, so start a new transaction + { + self.start_write(b) + } + None => (), + } + } + } + } +} + +impl fmt::Write for Uarte +where + T: Instance, +{ fn write_str(&mut self, s: &str) -> fmt::Result { // Copy all data into an on-stack buffer so we never try to EasyDMA from // flash @@ -349,7 +579,6 @@ pub struct Pins { pub rts: Option>>, } - #[derive(Debug)] pub enum Error { TxBufferTooLong, @@ -359,7 +588,12 @@ pub enum Error { Timeout(usize), } +pub trait Instance: Deref { + fn ptr() -> *const uarte0::RegisterBlock; +} -pub trait Instance: Deref {} - -impl Instance for UARTE0 {} +impl Instance for UARTE0 { + fn ptr() -> *const uarte0::RegisterBlock { + UARTE0::ptr() + } +} From d1ff60e03cf48d1933cb26bcbd6277ff770c598b Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Thu, 2 May 2019 22:06:30 +0200 Subject: [PATCH 2/8] Updated heapless dep, added missing exports --- nrf52-hal-common/Cargo.toml | 2 +- nrf52-hal-common/src/lib.rs | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/nrf52-hal-common/Cargo.toml b/nrf52-hal-common/Cargo.toml index 95528c52..6a1acbd0 100644 --- a/nrf52-hal-common/Cargo.toml +++ b/nrf52-hal-common/Cargo.toml @@ -23,7 +23,7 @@ fpa = "0.1.0" rand_core = "0.4.0" [dependencies.heapless] -version = "0.4.3" +version = "0.4.4" features = ["min-const-fn"] [dependencies.void] diff --git a/nrf52-hal-common/src/lib.rs b/nrf52-hal-common/src/lib.rs index c5e214d7..ba5c1655 100644 --- a/nrf52-hal-common/src/lib.rs +++ b/nrf52-hal-common/src/lib.rs @@ -53,8 +53,7 @@ pub mod target_constants { /// 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 + ptr >= target_constants::SRAM_LOWER && (ptr + slice.len()) < target_constants::SRAM_UPPER } /// A handy structure for converting rust slices into ptr and len pairs @@ -67,10 +66,7 @@ pub(crate) struct DmaSlice { impl DmaSlice { pub fn null() -> Self { - Self { - ptr: 0, - len: 0, - } + Self { ptr: 0, len: 0 } } pub fn from_slice(slice: &[u8]) -> Self { @@ -89,4 +85,4 @@ pub use crate::saadc::Saadc; pub use crate::spim::Spim; pub use crate::timer::Timer; pub use crate::twim::Twim; -pub use crate::uarte::Uarte; +pub use crate::uarte::{DMAPool, RXError, TXQSize, Uarte, UarteRX, UarteTX, DMA_SIZE}; From 618330f901c0e375ff17d3ac1dd5639d59498140 Mon Sep 17 00:00:00 2001 From: Per Date: Fri, 3 May 2019 12:37:53 +0200 Subject: [PATCH 3/8] transmit queue generic over size S --- nrf52-hal-common/src/lib.rs | 5 ++-- nrf52-hal-common/src/uarte.rs | 49 +++++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/nrf52-hal-common/src/lib.rs b/nrf52-hal-common/src/lib.rs index ba5c1655..7dfbcc61 100644 --- a/nrf52-hal-common/src/lib.rs +++ b/nrf52-hal-common/src/lib.rs @@ -53,7 +53,8 @@ pub mod target_constants { /// 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 + ptr >= target_constants::SRAM_LOWER + && (ptr + slice.len()) < target_constants::SRAM_UPPER } /// A handy structure for converting rust slices into ptr and len pairs @@ -85,4 +86,4 @@ pub use crate::saadc::Saadc; pub use crate::spim::Spim; pub use crate::timer::Timer; pub use crate::twim::Twim; -pub use crate::uarte::{DMAPool, RXError, TXQSize, Uarte, UarteRX, UarteTX, DMA_SIZE}; +pub use crate::uarte::{DMAPool, RXError, Uarte, UarteRX, UarteTX, DMA_SIZE}; diff --git a/nrf52-hal-common/src/uarte.rs b/nrf52-hal-common/src/uarte.rs index 7a141d26..750fadc0 100644 --- a/nrf52-hal-common/src/uarte.rs +++ b/nrf52-hal-common/src/uarte.rs @@ -20,10 +20,13 @@ use heapless::{ pool, pool::singleton::{Box, Pool}, spsc::{Consumer, Queue}, + ArrayLength, }; // Re-export SVD variants to allow user to directly set values -pub use crate::target::uarte0::{baudrate::BAUDRATEW as Baudrate, config::PARITYW as Parity}; +pub use crate::target::uarte0::{ + baudrate::BAUDRATEW as Baudrate, config::PARITYW as Parity, +}; /// Interface to a UARTE instance /// @@ -39,7 +42,12 @@ impl Uarte where T: Instance, { - pub fn new(uarte: T, mut pins: Pins, parity: Parity, baudrate: Baudrate) -> Self { + pub fn new( + uarte: T, + mut pins: Pins, + parity: Parity, + baudrate: Baudrate, + ) -> Self { // Select pins uarte.psel.rxd.write(|w| { let w = unsafe { w.pin().bits(pins.rxd.pin) }; @@ -83,9 +91,9 @@ where // Configure let hardware_flow_control = pins.rts.is_some() && pins.cts.is_some(); - uarte - .config - .write(|w| w.hwfc().bit(hardware_flow_control).parity().variant(parity)); + uarte.config.write(|w| { + w.hwfc().bit(hardware_flow_control).parity().variant(parity) + }); // Configure frequency uarte.baudrate.write(|w| w.baudrate().variant(baudrate)); @@ -316,17 +324,20 @@ where /// /// Note: The act of splitting might not be needed on the nRF52 chips, as they map to the same /// interrupt in the end. Kept as a split for now, but might be merged in the future. - pub fn split( + pub fn split( self, rxq: Queue, U2>, - txc: Consumer<'static, Box, TXQSize>, - ) -> (UarteRX, UarteTX) { + txc: Consumer<'static, Box, S>, + ) -> (UarteRX, UarteTX) + where + S: ArrayLength>, + { let mut rx = UarteRX::::new(rxq); rx.enable_interrupts(); rx.prepare_read().unwrap(); rx.start_read(); - let tx = UarteTX::::new(txc); + let tx = UarteTX::::new(txc); tx.enable_interrupts(); (rx, tx) } @@ -423,7 +434,9 @@ where /// 1. `Ok(Some(Box))` if data was received /// 2. `Ok(None)` if there was no data, i.e. UARTE interrupt was due to other events /// 3. `Err(RXError::OOM)` if the memory pool was depleted, see `RXError` for mitigations - pub fn process_interrupt(&mut self) -> Result>, RXError> { + pub fn process_interrupt( + &mut self, + ) -> Result>, RXError> { // This operation is safe due to type-state programming guaranteeing that the RX and TX are // unique within the driver let uarte = unsafe { &*T::ptr() }; @@ -457,22 +470,24 @@ where } } -/// A transmit queue size of 4 `DMAPool` chunks for now -pub type TXQSize = U4; - /// UARTE TX part, used in interrupt driven contexts -pub struct UarteTX { - txc: Consumer<'static, Box, TXQSize>, // chunks to transmit +/// S is the queue length, can be U3, U4 etc. +pub struct UarteTX +where + S: ArrayLength>, +{ + txc: Consumer<'static, Box, S>, // chunks to transmit current: Option>, _marker: core::marker::PhantomData, } -impl UarteTX +impl UarteTX where T: Instance, + S: ArrayLength>, { /// Construct new UARTE TX, hidden from users - used internally - fn new(txc: Consumer<'static, Box, TXQSize>) -> Self { + fn new(txc: Consumer<'static, Box, S>) -> Self { Self { txc, current: None, From bc93bc259113ccfb309a4d4427271e6374126254 Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Sun, 5 May 2019 01:22:11 +0200 Subject: [PATCH 4/8] DMA_SIZE now parametrised --- nrf52-hal-common/Cargo.toml | 8 +++++ nrf52-hal-common/src/uarte.rs | 68 +++++++++++++++++++++++++++-------- nrf52810-hal/Cargo.toml | 13 ++++++- nrf52832-hal/Cargo.toml | 13 +++++-- nrf52840-hal/Cargo.toml | 12 ++++++- 5 files changed, 95 insertions(+), 19 deletions(-) diff --git a/nrf52-hal-common/Cargo.toml b/nrf52-hal-common/Cargo.toml index 6a1acbd0..7a466764 100644 --- a/nrf52-hal-common/Cargo.toml +++ b/nrf52-hal-common/Cargo.toml @@ -56,3 +56,11 @@ default = ["52832"] 52810 = ["nrf52810-pac"] 52832 = ["nrf52832-pac"] 52840 = ["nrf52840-pac"] +POOL = [] +DMA_SIZE_4 = [] +DMA_SIZE_8 = [] +DMA_SIZE_16 = [] +DMA_SIZE_32 = [] +DMA_SIZE_64 = [] +DMA_SIZE_128 = [] +DMA_SIZE_256 = [] diff --git a/nrf52-hal-common/src/uarte.rs b/nrf52-hal-common/src/uarte.rs index 750fadc0..f815bd0d 100644 --- a/nrf52-hal-common/src/uarte.rs +++ b/nrf52-hal-common/src/uarte.rs @@ -24,9 +24,7 @@ use heapless::{ }; // Re-export SVD variants to allow user to directly set values -pub use crate::target::uarte0::{ - baudrate::BAUDRATEW as Baudrate, config::PARITYW as Parity, -}; +pub use crate::target::uarte0::{baudrate::BAUDRATEW as Baudrate, config::PARITYW as Parity}; /// Interface to a UARTE instance /// @@ -42,12 +40,7 @@ impl Uarte where T: Instance, { - pub fn new( - uarte: T, - mut pins: Pins, - parity: Parity, - baudrate: Baudrate, - ) -> Self { + pub fn new(uarte: T, mut pins: Pins, parity: Parity, baudrate: Baudrate) -> Self { // Select pins uarte.psel.rxd.write(|w| { let w = unsafe { w.pin().bits(pins.rxd.pin) }; @@ -91,9 +84,9 @@ where // Configure let hardware_flow_control = pins.rts.is_some() && pins.cts.is_some(); - uarte.config.write(|w| { - w.hwfc().bit(hardware_flow_control).parity().variant(parity) - }); + uarte + .config + .write(|w| w.hwfc().bit(hardware_flow_control).parity().variant(parity)); // Configure frequency uarte.baudrate.write(|w| w.baudrate().variant(baudrate)); @@ -344,7 +337,54 @@ where } /// DMA block size +/// Defaults to DMA_SIZE = 16 if not explicitly set +#[cfg(not(any( + feature = "DMA_SIZE_4", + feature = "DMA_SIZE_8", + feature = "DMA_SIZE_16", + feature = "DMA_SIZE_32", + feature = "DMA_SIZE_64", + feature = "DMA_SIZE_128", + feature = "DMA_SIZE_256" +)))] +pub const DMA_SIZE: usize = 16; + +#[cfg(feature = "DMA_SIZE_4")] +pub const DMA_SIZE: usize = 4; + +#[cfg(feature = "DMA_SIZE_8")] +pub const DMA_SIZE: usize = 8; + +#[cfg(feature = "DMA_SIZE_16")] pub const DMA_SIZE: usize = 16; + +#[cfg(feature = "DMA_SIZE_32")] +pub const DMA_SIZE: usize = 32; + +#[cfg(feature = "DMA_SIZE_64")] +pub const DMA_SIZE: usize = 64; + +#[cfg(feature = "DMA_SIZE_128")] +pub const DMA_SIZE: usize = 128; + +// Currently causes internal OOM, needs fixing +#[cfg(feature = "DMA_SIZE_256")] +pub const DMA_SIZE: usize = 256; + +// An alternative solution to the above is to define the DMA_SIZE +// in a separate (default) crate, which can be overridden +// by a patch in the user Cargo.toml, pointing to +// a local crate with the user defined DMA_SIZE constant. +// What would you prefer? + +// The DMA implementation shuld be hidden behind a feature POOL +// This likely requires a mod {} around the related code +// or the non-ergonomic repetition of the gate. +// Is there a better solution? +// +// The reason to have a POOL gate is that we don't want the POOL +// to cause memory OH if not used by the application + pool!(DMAPool: [u8; DMA_SIZE]); /// UARTE RX part, used in interrupt driven contexts @@ -434,9 +474,7 @@ where /// 1. `Ok(Some(Box))` if data was received /// 2. `Ok(None)` if there was no data, i.e. UARTE interrupt was due to other events /// 3. `Err(RXError::OOM)` if the memory pool was depleted, see `RXError` for mitigations - pub fn process_interrupt( - &mut self, - ) -> Result>, RXError> { + pub fn process_interrupt(&mut self) -> Result>, RXError> { // This operation is safe due to type-state programming guaranteeing that the RX and TX are // unique within the driver let uarte = unsafe { &*T::ptr() }; diff --git a/nrf52810-hal/Cargo.toml b/nrf52810-hal/Cargo.toml index 67ca9e9f..405acf45 100644 --- a/nrf52810-hal/Cargo.toml +++ b/nrf52810-hal/Cargo.toml @@ -41,4 +41,15 @@ version = "0.2.1" [features] doc = [] rt = ["nrf52810-pac/rt"] -default = ["rt"] +default = ["rt", "POOL"] +POOL = ["nrf52-hal-common/POOL"] +DMA_SIZE_4 = ["nrf52-hal-common/DMA_SIZE_4"] +DMA_SIZE_8 = ["nrf52-hal-common/DMA_SIZE_8"] +DMA_SIZE_16 = ["nrf52-hal-common/DMA_SIZE_16"] +DMA_SIZE_32 = ["nrf52-hal-common/DMA_SIZE_32"] +DMA_SIZE_64 = ["nrf52-hal-common/DMA_SIZE_64"] +DMA_SIZE_128 = ["nrf52-hal-common/DMA_SIZE_128"] +DMA_SIZE_256 = ["nrf52-hal-common/DMA_SIZE_256"] + + + diff --git a/nrf52832-hal/Cargo.toml b/nrf52832-hal/Cargo.toml index ea141117..78d21f5f 100644 --- a/nrf52832-hal/Cargo.toml +++ b/nrf52832-hal/Cargo.toml @@ -42,8 +42,17 @@ doc = [] rt = ["nrf52832-pac/rt"] xxAA-package = [] xxAB-package = [] +POOL = ["nrf52-hal-common/POOL"] +DMA_SIZE_4 = ["nrf52-hal-common/DMA_SIZE_4"] +DMA_SIZE_8 = ["nrf52-hal-common/DMA_SIZE_8"] +DMA_SIZE_16 = ["nrf52-hal-common/DMA_SIZE_16"] +DMA_SIZE_32 = ["nrf52-hal-common/DMA_SIZE_32"] +DMA_SIZE_64 = ["nrf52-hal-common/DMA_SIZE_64"] +DMA_SIZE_128 = ["nrf52-hal-common/DMA_SIZE_128"] +DMA_SIZE_256 = ["nrf52-hal-common/DMA_SIZE_256"] + # Note: We use the xxAB package because it has the least amount of available resources. # However, most users will want to use the xxAA package. -default = ["rt", "xxAB-package"] - +# If disabling default features, "POOL" and "DMA_SIZE_XX" should be manually set. +default = ["rt", "xxAB-package", "POOL"] diff --git a/nrf52840-hal/Cargo.toml b/nrf52840-hal/Cargo.toml index b22a03d9..bb9486c7 100644 --- a/nrf52840-hal/Cargo.toml +++ b/nrf52840-hal/Cargo.toml @@ -42,5 +42,15 @@ version = "0.2.1" [features] doc = [] rt = ["nrf52840-pac/rt"] -default = ["rt"] +default = ["rt", "POOL"] +POOL = ["nrf52-hal-common/POOL"] +DMA_SIZE_4 = ["nrf52-hal-common/DMA_SIZE_4"] +DMA_SIZE_8 = ["nrf52-hal-common/DMA_SIZE_8"] +DMA_SIZE_16 = ["nrf52-hal-common/DMA_SIZE_16"] +DMA_SIZE_32 = ["nrf52-hal-common/DMA_SIZE_32"] +DMA_SIZE_64 = ["nrf52-hal-common/DMA_SIZE_64"] +DMA_SIZE_128 = ["nrf52-hal-common/DMA_SIZE_128"] +DMA_SIZE_256 = ["nrf52-hal-common/DMA_SIZE_256"] + + From fffe36c9886bde0e48121a46de443614f5a362dc Mon Sep 17 00:00:00 2001 From: Jacob Rosenthal Date: Mon, 15 Jul 2019 16:11:42 -0700 Subject: [PATCH 5/8] uarte interrupt example from https://github.com/korken89/rtfm_workshop/blob/7a153023d4c7923e058126ae6bd12f9ce7639098/examples/pool.rs --- Cargo.toml | 1 + examples/rtfm-demo/Cargo.toml | 2 +- examples/rtfm-uarte-interrupts/.gdbinit | 10 ++ examples/rtfm-uarte-interrupts/Cargo.toml | 33 ++++++ examples/rtfm-uarte-interrupts/src/main.rs | 114 +++++++++++++++++++++ nrf52-hal-common/Cargo.toml | 3 +- 6 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 examples/rtfm-uarte-interrupts/.gdbinit create mode 100644 examples/rtfm-uarte-interrupts/Cargo.toml create mode 100644 examples/rtfm-uarte-interrupts/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 26d0c8b0..db273ee8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "nrf52832-hal", "nrf52840-hal", "examples/rtfm-demo", + "examples/rtfm-uarte-interrupts", "examples/spi-demo", "examples/twi-ssd1306", ] diff --git a/examples/rtfm-demo/Cargo.toml b/examples/rtfm-demo/Cargo.toml index 4d4ce3da..35120e35 100644 --- a/examples/rtfm-demo/Cargo.toml +++ b/examples/rtfm-demo/Cargo.toml @@ -5,7 +5,7 @@ authors = ["James Munns "] edition = "2018" [dependencies] -cortex-m-rtfm = "0.4.3" +cortex-m-rtfm = { git = "https://github.com/japaric/cortex-m-rtfm.git"} panic-semihosting = "0.5.1" cortex-m-semihosting = "0.3.3" diff --git a/examples/rtfm-uarte-interrupts/.gdbinit b/examples/rtfm-uarte-interrupts/.gdbinit new file mode 100644 index 00000000..ec068055 --- /dev/null +++ b/examples/rtfm-uarte-interrupts/.gdbinit @@ -0,0 +1,10 @@ +set remotetimeout 60000 +target remote :2331 +set arm force-mode thumb + +# Uncomment to enable semihosting, when necessary +monitor semihosting enable + +layout split +monitor reset +load diff --git a/examples/rtfm-uarte-interrupts/Cargo.toml b/examples/rtfm-uarte-interrupts/Cargo.toml new file mode 100644 index 00000000..749c102e --- /dev/null +++ b/examples/rtfm-uarte-interrupts/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "rtfm-uarte-interrupts" +version = "0.1.0" +authors = ["James Munns "] +edition = "2018" + +[dependencies] +cortex-m-rtfm = { git = "https://github.com/japaric/cortex-m-rtfm.git"} +panic-semihosting = "0.5.1" +cortex-m-semihosting = "0.3.3" +heapless = ">= 0.5.0" + +[dependencies.nrf52810-hal] +version = "0.8.0" +path = "../../nrf52810-hal" +optional = true + +[dependencies.nrf52832-hal] +version = "0.8.0" +path = "../../nrf52832-hal" +optional = true +features = ["rt", "xxAB-package", "POOL"] + +[dependencies.nrf52840-hal] +version = "0.8.0" +path = "../../nrf52840-hal" +optional = true + +[features] +52810 = ["nrf52810-hal"] +52832 = ["nrf52832-hal"] +52840 = ["nrf52840-hal"] +default = ["52832"] diff --git a/examples/rtfm-uarte-interrupts/src/main.rs b/examples/rtfm-uarte-interrupts/src/main.rs new file mode 100644 index 00000000..4be0da62 --- /dev/null +++ b/examples/rtfm-uarte-interrupts/src/main.rs @@ -0,0 +1,114 @@ +#![no_main] +#![no_std] + +// panic handler +extern crate panic_semihosting; + +use cortex_m_semihosting::hprintln; + +use nrf52832_hal as hal; + +use hal::gpio::{p0, Level}; +use hal::target::{interrupt, UARTE0}; +use hal::{uarte, Uarte}; +use hal::{DMAPool, RXError, UarteRX, UarteTX, DMA_SIZE}; + +use heapless::{ + consts::U4, + pool::singleton::Box, + pool::singleton::Pool, + spsc::{Producer, Queue}, +}; + +use rtfm::app; + +const NR_PACKAGES: usize = 10; +const DMA_MEM: usize = DMA_SIZE * NR_PACKAGES + 16; + +//not sure about U4 +type TXQSize = U4; + +#[app(device = crate::hal::target)] +const APP: () = { + static mut RX: UarteRX = (); + //not sure about u4 + static mut TX: UarteTX = (); + static mut PRODUCER: Producer<'static, Box, TXQSize> = (); + + #[init(spawn = [])] + fn init(c: init::Context) -> init::LateResources { + // for the actual DMA buffers + static mut MEMORY: [u8; DMA_MEM] = [0; DMA_MEM]; + // for the producer/consumer of TX + static mut TX_RB: Option, TXQSize>> = None; + + hprintln!("init").unwrap(); + // move MEMORY to P (the DMA buffer allocator) + DMAPool::grow(MEMORY); + + let port0 = p0::Parts::new(c.device.P0); + + let uarte0 = Uarte::new( + c.device.UARTE0, + uarte::Pins { + txd: port0.p0_06.into_push_pull_output(Level::High).degrade(), + rxd: port0.p0_08.into_floating_input().degrade(), + cts: None, + rts: None, + }, + uarte::Parity::EXCLUDED, + uarte::Baudrate::BAUD115200, + ); + + *TX_RB = Some(Queue::new()); + let (txp, txc) = TX_RB.as_mut().unwrap().split(); + let (rx, tx) = uarte0.split(Queue::new(), txc); + + init::LateResources { + RX: rx, + TX: tx, + PRODUCER: txp, + } + } + + // // we can get Box

us being now the owner + #[task(capacity = 2, resources = [PRODUCER])] + fn printer(c: printer::Context, data: Box) { + // enqueue a test message + // let mut b = DMAPool::alloc().unwrap().freeze(); + // b.copy_from_slice(&[0, 1, 2, 3]); + + // hprintln!("{:?}", &data).unwrap(); + // just do the buffer dance without copying + c.resources.PRODUCER.enqueue(data).unwrap(); + rtfm::pend(interrupt::UARTE0_UART0); + } + + #[task] + fn rx_error(_: rx_error::Context, err: RXError) { + hprintln!("rx_error {:?}", err).unwrap(); + } + + #[interrupt(priority = 2, resources = [RX, TX], spawn = [printer, rx_error])] + fn UARTE0_UART0(c: UARTE0_UART0::Context) { + // probe RX + match c.resources.RX.process_interrupt() { + Ok(Some(b)) => { + // delegate data to printer + match c.spawn.printer(b) { + Err(_) => c.spawn.rx_error(RXError::OOM).unwrap(), + _ => (), + }; + } + Ok(None) => (), // no + Err(err) => c.spawn.rx_error(err).unwrap(), + } + + c.resources.TX.process_interrupt(); + } + + extern "C" { + fn SWI1_EGU1(); + fn SWI2_EGU2(); + } +}; diff --git a/nrf52-hal-common/Cargo.toml b/nrf52-hal-common/Cargo.toml index 7a466764..2ee36fba 100644 --- a/nrf52-hal-common/Cargo.toml +++ b/nrf52-hal-common/Cargo.toml @@ -23,8 +23,7 @@ fpa = "0.1.0" rand_core = "0.4.0" [dependencies.heapless] -version = "0.4.4" -features = ["min-const-fn"] +version = "0.5.0" [dependencies.void] default-features = false From 9b5f7813683a90ac38802ef16116d975196c0541 Mon Sep 17 00:00:00 2001 From: Jacob Rosenthal Date: Tue, 16 Jul 2019 12:30:18 -0700 Subject: [PATCH 6/8] add missing .cargo for rtfm-uarte-interrupts example --- examples/rtfm-uarte-interrupts/.cargo/config | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 examples/rtfm-uarte-interrupts/.cargo/config diff --git a/examples/rtfm-uarte-interrupts/.cargo/config b/examples/rtfm-uarte-interrupts/.cargo/config new file mode 100644 index 00000000..90b5200a --- /dev/null +++ b/examples/rtfm-uarte-interrupts/.cargo/config @@ -0,0 +1,2 @@ +[target.thumbv7em-none-eabihf] +runner = "arm-none-eabi-gdb -tui" From 37632a8f36aecb8ec4e30dff5b861f1661a64c84 Mon Sep 17 00:00:00 2001 From: Jacob Rosenthal Date: Tue, 30 Jul 2019 19:00:10 -0700 Subject: [PATCH 7/8] default build target --- examples/rtfm-uarte-interrupts/.cargo/config | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/rtfm-uarte-interrupts/.cargo/config b/examples/rtfm-uarte-interrupts/.cargo/config index 90b5200a..64e3ce85 100644 --- a/examples/rtfm-uarte-interrupts/.cargo/config +++ b/examples/rtfm-uarte-interrupts/.cargo/config @@ -1,2 +1,5 @@ [target.thumbv7em-none-eabihf] runner = "arm-none-eabi-gdb -tui" + +[build] +target = "thumbv7em-none-eabihf" From f4511d905c9e56e92ac4d536b4f185151a3ea330 Mon Sep 17 00:00:00 2001 From: Jacob Rosenthal Date: Sat, 3 Aug 2019 22:06:36 -0700 Subject: [PATCH 8/8] fix to original example --- examples/rtfm-uarte-interrupts/src/main.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/rtfm-uarte-interrupts/src/main.rs b/examples/rtfm-uarte-interrupts/src/main.rs index 4be0da62..b7cc9278 100644 --- a/examples/rtfm-uarte-interrupts/src/main.rs +++ b/examples/rtfm-uarte-interrupts/src/main.rs @@ -14,7 +14,7 @@ use hal::{uarte, Uarte}; use hal::{DMAPool, RXError, UarteRX, UarteTX, DMA_SIZE}; use heapless::{ - consts::U4, + consts::U3, pool::singleton::Box, pool::singleton::Pool, spsc::{Producer, Queue}, @@ -25,14 +25,12 @@ use rtfm::app; const NR_PACKAGES: usize = 10; const DMA_MEM: usize = DMA_SIZE * NR_PACKAGES + 16; -//not sure about U4 -type TXQSize = U4; +type TXQSize = U3; #[app(device = crate::hal::target)] const APP: () = { static mut RX: UarteRX = (); - //not sure about u4 - static mut TX: UarteTX = (); + static mut TX: UarteTX = (); static mut PRODUCER: Producer<'static, Box, TXQSize> = (); #[init(spawn = [])] @@ -48,6 +46,7 @@ const APP: () = { let port0 = p0::Parts::new(c.device.P0); + //adafruit nrf52 le let uarte0 = Uarte::new( c.device.UARTE0, uarte::Pins {