From 53c2829eb17a233f848e64162d16491d18d7bafa Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 29 Dec 2020 19:10:47 -0600 Subject: add dma example; rename uarte --- Cargo.toml | 1 + embassy-stm32f4/src/lib.rs | 2 +- embassy-stm32f4/src/serial.rs | 233 ++++++++++++++++++++++++++++++++++++++++ embassy-stm32f4/src/uarte.rs | 233 ---------------------------------------- examples-stm32f4/Cargo.toml | 38 +++++++ examples-stm32f4/src/dma_adc.rs | 125 +++++++++++++++++++++ 6 files changed, 398 insertions(+), 234 deletions(-) create mode 100644 embassy-stm32f4/src/serial.rs delete mode 100644 embassy-stm32f4/src/uarte.rs create mode 100644 examples-stm32f4/Cargo.toml create mode 100644 examples-stm32f4/src/dma_adc.rs diff --git a/Cargo.toml b/Cargo.toml index e1a1c34b5..be98b26f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "embassy-stm32f4", "embassy-macros", "examples", + "examples-stm32f4", ] exclude = [ diff --git a/embassy-stm32f4/src/lib.rs b/embassy-stm32f4/src/lib.rs index e6d523d21..cfcb49bcb 100644 --- a/embassy-stm32f4/src/lib.rs +++ b/embassy-stm32f4/src/lib.rs @@ -361,6 +361,6 @@ macro_rules! waker_interrupt { // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; -pub mod uarte; +pub mod serial; pub use cortex_m_rt::interrupt; diff --git a/embassy-stm32f4/src/serial.rs b/embassy-stm32f4/src/serial.rs new file mode 100644 index 000000000..273c384cb --- /dev/null +++ b/embassy-stm32f4/src/serial.rs @@ -0,0 +1,233 @@ +//! Async low power UARTE. +//! +//! The peripheral is autmatically enabled and disabled as required to save power. +//! Lowest power consumption can only be guaranteed if the send receive futures +//! are dropped correctly (e.g. not using `mem::forget()`). + +use core::cell::UnsafeCell; +use core::cmp::min; +use core::future::Future; +use core::marker::PhantomPinned; +use core::ops::Deref; +use core::pin::Pin; +use core::ptr; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::{Context, Poll}; +use cortex_m::singleton; + +use embassy::util::Signal; +use embedded_dma::{StaticReadBuffer, StaticWriteBuffer, WriteBuffer}; + +use crate::fmt::assert; +use crate::hal::dma::config::DmaConfig; +use crate::hal::dma::{ + Channel4, Channel7, MemoryToPeripheral, PeripheralToMemory, Stream2, Stream7, StreamsTuple, + Transfer, +}; +use crate::hal::gpio::gpioa::{PA10, PA9}; +use crate::hal::gpio::{Alternate, AF10, AF7, AF9}; +use crate::hal::gpio::{Floating, Input, Output, PushPull}; +use crate::hal::pac; +use crate::hal::prelude::*; +use crate::hal::rcc::Clocks; +use crate::hal::serial::config::{ + Config as SerialConfig, DmaConfig as SerialDmaConfig, Parity, StopBits, WordLength, +}; +use crate::hal::serial::Serial; +use crate::hal::time::Bps; + +use crate::interrupt; + +use crate::pac::Interrupt; +use crate::pac::{DMA2, USART1}; + +use embedded_hal::digital::v2::OutputPin; + +// Re-export SVD variants to allow user to directly set values. +// pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; + +/// Interface to the UARTE peripheral +pub struct Uarte { + instance: Serial>, PA10>)>, + usart: USART1, + dma: DMA2, +} + +struct State { + tx_done: Signal<()>, + rx_done: Signal, +} + +static STATE: State = State { + tx_done: Signal::new(), + rx_done: Signal::new(), +}; + +pub struct Pins { + pub rxd: PA10>, + pub txd: PA9>, + pub dma: DMA2, + pub usart: USART1, +} + +impl Uarte { + pub fn new(mut pins: Pins, parity: Parity, baudrate: Bps, clocks: Clocks) -> Self { + // // Enable interrupts + // uarte.events_endtx.reset(); + // uarte.events_endrx.reset(); + // uarte + // .intenset + // .write(|w| w.endtx().set().txstopped().set().endrx().set().rxto().set()); + // // TODO: Set interrupt priority? + // interrupt::unpend(interrupt::UARTE0_UART0); + // interrupt::enable(interrupt::UARTE0_UART0); + + // Serial>, PA10>)> + let mut serial = Serial::usart1( + pins.usart, + (pins.txd, pins.rxd), + SerialConfig { + baudrate: baudrate, + wordlength: WordLength::DataBits8, + parity: Parity::ParityNone, + stopbits: StopBits::STOP1, + dma: SerialDmaConfig::TxRx, + }, + clocks, + ) + .unwrap(); + + // let is_set = dma.hifcr.read().tcif7.bit_is_set(); + + Uarte { + instance: serial, + dma: pins.dma, + usart: pins.usart, + } + } + + /// Sends serial data. + /// + /// `tx_buffer` is marked as static as per `embedded-dma` requirements. + /// It it safe to use a buffer with a non static lifetime if memory is not + /// reused until the future has finished. + pub fn send<'a, B>(&'a mut self, tx_buffer: B) -> SendFuture<'a, B> + where + B: WriteBuffer + 'static, + { + SendFuture { + uarte: self, + buf: tx_buffer, + transfer: None, + } + } + + /// Receives serial data. + /// + /// The future is pending until the buffer is completely filled. + /// A common pattern is to use [`stop()`](ReceiveFuture::stop) to cancel + /// unfinished transfers after a timeout to prevent lockup when no more data + /// is incoming. + /// + /// `rx_buffer` is marked as static as per `embedded-dma` requirements. + /// It it safe to use a buffer with a non static lifetime if memory is not + /// reused until the future has finished. + pub fn receive<'a, B>(&'a mut self, rx_buffer: B) -> ReceiveFuture<'a, B> + where + B: WriteBuffer + 'static, + { + ReceiveFuture { + uarte: self, + buf: rx_buffer, + transfer: None, + } + } +} + +/// Future for the [`LowPowerUarte::send()`] method. +pub struct SendFuture<'a, B: WriteBuffer + 'static> { + uarte: &'a Uarte, + transfer: Option<&'a Transfer, Channel4, USART1, MemoryToPeripheral, B>>, + buf: B, +} + +impl<'a, B> Drop for SendFuture<'a, B> +where + B: WriteBuffer + 'static, +{ + fn drop(self: &mut Self) { + drop(self.transfer); + } +} + +impl<'a, B> Future for SendFuture<'a, B> +where + B: WriteBuffer + 'static, +{ + type Output = (); + + fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + if !self.transfer.is_none() && self.transfer.unwrap().is_done() { + Poll::Ready(()) + } else { + self.transfer = Some(&mut Transfer::init( + StreamsTuple::new(self.uarte.dma).7, + self.uarte.usart, + self.buf, + // Some(second_buffer), + None, + DmaConfig::default() + .transfer_complete_interrupt(true) + .memory_increment(true) + .double_buffer(false), + )); + + waker_interrupt!(DMA2_STREAM7, cx.waker().clone()); + Poll::Pending + } + } +} + +/// Future for the [`Uarte::receive()`] method. +pub struct ReceiveFuture<'a, B: WriteBuffer + 'static> { + uarte: &'a Uarte, + transfer: Option<&'a Transfer, Channel4, USART1, PeripheralToMemory, B>>, + buf: B, +} + +impl<'a, B> Drop for ReceiveFuture<'a, B> +where + B: WriteBuffer + 'static, +{ + fn drop(self: &mut Self) { + drop(self.transfer); + } +} + +impl<'a, B> Future for ReceiveFuture<'a, B> +where + B: WriteBuffer + 'static, +{ + type Output = B; + + fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if !self.transfer.is_none() && self.transfer.unwrap().is_done() { + Poll::Ready(self.buf.take()); + } else { + self.transfer = Some(&mut Transfer::init( + StreamsTuple::new(self.uarte.dma).2, + self.uarte.usart, + self.buf, + None, + DmaConfig::default() + .transfer_complete_interrupt(true) + .half_transfer_interrupt(true) + .memory_increment(true) + .double_buffer(false), + )); + + waker_interrupt!(DMA2_STREAM2, cx.waker().clone()); + Poll::Pending + } + } +} diff --git a/embassy-stm32f4/src/uarte.rs b/embassy-stm32f4/src/uarte.rs deleted file mode 100644 index 273c384cb..000000000 --- a/embassy-stm32f4/src/uarte.rs +++ /dev/null @@ -1,233 +0,0 @@ -//! Async low power UARTE. -//! -//! The peripheral is autmatically enabled and disabled as required to save power. -//! Lowest power consumption can only be guaranteed if the send receive futures -//! are dropped correctly (e.g. not using `mem::forget()`). - -use core::cell::UnsafeCell; -use core::cmp::min; -use core::future::Future; -use core::marker::PhantomPinned; -use core::ops::Deref; -use core::pin::Pin; -use core::ptr; -use core::sync::atomic::{compiler_fence, Ordering}; -use core::task::{Context, Poll}; -use cortex_m::singleton; - -use embassy::util::Signal; -use embedded_dma::{StaticReadBuffer, StaticWriteBuffer, WriteBuffer}; - -use crate::fmt::assert; -use crate::hal::dma::config::DmaConfig; -use crate::hal::dma::{ - Channel4, Channel7, MemoryToPeripheral, PeripheralToMemory, Stream2, Stream7, StreamsTuple, - Transfer, -}; -use crate::hal::gpio::gpioa::{PA10, PA9}; -use crate::hal::gpio::{Alternate, AF10, AF7, AF9}; -use crate::hal::gpio::{Floating, Input, Output, PushPull}; -use crate::hal::pac; -use crate::hal::prelude::*; -use crate::hal::rcc::Clocks; -use crate::hal::serial::config::{ - Config as SerialConfig, DmaConfig as SerialDmaConfig, Parity, StopBits, WordLength, -}; -use crate::hal::serial::Serial; -use crate::hal::time::Bps; - -use crate::interrupt; - -use crate::pac::Interrupt; -use crate::pac::{DMA2, USART1}; - -use embedded_hal::digital::v2::OutputPin; - -// Re-export SVD variants to allow user to directly set values. -// pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; - -/// Interface to the UARTE peripheral -pub struct Uarte { - instance: Serial>, PA10>)>, - usart: USART1, - dma: DMA2, -} - -struct State { - tx_done: Signal<()>, - rx_done: Signal, -} - -static STATE: State = State { - tx_done: Signal::new(), - rx_done: Signal::new(), -}; - -pub struct Pins { - pub rxd: PA10>, - pub txd: PA9>, - pub dma: DMA2, - pub usart: USART1, -} - -impl Uarte { - pub fn new(mut pins: Pins, parity: Parity, baudrate: Bps, clocks: Clocks) -> Self { - // // Enable interrupts - // uarte.events_endtx.reset(); - // uarte.events_endrx.reset(); - // uarte - // .intenset - // .write(|w| w.endtx().set().txstopped().set().endrx().set().rxto().set()); - // // TODO: Set interrupt priority? - // interrupt::unpend(interrupt::UARTE0_UART0); - // interrupt::enable(interrupt::UARTE0_UART0); - - // Serial>, PA10>)> - let mut serial = Serial::usart1( - pins.usart, - (pins.txd, pins.rxd), - SerialConfig { - baudrate: baudrate, - wordlength: WordLength::DataBits8, - parity: Parity::ParityNone, - stopbits: StopBits::STOP1, - dma: SerialDmaConfig::TxRx, - }, - clocks, - ) - .unwrap(); - - // let is_set = dma.hifcr.read().tcif7.bit_is_set(); - - Uarte { - instance: serial, - dma: pins.dma, - usart: pins.usart, - } - } - - /// Sends serial data. - /// - /// `tx_buffer` is marked as static as per `embedded-dma` requirements. - /// It it safe to use a buffer with a non static lifetime if memory is not - /// reused until the future has finished. - pub fn send<'a, B>(&'a mut self, tx_buffer: B) -> SendFuture<'a, B> - where - B: WriteBuffer + 'static, - { - SendFuture { - uarte: self, - buf: tx_buffer, - transfer: None, - } - } - - /// Receives serial data. - /// - /// The future is pending until the buffer is completely filled. - /// A common pattern is to use [`stop()`](ReceiveFuture::stop) to cancel - /// unfinished transfers after a timeout to prevent lockup when no more data - /// is incoming. - /// - /// `rx_buffer` is marked as static as per `embedded-dma` requirements. - /// It it safe to use a buffer with a non static lifetime if memory is not - /// reused until the future has finished. - pub fn receive<'a, B>(&'a mut self, rx_buffer: B) -> ReceiveFuture<'a, B> - where - B: WriteBuffer + 'static, - { - ReceiveFuture { - uarte: self, - buf: rx_buffer, - transfer: None, - } - } -} - -/// Future for the [`LowPowerUarte::send()`] method. -pub struct SendFuture<'a, B: WriteBuffer + 'static> { - uarte: &'a Uarte, - transfer: Option<&'a Transfer, Channel4, USART1, MemoryToPeripheral, B>>, - buf: B, -} - -impl<'a, B> Drop for SendFuture<'a, B> -where - B: WriteBuffer + 'static, -{ - fn drop(self: &mut Self) { - drop(self.transfer); - } -} - -impl<'a, B> Future for SendFuture<'a, B> -where - B: WriteBuffer + 'static, -{ - type Output = (); - - fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { - if !self.transfer.is_none() && self.transfer.unwrap().is_done() { - Poll::Ready(()) - } else { - self.transfer = Some(&mut Transfer::init( - StreamsTuple::new(self.uarte.dma).7, - self.uarte.usart, - self.buf, - // Some(second_buffer), - None, - DmaConfig::default() - .transfer_complete_interrupt(true) - .memory_increment(true) - .double_buffer(false), - )); - - waker_interrupt!(DMA2_STREAM7, cx.waker().clone()); - Poll::Pending - } - } -} - -/// Future for the [`Uarte::receive()`] method. -pub struct ReceiveFuture<'a, B: WriteBuffer + 'static> { - uarte: &'a Uarte, - transfer: Option<&'a Transfer, Channel4, USART1, PeripheralToMemory, B>>, - buf: B, -} - -impl<'a, B> Drop for ReceiveFuture<'a, B> -where - B: WriteBuffer + 'static, -{ - fn drop(self: &mut Self) { - drop(self.transfer); - } -} - -impl<'a, B> Future for ReceiveFuture<'a, B> -where - B: WriteBuffer + 'static, -{ - type Output = B; - - fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - if !self.transfer.is_none() && self.transfer.unwrap().is_done() { - Poll::Ready(self.buf.take()); - } else { - self.transfer = Some(&mut Transfer::init( - StreamsTuple::new(self.uarte.dma).2, - self.uarte.usart, - self.buf, - None, - DmaConfig::default() - .transfer_complete_interrupt(true) - .half_transfer_interrupt(true) - .memory_increment(true) - .double_buffer(false), - )); - - waker_interrupt!(DMA2_STREAM2, cx.waker().clone()); - Poll::Pending - } - } -} diff --git a/examples-stm32f4/Cargo.toml b/examples-stm32f4/Cargo.toml new file mode 100644 index 000000000..0fdb21b4e --- /dev/null +++ b/examples-stm32f4/Cargo.toml @@ -0,0 +1,38 @@ +[package] +authors = ["Dario Nieuwenhuis "] +edition = "2018" +name = "embassy-examples-stm32f4" +version = "0.1.0" + +[features] +default = [ + "defmt-default", +] +defmt-default = [] +defmt-trace = [] +defmt-debug = [] +defmt-info = [] +defmt-warn = [] +defmt-error = [] + + +[dependencies] +embassy = { version = "0.1.0", path = "../embassy", features = ["defmt", "defmt-trace"] } +# embassy-stm32f4 = { version = "*", path = "../embassy-stm32f4", features = ["stm32f405"] } + +defmt = "0.1.3" +defmt-rtt = "0.1.0" + +cortex-m = { version = "0.6.3" } +cortex-m-rt = "0.6.13" +embedded-hal = { version = "0.2.4" } +panic-probe = "0.1.0" +stm32f4xx-hal = { version = "0.8.3", features = ["rt", "stm32f405"], git = "https://github.com/xoviat/stm32f4xx-hal.git", branch = "dma-is-done"} +futures = { version = "0.3.8", default-features = false, features = ["async-await"] } +cortex-m-rtic = "0.5" # { git = "https://github.com/rtic-rs/cortex-m-rtic", branch = "master"} +rtt-target = { version = "0.3", features = ["cortex-m"] } + + +[[bin]] +name = "dma_adc" +path = "src/dma_adc.rs" \ No newline at end of file diff --git a/examples-stm32f4/src/dma_adc.rs b/examples-stm32f4/src/dma_adc.rs new file mode 100644 index 000000000..110bda306 --- /dev/null +++ b/examples-stm32f4/src/dma_adc.rs @@ -0,0 +1,125 @@ +#![no_std] +#![no_main] +#![feature(lang_items)] + +use core::{ + panic::PanicInfo, + sync::atomic::{compiler_fence, Ordering}, +}; + +use cortex_m::singleton; +use rtic::app; +// use rtt_target::{rprintln, rtt_init_print}; +use stm32f4xx_hal::{ + dma::{config::DmaConfig, Channel0, PeripheralToMemory, Stream0, StreamsTuple, Transfer}, + pac::{ADC1, DMA2, RCC}, + prelude::*, +}; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +type DmaTransfer = + Transfer, Channel0, ADC1, PeripheralToMemory, &'static mut [u16; 128]>; + +#[app(device = stm32f4xx_hal::pac, peripherals = true)] +const APP: () = { + struct Resources { + transfer: DmaTransfer, + triple_buffer: Option<&'static mut [u16; 128]>, + } + + #[init] + fn init(cx: init::Context) -> init::LateResources { + let rcc = cx.device.RCC.constrain(); + + // rtt_init_print!(); + // rprintln!("Init"); + + let _clocks = rcc + .cfgr + .sysclk(84.mhz()) + .pclk2(28.mhz()) + .pclk1(28.mhz()) + .freeze(); + + let gpioa = cx.device.GPIOA.split(); + let _pa0 = gpioa.pa0.into_analog(); + + let stream_0 = StreamsTuple::new(cx.device.DMA2).0; + let config = DmaConfig::default() + .transfer_complete_interrupt(true) + .memory_increment(true) + .double_buffer(true); + + let rcc = unsafe { &*RCC::ptr() }; + rcc.apb2enr.modify(|_, w| w.adc1en().enabled()); + rcc.apb2rstr.modify(|_, w| w.adcrst().set_bit()); + rcc.apb2rstr.modify(|_, w| w.adcrst().clear_bit()); + let adc = cx.device.ADC1; + adc.cr2.modify(|_, w| { + w.dma() + .enabled() + .cont() + .continuous() + .dds() + .continuous() + .adon() + .enabled() + }); + + let first_buffer = singleton!(: [u16; 128] = [0; 128]).unwrap(); + let second_buffer = singleton!(: [u16; 128] = [0; 128]).unwrap(); + let triple_buffer = Some(singleton!(: [u16; 128] = [0; 128]).unwrap()); + + let transfer = Transfer::init(stream_0, adc, first_buffer, Some(second_buffer), config); + + // rprintln!("Finished init"); + init::LateResources { + transfer, + triple_buffer, + } + } + + #[idle(resources = [transfer])] + fn idle(mut cx: idle::Context) -> ! { + cx.resources.transfer.lock(|shared| { + shared.start(|adc| { + adc.cr2.modify(|_, w| w.swstart().start()); + }); + }); + // rprintln!("DMA started"); + loop { + compiler_fence(Ordering::SeqCst); + } + } + + #[task(binds = DMA2_STREAM0, priority = 2, resources = [transfer, triple_buffer])] + fn dma(cx: dma::Context) { + static mut COUNT: usize = 0; + + let triple = cx.resources.triple_buffer.take().unwrap(); + let buf = cx + .resources + .transfer + .next_transfer(triple) + .map_err(|_| {}) + .unwrap() + .0; + if *COUNT % (1 << 14) == 0 { + // rprintln!("Buf: {:?}", &buf[0..10]); + } + *COUNT += 1; + *cx.resources.triple_buffer = Some(buf); + } +}; + +#[inline(never)] +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + cortex_m::interrupt::disable(); + // rprintln!("{}", info); + loop { + compiler_fence(Ordering::SeqCst); + } +} -- cgit