Skip to content

Commit

Permalink
Add dma fn read_available and uart-dma-rx example (#116)
Browse files Browse the repository at this point in the history
* Add dma fn read_available and uart-dma-rx example

* Remove debug code

* Enable circular buffer

* Rename uart-dma example

* DMA: Add some useful methods for checking for trasfer errors
  • Loading branch information
usbalbin authored Jan 28, 2024
1 parent 4df3c72 commit cd0921d
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 0 deletions.
94 changes: 94 additions & 0 deletions examples/uart-dma-rx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#![deny(warnings)]
#![deny(unsafe_code)]
#![no_main]
#![no_std]

extern crate cortex_m_rt as rt;

use hal::dma::{config::DmaConfig, stream::DMAExt, TransferExt};
use hal::prelude::*;
use hal::pwr::PwrExt;
use hal::serial::*;
use hal::{rcc, stm32};
use stm32g4xx_hal as hal;

use cortex_m_rt::entry;
use utils::logger::info;

#[macro_use]
mod utils;

#[entry]
fn main() -> ! {
utils::logger::init();

info!("Initializing...");

let dp = stm32::Peripherals::take().expect("cannot take peripherals");

let rcc = dp.RCC.constrain();
let pwr = dp.PWR.constrain().freeze();
let mut rcc = rcc.freeze(rcc::Config::hsi(), pwr);

let streams = dp.DMA1.split(&rcc);
let config = DmaConfig::default()
.transfer_complete_interrupt(false)
.circular_buffer(true)
.memory_increment(true);

info!("Init UART");
//let gpioa = dp.GPIOA.split(&mut rcc);
//let tx = gpioa.pa2.into_alternate();
//let rx = gpioa.pa3.into_alternate();
let gpioc = dp.GPIOC.split(&mut rcc);
let tx = gpioc.pc10.into_alternate();
let rx = gpioc.pc11.into_alternate();

let usart = dp
//.USART2
.USART3
.usart(
tx,
rx,
FullConfig::default()
.baudrate(115200.bps())
.receiver_timeout_us(1000), // Timeout after 1ms
&mut rcc,
)
.unwrap();

//let mut led = gpioa.pa5.into_push_pull_output();

info!("Start reading");

let rx_buffer = cortex_m::singleton!(: [u8; 256] = [0; 256]).unwrap();

let (_tx, rx) = usart.split();

let mut transfer = streams.0.into_circ_peripheral_to_memory_transfer(
rx.enable_dma(),
&mut rx_buffer[..],
config,
);

transfer.start(|_rx| {});
loop {
while !transfer.timeout_lapsed() {}
transfer.clear_timeout();

let mut data = [0; 256];
loop {
let data = transfer.read_available(&mut data);
if data.is_empty() {
break;
}
if let Ok(data) = core::str::from_utf8(&*data) {
info!("Received: '{}'", data);
} else {
info!("Received: {:?}", data);
}
info!("Sup");
}
//led.toggle().unwrap();
}
}
File renamed without changes.
7 changes: 7 additions & 0 deletions src/dma/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,13 @@ macro_rules! dma_stream {
dma.isr.read().$tcisr().bit_is_set()
}

#[inline(always)]
fn get_transfer_error_flag() -> bool {
//NOTE(unsafe) Atomic read with no side effects
let dma = unsafe { &*I::ptr() };
dma.isr.read().$teisr().bit_is_set()
}

#[inline(always)]
unsafe fn enable(&mut self) {
//NOTE(unsafe) We only access the registers that belongs to the StreamX
Expand Down
3 changes: 3 additions & 0 deletions src/dma/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ pub trait Stream: Sealed {
/// Get transfer complete flag.
fn get_transfer_complete_flag() -> bool;

/// Get transfer error flag.
fn get_transfer_error_flag() -> bool;

/// Enable the DMA stream.
///
/// # Safety
Expand Down
44 changes: 44 additions & 0 deletions src/dma/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,11 @@ where
STREAM::get_transfer_complete_flag()
}

#[inline(always)]
pub fn get_transfer_error_flag(&self) -> bool {
STREAM::get_transfer_error_flag()
}

/// Clear half transfer interrupt (htif) for the DMA stream.
#[inline(always)]
pub fn clear_half_transfer_interrupt(&mut self) {
Expand Down Expand Up @@ -378,6 +383,19 @@ where
read
}

pub fn read_available<'a>(
&mut self,
data: &'a mut [<PERIPHERAL as TargetAddress<PeripheralToMemory>>::MemSize],
) -> &'a mut [<PERIPHERAL as TargetAddress<PeripheralToMemory>>::MemSize] {
let blen = unsafe { self.transfer.buf.static_write_buffer().1 };
let available = self.elements_available();
let len = data.len().min(available).min(blen - 1);
let result = &mut data[0..len];
self.read_exact(result);

result
}

/// Starts the transfer, the closure will be executed right after enabling
/// the stream.
pub fn start<F>(&mut self, f: F)
Expand Down Expand Up @@ -424,6 +442,11 @@ where
self.transfer.get_transfer_complete_flag()
}

#[inline(always)]
pub fn get_transfer_error_flag(&self) -> bool {
self.transfer.get_transfer_error_flag()
}

/// Clear half transfer interrupt (htif) for the DMA stream.
#[inline(always)]
pub fn clear_half_transfer_interrupt(&mut self) {
Expand Down Expand Up @@ -487,6 +510,27 @@ impl_adc_overrun!(ADC3,);
))]
impl_adc_overrun!(ADC4, ADC5,);

macro_rules! impl_serial_timeout {
($($uart:ident, )*) => {$(
impl<STREAM, BUF, Pin> CircTransfer<STREAM, crate::serial::Rx<crate::stm32::$uart, Pin, crate::serial::DMA>, BUF>
where
STREAM: Stream,
/*BUF: StaticWriteBuffer + Deref*/ {
pub fn timeout_lapsed(&self) -> bool {
self.transfer.peripheral.timeout_lapsed()
}

pub fn clear_timeout(&mut self) {
self.transfer.peripheral.clear_timeout();
}
}
)*};
}

impl_serial_timeout!(USART1, USART2, USART3, UART4,);
#[cfg(not(any(feature = "stm32g431", feature = "stm32g441")))]
impl_serial_timeout!(UART5,);

pub trait TransferExt<STREAM>
where
STREAM: traits::Stream,
Expand Down

0 comments on commit cd0921d

Please sign in to comment.