diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ddf62ca..435433f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Breaking changes + +- Replace DMA buffer types with `Deref` ans `Unpin` + ## [v0.6.1] - 2020-06-25 ### Added diff --git a/Cargo.toml b/Cargo.toml index c71c8a2e..ba03ac53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,10 @@ version = "0.2.2" version = "0.2.3" features = ["unproven"] +[dependencies.stable_deref_trait] +default-features = false +version = "1.1" + [dependencies.stm32-usbd] version = "0.5.0" features = ["ram_access_1x16"] diff --git a/examples/adc-dma-circ.rs b/examples/adc-dma-circ.rs index dc3a3a4a..d2397831 100644 --- a/examples/adc-dma-circ.rs +++ b/examples/adc-dma-circ.rs @@ -38,7 +38,7 @@ fn main() -> ! { let adc_dma = adc1.with_dma(adc_ch0, dma_ch1); let buf = singleton!(: [[u16; 8]; 2] = [[0; 8]; 2]).unwrap(); - let mut circ_buffer = adc_dma.circ_read(buf); + let mut circ_buffer = adc_dma.circ_double_read(buf); while circ_buffer.readable_half().unwrap() != Half::First {} diff --git a/examples/serial-dma-circ-len.rs b/examples/serial-dma-circ-len.rs new file mode 100644 index 00000000..508cf0b5 --- /dev/null +++ b/examples/serial-dma-circ-len.rs @@ -0,0 +1,99 @@ +//! Serial interface circular DMA RX transfer test + +#![deny(unsafe_code)] +#![no_std] +#![no_main] + +use panic_semihosting as _; + +use cortex_m::singleton; +use cortex_m_semihosting::{hprint, hprintln}; + +use cortex_m_rt::entry; +use stm32f1xx_hal::{ + dma::CircReadDmaLen, + pac, + prelude::*, + serial::{Config, Serial}, +}; + +#[entry] +fn main() -> ! { + let p = pac::Peripherals::take().unwrap(); + + let mut flash = p.FLASH.constrain(); + let mut rcc = p.RCC.constrain(); + + let clocks = rcc.cfgr.freeze(&mut flash.acr); + + let mut afio = p.AFIO.constrain(&mut rcc.apb2); + let channels = p.DMA1.split(&mut rcc.ahb); + + let mut gpioa = p.GPIOA.split(&mut rcc.apb2); + // let mut gpiob = p.GPIOB.split(&mut rcc.apb2); + + // USART1 + let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh); + let rx = gpioa.pa10; + + // USART1 + // let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); + // let rx = gpiob.pb7; + + // USART2 + // let tx = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl); + // let rx = gpioa.pa3; + + // USART3 + // let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh); + // let rx = gpiob.pb11; + + let serial = Serial::usart1( + p.USART1, + (tx, rx), + &mut afio.mapr, + Config::default().baudrate(9_600.bps()), + clocks, + &mut rcc.apb2, + ); + + hprintln!("waiting for 5 bytes").unwrap(); + + let rx = serial.split().1.with_dma(channels.5); + // increase to reasonable size (e.g. 64, 128 etc.) for actual applications + let buf = singleton!(: [u8; 8] = [0; 8]).unwrap(); + + let mut circ_buffer = rx.circ_read_len(buf); + + // wait until we have 5 bytes + while circ_buffer.len() < 5 {} + + let mut dat = [0 as u8; 4]; + assert!(circ_buffer.read(&mut dat[..]) == 4); + + hprintln!("[{}, {}, {}, {}]", dat[0], dat[1], dat[2], dat[3]).unwrap(); + + // try to read again, now only one byte is returned + hprintln!("read {}", circ_buffer.read(&mut dat)).unwrap(); + + hprintln!("[{}]", dat[0]).unwrap(); + + // wait for the buffer to have 4 bytes again + while circ_buffer.len() < 4 {} + + // all four bytes should be read in one go + assert!(circ_buffer.read(&mut dat) == 4); + + hprintln!("[{}, {}, {}, {}]", dat[0], dat[1], dat[2], dat[3]).unwrap(); + + loop { + let read = circ_buffer.read(&mut dat); + + if read > 0 { + for c in &dat[..read] { + hprint!("{}", *c as char).unwrap(); + } + hprintln!("").unwrap(); + } + } +} diff --git a/examples/serial-dma-circ.rs b/examples/serial-dma-circ.rs index 956bb970..f474c74f 100644 --- a/examples/serial-dma-circ.rs +++ b/examples/serial-dma-circ.rs @@ -59,7 +59,7 @@ fn main() -> ! { let rx = serial.split().1.with_dma(channels.5); let buf = singleton!(: [[u8; 8]; 2] = [[0; 8]; 2]).unwrap(); - let mut circ_buffer = rx.circ_read(buf); + let mut circ_buffer = rx.circ_double_read(buf); while circ_buffer.readable_half().unwrap() != Half::First {} diff --git a/src/adc.rs b/src/adc.rs index f6cd520b..c265d1aa 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use embedded_hal::adc::{Channel, OneShot}; -use crate::dma::{dma1::C1, CircBuffer, Receive, RxDma, Transfer, TransferPayload, W}; +use crate::dma::{dma1::C1, CircDoubleBuffer, Receive, RxDma, Transfer, TransferPayload, W}; use crate::gpio::Analog; use crate::gpio::{gpioa, gpiob, gpioc}; use crate::rcc::{Clocks, Enable, Reset, APB2}; @@ -667,12 +667,12 @@ where } } -impl crate::dma::CircReadDma for AdcDma +impl crate::dma::CircDoubleReadDma for AdcDma where Self: TransferPayload, B: as_slice::AsMutSlice, { - fn circ_read(mut self, buffer: &'static mut [B; 2]) -> CircBuffer { + fn circ_double_read(mut self, buffer: &'static mut [B; 2]) -> CircDoubleBuffer { { let buffer = buffer[0].as_mut_slice(); self.channel @@ -701,7 +701,7 @@ where self.start(); - CircBuffer::new(buffer, self) + CircDoubleBuffer::new(buffer, self) } } diff --git a/src/dma.rs b/src/dma.rs index 9628587d..95d89434 100644 --- a/src/dma.rs +++ b/src/dma.rs @@ -4,6 +4,8 @@ use core::marker::PhantomData; use core::ops; +use stable_deref_trait::StableDeref; + use crate::rcc::AHB; #[derive(Debug)] @@ -24,7 +26,29 @@ pub enum Half { Second, } -pub struct CircBuffer +type DataSize = crate::stm32::dma1::ch::cr::PSIZE_A; +pub type Priority = crate::stm32::dma1::ch::cr::PL_A; + +pub struct CircBuffer +where + EL: 'static, +{ + buffer: &'static mut [EL], + payload: PAYLOAD, + position: usize, +} + +impl CircBuffer { + pub(crate) fn new(buf: &'static mut [EL], payload: PAYLOAD) -> Self { + CircBuffer { + buffer: buf, + payload, + position: 0, + } + } +} + +pub struct CircDoubleBuffer where BUFFER: 'static, { @@ -33,9 +57,9 @@ where readable_half: Half, } -impl CircBuffer { +impl CircDoubleBuffer { pub(crate) fn new(buf: &'static mut [BUFFER; 2], payload: PAYLOAD) -> Self { - CircBuffer { + CircDoubleBuffer { buffer: buf, payload, readable_half: Half::Second, @@ -70,6 +94,11 @@ pub trait TransferPayload { fn stop(&mut self); } +pub trait Transferable { + fn is_done(&self) -> bool; + fn wait(self) -> (BUFFER, DMA); +} + pub struct Transfer { _mode: PhantomData, buffer: BUFFER, @@ -123,12 +152,29 @@ macro_rules! dma { }),)+) => { $( pub mod $dmaX { + use core::marker::Copy; + use core::mem::size_of; use core::sync::atomic::{self, Ordering}; use core::ptr; + use core::cmp::min; use crate::pac::{$DMAX, dma1}; - use crate::dma::{CircBuffer, DmaExt, Error, Event, Half, Transfer, W, RxDma, TxDma, TransferPayload}; + use crate::dma::{ + CircBuffer, + CircDoubleBuffer, + DataSize, + DmaExt, + Error, + Event, + Half, + Priority, + Transfer, + W, + RxDma, + TxDma, + TransferPayload, + Transferable}; use crate::rcc::{AHB, Enable}; pub struct Channels((), $(pub $CX),+); @@ -218,7 +264,86 @@ macro_rules! dma { } } - impl CircBuffer> + impl CircBuffer> + where + RxDma: TransferPayload, + EL: Copy, + { + pub unsafe fn setup(&mut self, peripheral_address : u32, priority: Priority) { + self.payload.channel.set_peripheral_address(peripheral_address, false); + self.payload.channel.set_memory_address(self.buffer.as_ptr() as u32, true); + self.payload.channel.set_transfer_length(self.buffer.len()); + + atomic::compiler_fence(Ordering::Release); + + let bits = match size_of::() { + 1 => DataSize::BITS8, + 2 => DataSize::BITS16, + 4 => DataSize::BITS32, + _ => panic!("Unsupported element size.") + }; + + self.payload.channel.ch().cr.modify(|_, w| { + w.mem2mem().clear_bit() + .pl() .variant(priority) + .circ() .set_bit() + .dir() .clear_bit() + .msize() .variant(bits) + .psize() .variant(bits) + }); + + self.payload.start(); + } + + + /// Return the number of elements available to read + pub fn len(&mut self) -> usize { + let blen = self.buffer.len(); + let ndtr = self.payload.channel.get_ndtr() as usize; + let pos_at = self.position; + + // the position the DMA would write to next + let pos_to = blen - ndtr; + + if pos_at > pos_to { + // the buffer wraps around + blen + pos_to - pos_at + } else { + // the buffer does not wrap around + pos_to - pos_at + } + } + + pub fn read(&mut self, dat: &mut [EL]) -> usize { + let blen = self.buffer.len(); + let len = self.len(); + let pos = self.position; + let read = min(dat.len(), len); + + if pos + read <= blen { + // the read operation does not wrap around the + // circular buffer, perform a single read + dat[0..read].copy_from_slice(&self.buffer[pos..pos + read]); + self.position = pos + read; + if self.position >= blen { + self.position = 0; + } + } else { + // the read operation wraps around the circular buffer, + let left = blen - pos; + // copy until the end of the buffer + dat[0..left].copy_from_slice(&self.buffer[pos..blen]); + // copy from the beginning of the buffer until the amount to read + dat[left..read].copy_from_slice(&self.buffer[0..read - left]); + self.position = read - left; + } + + // return the number of bytes read + read + } + } + + impl CircDoubleBuffer> where RxDma: TransferPayload, { @@ -294,15 +419,15 @@ macro_rules! dma { } } - impl Transfer> + impl Transferable> for Transfer> where RxDma: TransferPayload, { - pub fn is_done(&self) -> bool { + fn is_done(&self) -> bool { !self.payload.channel.in_progress() } - pub fn wait(mut self) -> (BUFFER, RxDma) { + fn wait(mut self) -> (BUFFER, RxDma) { while !self.is_done() {} atomic::compiler_fence(Ordering::Acquire); @@ -320,15 +445,15 @@ macro_rules! dma { } } - impl Transfer> + impl Transferable> for Transfer> where TxDma: TransferPayload, { - pub fn is_done(&self) -> bool { + fn is_done(&self) -> bool { !self.payload.channel.in_progress() } - pub fn wait(mut self) -> (BUFFER, TxDma) { + fn wait(mut self) -> (BUFFER, TxDma) { while !self.is_done() {} atomic::compiler_fence(Ordering::Acquire); @@ -485,7 +610,15 @@ where B: as_slice::AsMutSlice, Self: core::marker::Sized, { - fn circ_read(self, buffer: &'static mut [B; 2]) -> CircBuffer; + fn circ_read(self, buffer: &'static mut B) -> CircBuffer; +} + +pub trait CircDoubleReadDma: Receive +where + B: as_slice::AsMutSlice, + Self: core::marker::Sized, +{ + fn circ_double_read(self, buffer: &'static mut [B; 2]) -> CircDoubleBuffer; } pub trait ReadDma: Receive @@ -496,10 +629,11 @@ where fn read(self, buffer: &'static mut B) -> Transfer; } -pub trait WriteDma: Transmit +pub trait WriteDma: Transmit where - A: as_slice::AsSlice, - B: Static, + B: core::ops::Deref + 'static, + B::Target: as_slice::AsSlice + Unpin, + B: StableDeref, Self: core::marker::Sized, { fn write(self, buffer: B) -> Transfer; diff --git a/src/serial.rs b/src/serial.rs index f8b5d2de..82826e7a 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -44,10 +44,14 @@ use core::sync::atomic::{self, Ordering}; use crate::pac::{USART1, USART2, USART3}; use core::convert::Infallible; + +use as_slice::AsSlice; +use stable_deref_trait::StableDeref; + use embedded_hal::serial::Write; use crate::afio::MAPR; -use crate::dma::{dma1, CircBuffer, RxDma, Static, Transfer, TxDma, R, W}; +use crate::dma::{dma1, CircBuffer, CircDoubleBuffer, Priority, RxDma, Transfer, TxDma, R, W}; use crate::gpio::gpioa::{PA10, PA2, PA3, PA9}; use crate::gpio::gpiob::{PB10, PB11, PB6, PB7}; use crate::gpio::gpioc::{PC10, PC11}; @@ -585,8 +589,19 @@ macro_rules! serialdma { } impl crate::dma::CircReadDma for $rxdma where B: as_slice::AsMutSlice { - fn circ_read(mut self, buffer: &'static mut [B; 2], - ) -> CircBuffer + fn circ_read(self, buffer: &'static mut B) -> CircBuffer{ + let buffer = buffer.as_mut_slice(); + let paddr = unsafe { &(*$USARTX::ptr()).dr as *const _ as u32 }; + + let mut buf = CircBuffer::new(buffer, self); + unsafe { buf.setup(paddr, Priority::MEDIUM) }; + buf + } + } + + impl crate::dma::CircDoubleReadDma for $rxdma where B: as_slice::AsMutSlice { + fn circ_double_read(mut self, buffer: &'static mut [B; 2], + ) -> CircDoubleBuffer { { let buffer = buffer[0].as_mut_slice(); @@ -608,7 +623,7 @@ macro_rules! serialdma { self.start(); - CircBuffer::new(buffer, self) + CircDoubleBuffer::new(buffer, self) } } @@ -637,12 +652,16 @@ macro_rules! serialdma { } } - impl crate::dma::WriteDma for $txdma where A: as_slice::AsSlice, B: Static { + impl crate::dma::WriteDma for $txdma + where + B: StableDeref + core::ops::Deref + 'static, + B::Target: AsSlice + Unpin, + { fn write(mut self, buffer: B ) -> Transfer { { - let buffer = buffer.borrow().as_slice(); + let buffer = (*buffer).as_slice(); self.channel.set_peripheral_address(unsafe{ &(*$USARTX::ptr()).dr as *const _ as u32 }, false); diff --git a/src/spi.rs b/src/spi.rs index b7363ac8..4231a559 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -41,7 +41,7 @@ use crate::pac::{SPI1, SPI2}; use crate::afio::MAPR; use crate::dma::dma1::{C3, C5}; -use crate::dma::{Static, Transfer, TransferPayload, Transmit, TxDma, R}; +use crate::dma::{Transfer, TransferPayload, Transmit, TxDma, R}; use crate::gpio::gpioa::{PA5, PA6, PA7}; use crate::gpio::gpiob::{PB13, PB14, PB15, PB3, PB4, PB5}; #[cfg(feature = "connectivity")] @@ -53,6 +53,7 @@ use crate::time::Hertz; use core::sync::atomic::{self, Ordering}; use as_slice::AsSlice; +use stable_deref_trait::StableDeref; /// SPI error #[derive(Debug)] @@ -450,14 +451,14 @@ macro_rules! spi_dma { } } - impl crate::dma::WriteDma for SpiTxDma<$SPIi, REMAP, PIN, $TCi> + impl crate::dma::WriteDma for SpiTxDma<$SPIi, REMAP, PIN, $TCi> where - A: AsSlice, - B: Static, + B: StableDeref + core::ops::Deref + 'static, + B::Target: as_slice::AsSlice + Unpin, { fn write(mut self, buffer: B) -> Transfer { { - let buffer = buffer.borrow().as_slice(); + let buffer = buffer.as_slice(); self.channel.set_peripheral_address( unsafe { &(*$SPIi::ptr()).dr as *const _ as u32 }, false,