Skip to content

Commit b5d7538

Browse files
committed
create uart-dma.rs example
1 parent 198d127 commit b5d7538

File tree

2 files changed

+243
-0
lines changed

2 files changed

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

0 commit comments

Comments
 (0)