embedded-dma
embedded-dma copied to clipboard
intended api for serial dma read and write
I have been working on some examples using different setup()
functions for different stm32*xx_hals
and then common application code. Some time ago I put aside the serial dma examples because the different hal approaches were too different. Now these examples are some of the few I have not got working (scoreboard at https://pdgilbert.github.io/eg_stm_hal/), and I think there has been some progress adopting embedded-dma
as a common approach.
Unfortunately, there do not seem to be examples in any of the hals that I am able to recognize as representing the new approach. Below is code that works with stm32f3xx_hal
and the setup()
part works with stm32f1xx_hal
. stm32f3xx_hal
provides buffer using methods read_exact()
and write_all()
which are used in the code below. Those methods are not provided by other hals and seem rather messy because the application code has to deal explicitly with the buffers rather than hiding them inside TxDma and RxDma objects. I think I should be using something like ReadDma and WriteDma but I am not sure.
-
Is there a method that is or might become available in all
stm32*xx_hal
s? -
Which hals have implemented current best practice?
-
Is there a good example of buffered read and write using current best practice?
I think my setup functions should be able to return buffered version of Tx and Rx. Previously I had it working that way with stm32f1xx_hal
using something like tx1.with_dma(channels.4)
and rx1.with_dma(channels.5)
but I thought the hal was not using embedded-dma
at the time and the .with_dma()
method was not available in other hals.
-
What is the object I should be looking to return and what is the method to set it up?
-
Can my setup functions use some
impl trait
to find the return value type for these objects, and what traits should I be using?
Thanks for any insight. (I'm still a newbie so details are useful.)
#![deny(unsafe_code)]
#![no_main]
#![no_std]
#[cfg(debug_assertions)]
extern crate panic_semihosting;
#[cfg(not(debug_assertions))]
extern crate panic_halt;
use cortex_m::singleton;
use cortex_m_rt::entry;
use cortex_m_semihosting::hprintln;
#[cfg(feature = "stm32f1xx")] // eg blue pill stm32f103
use stm32f1xx_hal::{prelude::*,
pac::Peripherals,
serial::{Config, Serial, StopBits, Tx, Rx},
dma::{dma1, },
device::USART1 };
#[cfg(feature = "stm32f1xx")]
fn setup() -> (Tx<USART1>, dma1::C4, Rx<USART1>, dma1::C5) {
// should be able to do something like
//fn setup() -> (impl WriteDma<USART1>, impl ReadDma<USART1> ) {
let p = Peripherals::take().unwrap();
let mut rcc = p.RCC.constrain();
let clocks = rcc.cfgr.freeze(&mut p.FLASH.constrain().acr);
let mut afio = p.AFIO.constrain(&mut rcc.apb2);
let mut gpioa = p.GPIOA.split(&mut rcc.apb2);
let txrx1 = Serial::usart1(
p.USART1,
(gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh), //tx pa9,
gpioa.pa10), //rx pa10
&mut afio.mapr,
Config::default() .baudrate(9600.bps()) .stopbits(StopBits::STOP1),
clocks,
&mut rcc.apb2,
); //.split();
let (tx1, rx1) = txrx1.split();
let dma1 = p.DMA1.split(&mut rcc.ahb);
let (tx1_ch, rx1_ch) = (dma1.4, dma1.5);
// should be able to return just (tx1, rx1)
(tx1, tx1_ch, rx1, rx1_ch)
}
#[cfg(feature = "stm32f3xx")] // eg Discovery-stm32f303
use stm32f3xx_hal::{prelude::*,
stm32::Peripherals,
serial::{Serial, Tx, Rx},
dma::dma1,
stm32::USART1
};
#[cfg(feature = "stm32f3xx")]
fn setup() -> (Tx<USART1>, dma1::C4, Rx<USART1>, dma1::C5) {
// should be able to do something like
//fn setup() -> (impl WriteDma<USART1>, impl ReadDma<USART1> ) {
let p = Peripherals::take().unwrap();
let mut rcc = p.RCC.constrain();
let clocks = rcc.cfgr.freeze(&mut p.FLASH.constrain().acr);
let mut gpioa = p.GPIOA.split(&mut rcc.ahb);
let txrx1 = Serial::usart1(
p.USART1,
(gpioa.pa9.into_af7( &mut gpioa.moder, &mut gpioa.afrh),
gpioa.pa10.into_af7(&mut gpioa.moder, &mut gpioa.afrh)),
9600.bps(),
clocks,
&mut rcc.apb2,
);
let (tx1, rx1) = txrx1.split();
let dma1 = p.DMA1.split(&mut rcc.ahb);
let (tx1_ch, rx1_ch) = (dma1.ch4, dma1.ch5);
// should be able to return just (tx1, rx1)
(tx1, tx1_ch, rx1, rx1_ch)
}
// End of hal/MCU specific setup. Following should be generic code.
#[entry]
fn main() -> ! {
let (tx1, tx1_ch, rx1, rx1_ch) = setup();
hprintln!("test write to console ...").unwrap();
let buf = singleton!(: [u8; 15] = *b"\r\ncheck console").unwrap();
*buf = *b"\r\nSlowly type "; //NB. 15 characters
// create recv and send structures that can be modified in loop rather than re-assigned.
let mut recv = rx1.read_exact(buf, rx1_ch).wait(); //this returns 3-tuple (buf, rx1_ch, rx1)
let mut send = tx1.write_all(recv.0, tx1_ch).wait(); //this returns 3-tuple (buf, tx1_ch, tx1)
// Note send (write) is using buf as put into recv (read). The returned buffer in recv and
// the argument buffer in send are data. The argument buffer in recv may be a holding spot
// to put return buffer? but it is not part of the program logic. The size of the return
// buffer from recv does seem to be determined by the size of the recv argument buffer.
// The return buffer from send seems like it should be unnecessary, but it does provide
// the buffer needed in the recv argument.
// Read from console into buf and echo back to console
hprintln!("Enter 15 characters in console. Repeat.").unwrap();
hprintln!("Use ^C in gdb to exit.").unwrap();
//each pass in loop waits for input of 15 chars typed in console then echos them
loop {
recv = recv.2.read_exact(send.0, recv.1).wait();
send = send.2.write_all( recv.0, send.1).wait();
}
}
I think this is out of scope for this crate, we provide the safety requirements of each trait, but the specifics of each design is left for the downstream implementers.
I was beginning to fear that was the case. But what is the mechanism for encouraging the different stm32*-hal
's to use a common api? Is it just accident that they all use read
and write
for character-by-character serial input and output or is that because the api is dictated in embedded-hal
or somewhere else? (And where would be a better place to ask this question, because this is probably not the right place?) Thanks, and sorry I am still suffering from newbie confusion.
I think it would be hard to come up with a good interface that would work nicely with all the different DMA hardware out there. embedded-hal
would be a good place for that interface, or maybe even this crate. However, as of today, I'm unaware of such interface.