Skip to content

Commit 0bfc7f4

Browse files
committed
Implement Serial DMA abstraction
The implementation follows existing DMA abstraction for I2C interface. The implementation has been tested on STM32F407-Discovery board with the code in `examples/serial-dma.rs`.
1 parent df00482 commit 0bfc7f4

File tree

8 files changed

+881
-1
lines changed

8 files changed

+881
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1010
- add trait bound `RegisterBlockImpl` to type `RegisterBlock` associated with `serial::Instance` [#732]
1111
- remove unneeded trait bound for methods that take in a `serial::Instance` and use the associated `RegisterBlock`
1212
- bump `sdio-host` to 0.9.0, refactor SDIO initialization [#734]
13+
- Added non-blocking serial based on DMA [#738]
1314
- use RTCCLK for RTC wakeup timer for short durations [#746]
1415
- Support 8-bit FMC data bus
1516

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,10 @@ required-features = []
506506
name = "serial-9bit"
507507
required-features = ["gpiod"] # stm32f411
508508

509+
[[example]]
510+
name = "serial-dma"
511+
required-features = ["stm32f407"]
512+
509513
[[example]]
510514
name = "spi-dma"
511515
required-features = ["stm32f411"]

examples/serial-dma.rs

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
use core::{
5+
cell::RefCell,
6+
sync::atomic::{AtomicBool, Ordering},
7+
};
8+
use cortex_m::interrupt::Mutex;
9+
use cortex_m_rt::entry;
10+
use panic_halt as _;
11+
use stm32f4xx_hal::{
12+
self,
13+
dma::{StreamX, StreamsTuple},
14+
interrupt,
15+
pac::{self, DMA1, USART2},
16+
prelude::*,
17+
serial::{
18+
self,
19+
dma::{RxDMA, SerialDma, TxDMA},
20+
},
21+
uart::{config::StopBits, Config, Serial},
22+
};
23+
24+
/// Global variable for USART2 DMA handle.
25+
static USART2_DMA: Mutex<
26+
RefCell<
27+
Option<
28+
SerialDma<
29+
USART2,
30+
TxDMA<USART2, StreamX<DMA1, 6>, 4>,
31+
RxDMA<USART2, StreamX<DMA1, 5>, 4>,
32+
>,
33+
>,
34+
>,
35+
> = Mutex::new(RefCell::new(None));
36+
37+
/// Boolean flag to wait for DMA finish.
38+
static DONE: AtomicBool = AtomicBool::new(false);
39+
40+
#[entry]
41+
fn main() -> ! {
42+
if let Some(dp) = pac::Peripherals::take() {
43+
// Set up the system clock.
44+
let rcc = dp.RCC.constrain();
45+
let clocks = rcc.cfgr.freeze();
46+
47+
// Enable DMA1.
48+
let dma1 = StreamsTuple::new(dp.DMA1);
49+
50+
// Enable GPIOA.
51+
let gpioa = dp.GPIOA.split();
52+
53+
// Configure USART2.
54+
let rx_2 = gpioa.pa3.into_alternate();
55+
let tx_2 = gpioa.pa2.into_alternate();
56+
let usart2 = Serial::new(
57+
dp.USART2,
58+
(tx_2, rx_2),
59+
Config::default()
60+
.baudrate(115200.bps())
61+
.parity_none()
62+
.stopbits(StopBits::STOP1)
63+
.dma(serial::config::DmaConfig::TxRx),
64+
&clocks,
65+
)
66+
.unwrap();
67+
68+
// Make USART2 use DMA.
69+
let usart2_dma = usart2.use_dma(dma1.6, dma1.5);
70+
71+
// Put DMA handle to the global variable.
72+
cortex_m::interrupt::free(|cs| {
73+
USART2_DMA.borrow(cs).borrow_mut().replace(usart2_dma);
74+
});
75+
76+
// Enable interrupt
77+
unsafe {
78+
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::USART2);
79+
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA1_STREAM5);
80+
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA1_STREAM6);
81+
}
82+
83+
/*** Test Below ***/
84+
85+
let mut buf = [0u8; 6];
86+
87+
// Read 6 bytes to the buffer using the DMA non-blocking mode.
88+
cortex_m::interrupt::free(|cs| unsafe {
89+
if let Some(usart2_dma) = USART2_DMA.borrow(cs).borrow_mut().as_mut() {
90+
usart2_dma.read_dma(&mut buf, None).unwrap();
91+
}
92+
});
93+
94+
// Wait until DMA finishes.
95+
while !DONE.load(Ordering::SeqCst) {}
96+
DONE.store(false, Ordering::SeqCst);
97+
98+
// Write the 6 bytes back using the DMA non-blocking mode.
99+
cortex_m::interrupt::free(|cs| unsafe {
100+
if let Some(usart2_dma) = USART2_DMA.borrow(cs).borrow_mut().as_mut() {
101+
usart2_dma.write_dma(&buf, None).unwrap();
102+
}
103+
});
104+
105+
// Wait until DMA finishes.
106+
while !DONE.load(Ordering::SeqCst) {}
107+
108+
// Read 6 bytes to the buffer using the blocking mode.
109+
cortex_m::interrupt::free(|cs| {
110+
if let Some(usart2_dma) = USART2_DMA.borrow(cs).borrow_mut().as_mut() {
111+
usart2_dma.read(&mut buf).unwrap();
112+
}
113+
});
114+
115+
// Write the 6 bytes back using the blocking mode.
116+
cortex_m::interrupt::free(|cs| {
117+
if let Some(usart2_dma) = USART2_DMA.borrow(cs).borrow_mut().as_mut() {
118+
usart2_dma.write(&buf).unwrap();
119+
}
120+
});
121+
}
122+
123+
loop {}
124+
}
125+
126+
#[interrupt]
127+
#[allow(non_snake_case)]
128+
fn USART2() {
129+
cortex_m::interrupt::free(|cs| {
130+
if let Some(usart2_dma) = USART2_DMA.borrow(cs).borrow_mut().as_mut() {
131+
usart2_dma.handle_error_interrupt();
132+
}
133+
});
134+
}
135+
136+
#[interrupt]
137+
#[allow(non_snake_case)]
138+
fn DMA1_STREAM5() {
139+
cortex_m::interrupt::free(|cs| {
140+
if let Some(usart2_dma) = USART2_DMA.borrow(cs).borrow_mut().as_mut() {
141+
usart2_dma.handle_dma_interrupt();
142+
DONE.store(true, Ordering::SeqCst);
143+
}
144+
});
145+
}
146+
147+
#[interrupt]
148+
#[allow(non_snake_case)]
149+
fn DMA1_STREAM6() {
150+
cortex_m::interrupt::free(|cs| {
151+
if let Some(usart2_dma) = USART2_DMA.borrow(cs).borrow_mut().as_mut() {
152+
usart2_dma.handle_dma_interrupt();
153+
DONE.store(true, Ordering::SeqCst);
154+
}
155+
});
156+
}

src/prelude.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ pub use crate::qei::QeiExt as _stm32f4xx_hal_QeiExt;
6868
pub use crate::rcc::RccExt as _stm32f4xx_hal_rcc_RccExt;
6969
#[cfg(feature = "rng")]
7070
pub use crate::rng::RngExt as _stm32f4xx_hal_rng_RngExt;
71+
pub use crate::serial::dma::SerialHandleIT as _stm32f4xx_hal_serial_dma_SerialHandleIT;
72+
pub use crate::serial::dma::SerialReadDMA as _stm32f4xx_hal_serial_dma_SerialReadDMA;
73+
pub use crate::serial::dma::SerialWriteDMA as _stm32f4xx_hal_serial_dma_SerialWriteDMA;
7174
pub use crate::serial::RxISR as _stm32f4xx_hal_serial_RxISR;
7275
pub use crate::serial::RxListen as _stm32f4xx_hal_serial_RxListen;
7376
pub use crate::serial::SerialExt as _stm32f4xx_hal_serial_SerialExt;

src/serial.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ use crate::pac;
3030
use crate::gpio::NoPin;
3131
use crate::rcc::Clocks;
3232

33+
pub mod dma;
34+
3335
/// Serial error kind
3436
///
3537
/// This represents a common set of serial operation errors. HAL implementations are
@@ -273,6 +275,10 @@ macro_rules! halUsart {
273275
})
274276
});
275277
}
278+
279+
fn peri_address() -> u32 {
280+
unsafe { (*(<$USART>::ptr() as *const Self::RegisterBlock)).peri_address() }
281+
}
276282
}
277283
};
278284
}

0 commit comments

Comments
 (0)