From 323476136cb45ea04dbbd88ed6f7f0574f749364 Mon Sep 17 00:00:00 2001 From: Per Lindgren <per.lindgren@ltu.se> Date: Sun, 11 Feb 2018 19:51:22 +0100 Subject: [PATCH] receive example works still experimental --- .vscode/launch.json | 168 ++++++++++++++++++++------------------ examples/serial-dma-rx.rs | 40 +++++++++ examples/serial-dma-tx.rs | 18 ++-- src/dma.rs | 76 +++++++++++++++-- src/serial.rs | 52 ++++++++++-- 5 files changed, 251 insertions(+), 103 deletions(-) create mode 100644 examples/serial-dma-rx.rs diff --git a/.vscode/launch.json b/.vscode/launch.json index 785c352..9cdce3d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,84 +4,84 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - { - "type": "gdb", - "request": "attach", - "name": "hello_debug", - "gdbpath": "/usr/bin/arm-none-eabi-gdb", - "executable": "./target/thumbv7em-none-eabihf/debug/examples/hello_debug", - "target": ":3333", - "remote": true, - "autorun": [ - "load", - ], - "cwd": "${workspaceRoot}" - }, - { - "type": "gdb", - "request": "attach", - "name": "serial-echo", - "gdbpath": "/usr/bin/arm-none-eabi-gdb", - "executable": "./target/thumbv7em-none-eabihf/debug/examples/serial-echo", - "target": ":3333", - "remote": true, - "autorun": [ - "load" - ], - "cwd": "${workspaceRoot}" - }, - { - "type": "gdb", - "request": "attach", - "name": "rtfm-blinky-systic", - "gdbpath": "/usr/bin/arm-none-eabi-gdb", - "executable": "./target/thumbv7em-none-eabihf/debug/examples/rtfm-blinky-systic", - "target": ":3333", - "remote": true, - "autorun": [ - "load" - ], - "cwd": "${workspaceRoot}" - }, - { - "type": "gdb", - "request": "attach", - "name": "rtfm-blinky-tim2", - "gdbpath": "/usr/bin/arm-none-eabi-gdb", - "executable": "./target/thumbv7em-none-eabihf/debug/examples/rtfm-blinky-tim2", - "target": ":3333", - "remote": true, - "autorun": [ - "load" - ], - "cwd": "${workspaceRoot}" - }, - { - "type": "gdb", - "request": "attach", - "name": "rtfm-serial-echo", - "gdbpath": "/usr/bin/arm-none-eabi-gdb", - "executable": "./target/thumbv7em-none-eabihf/debug/examples/rtfm-serial-echo", - "target": ":3333", - "remote": true, - "autorun": [ - "load" - ], - "cwd": "${workspaceRoot}" - }, - { - "type": "gdb", - "request": "attach", - "name": "serial-dma-tx", - "gdbpath": "/usr/bin/arm-none-eabi-gdb", - "executable": "./target/thumbv7em-none-eabihf/debug/examples/serial-dma-tx", - "target": ":3333", - "remote": true, - "autorun": [ - "load" - ], - "cwd": "${workspaceRoot}" - }, + // { + // "type": "gdb", + // "request": "attach", + // "name": "hello_debug", + // "gdbpath": "/usr/bin/arm-none-eabi-gdb", + // "executable": "./target/thumbv7em-none-eabihf/debug/examples/hello_debug", + // "target": ":3333", + // "remote": true, + // "autorun": [ + // "load", + // ], + // "cwd": "${workspaceRoot}" + // }, + // { + // "type": "gdb", + // "request": "attach", + // "name": "serial-echo", + // "gdbpath": "/usr/bin/arm-none-eabi-gdb", + // "executable": "./target/thumbv7em-none-eabihf/debug/examples/serial-echo", + // "target": ":3333", + // "remote": true, + // "autorun": [ + // "load" + // ], + // "cwd": "${workspaceRoot}" + // }, + // { + // "type": "gdb", + // "request": "attach", + // "name": "rtfm-blinky-systic", + // "gdbpath": "/usr/bin/arm-none-eabi-gdb", + // "executable": "./target/thumbv7em-none-eabihf/debug/examples/rtfm-blinky-systic", + // "target": ":3333", + // "remote": true, + // "autorun": [ + // "load" + // ], + // "cwd": "${workspaceRoot}" + // }, + // { + // "type": "gdb", + // "request": "attach", + // "name": "rtfm-blinky-tim2", + // "gdbpath": "/usr/bin/arm-none-eabi-gdb", + // "executable": "./target/thumbv7em-none-eabihf/debug/examples/rtfm-blinky-tim2", + // "target": ":3333", + // "remote": true, + // "autorun": [ + // "load" + // ], + // "cwd": "${workspaceRoot}" + // }, + // { + // "type": "gdb", + // "request": "attach", + // "name": "rtfm-serial-echo", + // "gdbpath": "/usr/bin/arm-none-eabi-gdb", + // "executable": "./target/thumbv7em-none-eabihf/debug/examples/rtfm-serial-echo", + // "target": ":3333", + // "remote": true, + // "autorun": [ + // "load" + // ], + // "cwd": "${workspaceRoot}" + // }, + // { + // "type": "gdb", + // "request": "attach", + // "name": "serial-dma-tx", + // "gdbpath": "/usr/bin/arm-none-eabi-gdb", + // "executable": "./target/thumbv7em-none-eabihf/debug/examples/serial-dma-tx", + // "target": ":3333", + // "remote": true, + // "autorun": [ + // "load" + // ], + // "cwd": "${workspaceRoot}" + // }, { "type": "cortex-debug", "request": "launch", @@ -117,6 +117,18 @@ "target/stm32f4x.cfg" ], "cwd": "${workspaceRoot}" + }, + { + "type": "cortex-debug", + "request": "launch", + "servertype": "openocd", + "name": "c serial-dma-rx", + "executable": "./target/thumbv7em-none-eabihf/debug/examples/serial-dma-rx", + "configFiles": [ + "interface/stlink.cfg", + "target/stm32f4x.cfg" + ], + "cwd": "${workspaceRoot}" } ] } \ No newline at end of file diff --git a/examples/serial-dma-rx.rs b/examples/serial-dma-rx.rs new file mode 100644 index 0000000..90cd775 --- /dev/null +++ b/examples/serial-dma-rx.rs @@ -0,0 +1,40 @@ +//! Serial interface echo server +//! +//! In this example every received byte will be sent back to the sender. You can test this example +//! with serial terminal emulator like `minicom`. +#![deny(unsafe_code)] +//#![deny(warnings)] +#![no_std] + +#[macro_use(singleton)] +extern crate cortex_m; +extern crate stm32f4x_hal as f4; + +use cortex_m::asm; +use f4::prelude::*; +use f4::serial::Serial; +use f4::stm32f4x; + +fn main() { + let p = stm32f4x::Peripherals::take().unwrap(); + + let mut flash = p.FLASH.constrain(); + let mut rcc = p.RCC.constrain(); + let mut gpioa = p.GPIOA.split(&mut rcc.ahb1); + let streams = p.DMA1.split(&mut rcc.ahb1); + let rx_stream = streams.0.into_channel4(); // S5<C4> + + let clocks = rcc.cfgr.freeze(&mut flash.acr); + + let tx = gpioa.pa2.into_af7(&mut gpioa.moder, &mut gpioa.afrl); + let rx = gpioa.pa3.into_af7(&mut gpioa.moder, &mut gpioa.afrl); + + let serial = Serial::usart2(p.USART2, (tx, rx), 115_200.bps(), clocks, &mut rcc.apb1); + let (mut tx, mut rx) = serial.split(); + + let buf = singleton!(: [u8; 8] = [0; 8]).unwrap(); + + let (_buf, _c, _rx) = rx.read_exact(rx_stream, buf).wait(); + + asm::bkpt(); +} diff --git a/examples/serial-dma-tx.rs b/examples/serial-dma-tx.rs index 40cc1df..ec64cdd 100644 --- a/examples/serial-dma-tx.rs +++ b/examples/serial-dma-tx.rs @@ -1,13 +1,13 @@ -//! Serial interface echo server -//! -//! In this example every received byte will be sent back to the sender. You can test this example -//! with serial terminal emulator like `minicom`. +//! Serial interface with dma + #![deny(unsafe_code)] -//#![deny(warnings)] +#![deny(warnings)] #![no_std] +extern crate cortex_m; extern crate stm32f4x_hal as f4; +use cortex_m::asm; use f4::prelude::*; use f4::serial::Serial; use f4::stm32f4x; @@ -27,17 +27,17 @@ fn main() { let rx = gpioa.pa3.into_af7(&mut gpioa.moder, &mut gpioa.afrl); let serial = Serial::usart2(p.USART2, (tx, rx), 115_200.bps(), clocks, &mut rcc.apb1); - let (mut tx, mut rx) = serial.split(); + let (tx, _) = serial.split(); let (_, c, tx) = tx.write_all(tx_stream, b"The quick brown fox").wait(); - // asm::bkpt(); + asm::bkpt(); let (_, c, tx) = tx.write_all(c, b" jumps").wait(); - // asm::bkpt(); + asm::bkpt(); tx.write_all(c, b" over the lazy dog.").wait(); - // asm::bkpt(); + asm::bkpt(); } diff --git a/src/dma.rs b/src/dma.rs index f176508..ce9e0cd 100644 --- a/src/dma.rs +++ b/src/dma.rs @@ -126,7 +126,9 @@ pub unsafe trait UsartTxStream<USART> { fn start_transfer(&mut self, ndtr: u16, par: u32, m0: u32); } -pub unsafe trait UsartRxStream<USART> {} +pub unsafe trait UsartRxStream<USART> { + fn start_transfer(&mut self, ndtr: u16, par: u32, m0: u32); +} pub mod dma1 { use core::sync::atomic::{self, Ordering}; @@ -155,7 +157,6 @@ pub mod dma1 { pub struct S7<CHANNEL> { _channel: PhantomData<CHANNEL>, } - //pub struct S7<CHANNEL> {} // Channels === Alternate function pub struct C0; @@ -196,11 +197,15 @@ pub mod dma1 { unsafe impl super::UsartTxStream<USART2> for S6<C4> { fn start_transfer(&mut self, ndtr: u16, par: u32, m0: u32) { - start_transfer_s6_c4(ndtr, par, m0); + start_transfer_s6(4, ndtr, par, m0); + } + } + + unsafe impl super::UsartRxStream<USART2> for S5<C4> { + fn start_transfer(&mut self, ndtr: u16, par: u32, m0: u32) { + start_transfer_s5(4, ndtr, par, m0); } } - unsafe impl super::UsartRxStream<USART2> for S5<C4> {} - unsafe impl super::UsartRxStream<USART2> for S7<C6> {} pub struct Streams(pub S5<C0>, pub S6<C0>, pub S7<C0>); @@ -226,7 +231,8 @@ pub mod dma1 { use cortex_m::asm; - fn start_transfer_s6_c4(ndtr: u16, par: u32, m0: u32) { + // should be generalized by macro + fn start_transfer_s6(channel: u8, ndtr: u16, par: u32, m0: u32) { let dma = unsafe { &*DMA1::ptr() }; // nr data transfers @@ -248,7 +254,7 @@ pub mod dma1 { // en: Disabled dma.s6cr.modify(|_, w| unsafe { w.chsel() - .bits(4) // channel 4 + .bits(channel) // channel 4 .pl() .bits(0b01) // medium priority .msize() @@ -264,11 +270,44 @@ pub mod dma1 { .dir() .bits(1) // memory -> peripheral .en() - .set_bit() // setup + .set_bit() // enable }); // dma.s6cr.modify(|_, w| w.en().set_bit()); } + fn start_transfer_s5(channel: u8, ndtr: u16, par: u32, m0: u32) { + let dma = unsafe { &*DMA1::ptr() }; + + // nr data transfers + dma.s5ndtr.write(|w| unsafe { w.ndt().bits(ndtr) }); + // peripheral address + dma.s5par.write(|w| unsafe { w.bits(par) }); + // memory address 0 + dma.s5m0ar.write(|w| unsafe { w.bits(m0) }); + // RX DMA transfer + + dma.s5cr.modify(|_, w| unsafe { + w.chsel() + .bits(channel) // channel 4 + .pl() + .bits(0b01) // medium priority + .msize() + .bits(0b00) // memory 8 bits + .psize() + .bits(0b00) // peripheral 8 bits + .minc() // memory increment + .set_bit() + .circ() + .clear_bit() // not circular + .pinc() + .clear_bit() // no peripheral increment + .dir() + .bits(0) // peripheral -> memory + .en() + .set_bit() // setup + }); + } + impl DmaExt for DMA1 { type Streams = Streams; @@ -317,4 +356,25 @@ pub mod dma1 { (self.buffer, self.stream, self.payload) } } + + impl<BUFFER, PAYLOAD, MODE> Transfer<MODE, BUFFER, S5<C4>, PAYLOAD> { + pub fn wait(mut self) -> (BUFFER, S5<C4>, PAYLOAD) { + // XXX should we check for transfer errors here? + // The manual says "A DMA transfer error can be generated by reading + // from or writing to a reserved address space". I think it's impossible + // to get to that state with our type safe API and *safe* Rust. + let dma = unsafe { &*DMA1::ptr() }; + + while dma.hisr.read().tcif5().bit_is_clear() {} + dma.hifcr.write(|w| w.ctcif5().set_bit()); + dma.s5cr.modify(|_, w| w.en().clear_bit()); + + // TODO can we weaken this compiler barrier? + // NOTE(compiler_fence) operations on `buffer` should not be reordered + // before the previous statement, which marks the DMA transfer as done + atomic::compiler_fence(Ordering::SeqCst); + + (self.buffer, self.stream, self.payload) + } + } } diff --git a/src/serial.rs b/src/serial.rs index 5a16c3d..a059023 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -13,7 +13,7 @@ use cast::u16; use hal::serial; use nb; use stm32f4x::{USART1, USART2, USART6}; -use dma::{Static, Transfer, UsartTxStream, R}; +use dma::{Static, Transfer, UsartRxStream, UsartTxStream, R, W}; // usart2 use gpio::gpioa::{PA2, PA3}; @@ -101,7 +101,13 @@ pub struct Tx<USART> { macro_rules! hal { ($( - $USARTX:ident: ($usartX:ident, $APB:ident, $usartXen:ident, $usartXrst:ident, $pclkX:ident), + $USARTX:ident: ( + $usartX:ident, + $APB:ident, + $usartXen:ident, + $usartXrst:ident, + $pclkX:ident + ), )+) => { $( impl<TX, RX> Serial<$USARTX, (TX, RX)> { @@ -255,6 +261,7 @@ macro_rules! hal { } } } + impl Tx<$USARTX> { pub fn write_all<A, B, S>( self, // should be mutable? @@ -267,24 +274,53 @@ macro_rules! hal { S: UsartTxStream<$USARTX> { { - let buf :&[u8] = buffer.borrow(); + let buffer :&[u8] = buffer.borrow(); + + // TODO can we weaken this compiler barrier? + // NOTE(compiler_fence) operations on `buffer` should not be reordered after + // the next statement, which starts the DMA transfer + atomic::compiler_fence(Ordering::SeqCst); // ntdr, par, m0 tx_stream.start_transfer( - u16(buf.len()).unwrap(), + u16(buffer.len()).unwrap(), unsafe { &(*$USARTX::ptr()).dr as *const _ as usize as u32 }, - buf.as_ptr() as u32 + buffer.as_ptr() as u32 ); + } + + Transfer::r(buffer, tx_stream, self) + } + } + + impl Rx<$USARTX> { + pub fn read_exact<A, S>( + self, + mut rx_stream: S, + buffer: &'static mut A, + ) -> Transfer<W, &'static mut A, S, Self> + where + A: Unsize<[u8]>, + S: UsartRxStream<$USARTX> + { + { + let buffer: &[u8] = buffer; // TODO can we weaken this compiler barrier? // NOTE(compiler_fence) operations on `buffer` should not be reordered after // the next statement, which starts the DMA transfer - atomic::compiler_fence(Ordering::SeqCst); + atomic::compiler_fence(Ordering::SeqCst); + + rx_stream.start_transfer( + u16(buffer.len()).unwrap(), + unsafe { &(*$USARTX::ptr()).dr as *const _ as usize as u32 }, + buffer.as_ptr() as u32 + ); } - Transfer::r(buffer, tx_stream, self) + + Transfer::w(buffer, rx_stream, self) } } - )+ } } -- GitLab