Skip to content

Commit 0af3c30

Browse files
authored
Merge pull request #698 from hacknus/master
create uart-dma.rs example
2 parents 198d127 + 00ab471 commit 0af3c30

File tree

3 files changed

+246
-0
lines changed

3 files changed

+246
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
99

10+
- Added an example to show how to do DMA with UART (Rx only) [#698]
11+
1012
## [v0.18.0] - 2023-11-19
1113

1214
### Changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,10 @@ required-features = ["gpiod"] # stm32f411
472472
name = "spi-dma"
473473
required-features = ["stm32f411"]
474474

475+
[[example]]
476+
name = "uart-dma"
477+
required-features = ["stm32f405"]
478+
475479
[[example]]
476480
name = "ssd1306-image"
477481
required-features = ["device-selected"] # stm32f411

examples/uart-dma.rs

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
#![no_main]
2+
#![no_std]
3+
4+
// Halt on panic
5+
use panic_halt as _;
6+
use stm32f4xx_hal::dma::{DmaFlag, PeripheralToMemory, Stream1};
7+
8+
use core::cell::RefCell;
9+
use cortex_m::interrupt::Mutex;
10+
use cortex_m_rt::entry;
11+
use stm32f4xx_hal::dma::config::DmaConfig;
12+
use stm32f4xx_hal::pac::Interrupt;
13+
use stm32f4xx_hal::pac::{interrupt, DMA1};
14+
use stm32f4xx_hal::uart::config::StopBits;
15+
use stm32f4xx_hal::uart::{Config, Rx, Serial};
16+
use stm32f4xx_hal::{
17+
dma::{StreamsTuple, Transfer},
18+
pac,
19+
prelude::*,
20+
serial,
21+
};
22+
23+
// uart buffer size
24+
const UART_BUFFER_SIZE: usize = 128;
25+
26+
// Simple ring buffer
27+
pub struct Buffer {
28+
buffer: [u8; UART_BUFFER_SIZE],
29+
write_idx: usize,
30+
read_idx: usize,
31+
}
32+
33+
impl Buffer {
34+
pub(crate) const fn new() -> Buffer {
35+
Buffer {
36+
buffer: [0; UART_BUFFER_SIZE],
37+
write_idx: 0,
38+
read_idx: 0,
39+
}
40+
}
41+
42+
pub fn push(&mut self, data: u8) {
43+
self.buffer[self.write_idx] = data;
44+
self.write_idx = (self.write_idx + 1) % UART_BUFFER_SIZE;
45+
}
46+
47+
pub fn read(&mut self) -> Option<u8> {
48+
if self.write_idx != self.read_idx {
49+
let data = self.buffer[self.read_idx];
50+
self.read_idx = (self.read_idx + 1) % UART_BUFFER_SIZE;
51+
Some(data)
52+
} else {
53+
None
54+
}
55+
}
56+
}
57+
58+
// dma type, needs to be adapted for uart and dma channel
59+
type UartDma = Transfer<
60+
Stream1<DMA1>,
61+
4,
62+
Rx<pac::USART3>,
63+
PeripheralToMemory,
64+
&'static mut [u8; UART_BUFFER_SIZE],
65+
>;
66+
67+
// shared dma reference
68+
pub static G_TRANSFER: Mutex<RefCell<Option<UartDma>>> = Mutex::new(RefCell::new(None));
69+
70+
// shared uart3 reference
71+
pub static G_UART3_BUFFER: Mutex<RefCell<Option<Buffer>>> = Mutex::new(RefCell::new(None));
72+
73+
// shared TX reference
74+
pub static G_UART3_TX: Mutex<RefCell<Option<serial::Tx<pac::USART3>>>> =
75+
Mutex::new(RefCell::new(None));
76+
77+
// dma buffer
78+
pub static mut RX_UART3_BUFFER: [u8; UART_BUFFER_SIZE] = [0; UART_BUFFER_SIZE];
79+
80+
// a wrapper function that reads out of the uart ring buffer
81+
pub fn uart3_read_until(eol: u8) -> Option<[u8; UART_BUFFER_SIZE]> {
82+
let r = cortex_m::interrupt::free(|cs| {
83+
if let Some(buffer) = G_UART3_BUFFER.borrow(cs).borrow_mut().as_mut() {
84+
let mut buf = [0; UART_BUFFER_SIZE];
85+
let mut i = 0;
86+
while let Some(byte) = buffer.read() {
87+
if byte == eol {
88+
break;
89+
}
90+
if i < UART_BUFFER_SIZE - 1 {
91+
buf[i] = byte;
92+
} else {
93+
break;
94+
}
95+
i += 1;
96+
}
97+
if buf[0] == 0 {
98+
return None;
99+
}
100+
Some(buf)
101+
} else {
102+
None
103+
}
104+
});
105+
r
106+
}
107+
108+
// a wrapper function for uart write
109+
pub fn uart3_write(data: &[u8]) -> Result<(), serial::Error> {
110+
cortex_m::interrupt::free(|cs| {
111+
let ret = if let Some(uart) = G_UART3_TX.borrow(cs).borrow_mut().as_mut() {
112+
let non_zero_len = data
113+
.iter()
114+
.rposition(|&x| x != 0)
115+
.map(|idx| idx + 1)
116+
.unwrap_or(0);
117+
// Create a custom slice with only non-zero elements
118+
uart.bwrite_all(&data[0..non_zero_len])?;
119+
uart.bflush()
120+
} else {
121+
Err(serial::Error::Other)
122+
};
123+
ret
124+
})
125+
}
126+
127+
#[entry]
128+
fn main() -> ! {
129+
if let Some(dp) = pac::Peripherals::take() {
130+
// Set up the system clock.
131+
let rcc = dp.RCC.constrain();
132+
let clocks = rcc.cfgr.freeze();
133+
134+
let dma1 = StreamsTuple::new(dp.DMA1);
135+
136+
let gpiod = dp.GPIOD.split();
137+
138+
// configure UART, it is important to configure this to use DMA
139+
let rx_3 = gpiod.pd9.into_alternate();
140+
let tx_3 = gpiod.pd8.into_alternate();
141+
let uart3 = Serial::new(
142+
dp.USART3,
143+
(tx_3, rx_3),
144+
Config::default()
145+
.baudrate(9600.bps())
146+
.parity_none()
147+
.stopbits(StopBits::STOP1)
148+
.dma(serial::config::DmaConfig::Rx),
149+
&clocks,
150+
)
151+
.unwrap();
152+
153+
// Note! It is better to use memory pools, such as heapless::pool::Pool. But it not work with embedded_dma yet.
154+
// See CHANGELOG of unreleased main branch and issue https://github.com/japaric/heapless/pull/362 for details.
155+
let rx_buffer1 =
156+
cortex_m::singleton!(: [u8; UART_BUFFER_SIZE] = [0; UART_BUFFER_SIZE]).unwrap();
157+
let _rx_buffer2 =
158+
cortex_m::singleton!(: [u8; UART_BUFFER_SIZE] = [0; UART_BUFFER_SIZE]).unwrap();
159+
160+
let (tx, mut rx) = uart3.split();
161+
162+
rx.listen_idle();
163+
164+
cortex_m::interrupt::free(|cs| *G_UART3_TX.borrow(cs).borrow_mut() = Some(tx));
165+
166+
cortex_m::interrupt::free(|cs| {
167+
*G_UART3_BUFFER.borrow(cs).borrow_mut() = Some(Buffer::new());
168+
});
169+
// Initialize and start DMA stream
170+
let mut rx_transfer = Transfer::init_peripheral_to_memory(
171+
dma1.1,
172+
rx,
173+
rx_buffer1,
174+
None,
175+
DmaConfig::default()
176+
.memory_increment(true)
177+
.fifo_enable(true)
178+
.fifo_error_interrupt(true)
179+
.transfer_complete_interrupt(true),
180+
);
181+
182+
rx_transfer.start(|_rx| {});
183+
184+
cortex_m::interrupt::free(|cs| *G_TRANSFER.borrow(cs).borrow_mut() = Some(rx_transfer));
185+
186+
// Enable interrupt
187+
unsafe {
188+
cortex_m::peripheral::NVIC::unmask(Interrupt::USART3);
189+
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA1_STREAM1);
190+
}
191+
}
192+
193+
// to test this, connect the RX and TX pins together for a loopback
194+
// - so you can listen to what you just sent.
195+
196+
uart3_write(b"hello world\r").expect("Failed to write to UART3");
197+
let _response = uart3_read_until(b'\r').expect("Failed to read from UART3");
198+
// ... do something with response
199+
200+
loop {
201+
cortex_m::asm::nop();
202+
}
203+
}
204+
205+
#[interrupt]
206+
#[allow(non_snake_case)]
207+
fn USART3() {
208+
cortex_m::interrupt::free(|cs| {
209+
if let Some(transfer) = G_TRANSFER.borrow(cs).borrow_mut().as_mut() {
210+
if transfer.is_idle() {
211+
// Calc received bytes count
212+
let bytes_count = UART_BUFFER_SIZE - transfer.number_of_transfers() as usize;
213+
unsafe {
214+
let mut buffer = [0; UART_BUFFER_SIZE];
215+
match transfer.next_transfer(&mut RX_UART3_BUFFER) {
216+
Ok((b, _)) => buffer = *b,
217+
Err(_err) => {}
218+
}
219+
if let Some(ring_buffer) = G_UART3_BUFFER.borrow(cs).borrow_mut().as_mut() {
220+
for i in 0..bytes_count {
221+
ring_buffer.push(buffer[i]);
222+
}
223+
}
224+
}
225+
}
226+
transfer.clear_idle_interrupt();
227+
}
228+
});
229+
}
230+
231+
#[interrupt]
232+
#[allow(non_snake_case)]
233+
fn DMA1_STREAM1() {
234+
cortex_m::interrupt::free(|cs| {
235+
if let Some(transfer) = G_TRANSFER.borrow(cs).borrow_mut().as_mut() {
236+
// Its important to clear fifo errors as the transfer is paused until it is cleared
237+
transfer.clear_flags(DmaFlag::FifoError | DmaFlag::TransferComplete);
238+
}
239+
});
240+
}

0 commit comments

Comments
 (0)