Skip to content

Commit 1e77c05

Browse files
committed
Improve UART DMA
1 parent 86560ee commit 1e77c05

File tree

25 files changed

+654
-278
lines changed

25 files changed

+654
-278
lines changed

.vscode/tasks.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
"kind": "build",
1616
"isDefault": true
1717
},
18-
1918
},
2019
{
2120
"label": "build",
@@ -37,6 +36,19 @@
3736
"f103c8"
3837
],
3938
"group": "build",
39+
},
40+
{
41+
"label": "Test Windows",
42+
"type": "cargo",
43+
"command": "test",
44+
"args": [
45+
"--features=std",
46+
"--target=x86_64-pc-windows-msvc",
47+
],
48+
"group": {
49+
"kind": "test",
50+
"isDefault": true
51+
},
4052
}
4153
]
4254
}

Cargo.toml

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@
1515
features = ["stm32f103", "xG"]
1616

1717
[features]
18-
critical-section-single-core = ["cortex-m/critical-section-single-core"]
19-
stm32f100 = ["stm32f1/stm32f100"]
20-
stm32f101 = ["stm32f1/stm32f101"]
21-
stm32f103 = ["stm32f1/stm32f103"]
22-
stm32f105 = ["stm32f1/stm32f107", "connectivity"]
23-
stm32f107 = ["stm32f1/stm32f107", "connectivity"]
18+
critical-section-single-core = [
19+
"cortex-m/critical-section-single-core",
20+
"mcu",
21+
]
22+
stm32f100 = ["stm32f1/stm32f100", "mcu"]
23+
stm32f101 = ["stm32f1/stm32f101", "mcu"]
24+
stm32f103 = ["stm32f1/stm32f103", "mcu"]
25+
stm32f105 = ["stm32f1/stm32f107", "connectivity", "mcu"]
26+
stm32f107 = ["stm32f1/stm32f107", "connectivity", "mcu"]
2427
x4 = []
2528
x6 = []
2629
x8 = ["medium"]
@@ -42,12 +45,15 @@
4245
has-can = []
4346
# Devices with Dac
4447
has-dac = []
48+
mcu = ["dep:cortex-m", "dep:cortex-m-rt", "dep:stm32f1"]
4549
rtic = ["dep:rtic-monotonic"]
50+
std = []
4651

4752
[dependencies]
4853
bitflags = "2.9"
49-
cortex-m = "0.7"
50-
cortex-m-rt = "0.7"
54+
cfg-if = "1.0"
55+
cortex-m = { version = "0.7", optional = true }
56+
cortex-m-rt = { version = "0.7", optional = true }
5157
critical-section = "1.2"
5258
embedded-dma = "0.2"
5359
embedded-hal = "1.0"
@@ -58,7 +64,7 @@
5864
nb = "1.1"
5965
rtic-monotonic = { version = "1.0", optional = true }
6066
rtrb = { version = "0.3", default-features = false }
61-
stm32f1 = { version = "0.16", features = ["atomics"] }
67+
stm32f1 = { version = "0.16", optional = true }
6268

6369
[profile.release]
6470
codegen-units = 1

examples/f103c8/src/main.rs

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,21 @@
55
#![allow(unused_mut)]
66

77
use core::{mem::MaybeUninit, panic::PanicInfo};
8-
use stm32f1_hal::gpio::ExtiPin;
9-
use stm32f1_hal::{self as hal, Steal};
108
use stm32f1_hal::{
11-
Heap, Mcu,
9+
self as hal, Heap, Mcu, Steal,
1210
afio::{NONE_PIN, RemapDefault},
1311
cortex_m::asm,
1412
cortex_m_rt::entry,
13+
dma::{DmaBindRx, DmaBindTx, DmaEvent, DmaPriority},
1514
embedded_hal::{self, pwm::SetDutyCycle},
1615
embedded_io,
17-
gpio::{Edge, PinState},
16+
gpio::{Edge, ExtiPin, PinState},
1817
nvic_scb::PriorityGrouping,
1918
pac::{self, Interrupt},
2019
prelude::*,
2120
rcc,
2221
timer::*,
23-
uart::{self, UartPeriph},
22+
uart::{self, UartPeriphExt},
2423
};
2524

2625
mod led_task;
@@ -49,9 +48,6 @@ fn main() -> ! {
4948
let mut rcc = dp.RCC.constrain().freeze(cfg, &mut flash.acr);
5049
assert_eq!(rcc.clocks.sysclk(), sysclk);
5150

52-
let mut gpioa = dp.GPIOA.split(&mut rcc);
53-
let mut gpiob = dp.GPIOB.split(&mut rcc);
54-
5551
let afio = dp.AFIO.constrain(&mut rcc);
5652
let mut mcu = Mcu {
5753
scb,
@@ -62,10 +58,16 @@ fn main() -> ! {
6258
};
6359

6460
// Keep them in one place for easier management
65-
mcu.nvic.enable(pac::interrupt::USART1, false); // Optional
61+
mcu.nvic.enable(Interrupt::USART1, false); // Optional
6662
mcu.nvic.set_priority(Interrupt::USART1, 2);
67-
mcu.nvic.enable(pac::interrupt::EXTI1, false);
63+
mcu.nvic.enable(Interrupt::EXTI1, false);
6864
mcu.nvic.set_priority(Interrupt::EXTI1, 1);
65+
mcu.nvic.enable(Interrupt::DMA1_CHANNEL4, false);
66+
mcu.nvic.set_priority(Interrupt::DMA1_CHANNEL4, 2);
67+
68+
let mut gpioa = dp.GPIOA.split(&mut mcu.rcc);
69+
let mut gpiob = dp.GPIOB.split(&mut mcu.rcc);
70+
let mut dma1 = dp.DMA1.split(&mut mcu.rcc);
6971

7072
// UART -------------------------------------
7173

@@ -85,17 +87,18 @@ fn main() -> ! {
8587
};
8688

8789
// let mut uart_task = uart_poll_init(uart_tx, uart_rx);
88-
let mut uart_task = uart_interrupt_init(
90+
// let mut uart_task = uart_interrupt_init(uart_tx, uart_rx, &all_it::USART1_CB, &mut mcu);
91+
dma1.4.set_priority(DmaPriority::Medium);
92+
dma1.5.set_priority(DmaPriority::Medium);
93+
let mut uart_task = uart_dma_init(
8994
uart_tx,
95+
dma1.4,
96+
&all_it::DMA1_CHANNEL4_CB,
9097
uart_rx,
98+
dma1.5,
9199
&mut mcu,
92-
pac::interrupt::USART1,
93-
&all_it::USART1_CB,
94100
);
95101

96-
let dma1 = dp.DMA1.split(&mut mcu.rcc);
97-
let dma_rx = dma1.5;
98-
99102
// LED --------------------------------------
100103

101104
let mut led = gpiob
@@ -149,21 +152,20 @@ fn panic(_info: &PanicInfo) -> ! {
149152
loop {}
150153
}
151154

152-
fn uart_poll_init<U: UartPeriph>(
155+
fn uart_poll_init<U: UartPeriphExt>(
153156
tx: uart::Tx<U>,
154157
rx: uart::Rx<U>,
155158
) -> UartPollTask<impl embedded_io::Write, impl embedded_io::Read> {
156159
let (uart_tx, uart_rx) = (tx.into_poll(0, 10_000), rx.into_poll(0, 1_000));
157160
UartPollTask::new(32, uart_tx, uart_rx)
158161
}
159162

160-
fn uart_interrupt_init<U: UartPeriph + 'static>(
163+
fn uart_interrupt_init<U: UartPeriphExt + 'static>(
161164
tx: uart::Tx<U>,
162165
rx: uart::Rx<U>,
163-
mcu: &mut Mcu,
164-
it_line: pac::interrupt,
165166
interrupt_callback: &hal::interrupt::Callback,
166-
) -> UartPollTask<impl embedded_io::Write + use<U>, impl embedded_io::Read + use<U>> {
167+
mcu: &mut Mcu,
168+
) -> UartPollTask<impl embedded_io::Write + 'static, impl embedded_io::Read + 'static> {
167169
let (tx, mut tx_it) = tx.into_interrupt(64, 0, 10_000);
168170
let (rx, mut rx_it) = rx.into_interrupt(64, 0);
169171
interrupt_callback.set(mcu, move || {
@@ -173,7 +175,28 @@ fn uart_interrupt_init<U: UartPeriph + 'static>(
173175
UartPollTask::new(32, tx, rx)
174176
}
175177

178+
fn uart_dma_init<'r, U: UartPeriphExt + 'static>(
179+
tx: uart::Tx<U>,
180+
mut dma_tx: impl DmaBindTx<U> + 'static,
181+
interrupt_callback: &hal::interrupt::Callback,
182+
rx: uart::Rx<U>,
183+
dma_rx: impl DmaBindRx<U> + 'r,
184+
mcu: &mut Mcu,
185+
) -> UartPollTask<impl embedded_io::Write + 'static, impl embedded_io::Read + 'r> {
186+
let uart_rx = rx.into_dma_circle(dma_rx, 64);
187+
dma_tx.set_interrupt(DmaEvent::TransferComplete, true);
188+
let (uart_tx, mut tx_it) = tx.into_dma_ringbuf(dma_tx, 32);
189+
interrupt_callback.set(mcu, move || {
190+
tx_it.interrupt_reload();
191+
});
192+
UartPollTask::new(32, uart_tx, uart_rx)
193+
}
194+
176195
mod all_it {
177196
use super::hal::{interrupt_handler, pac::interrupt};
178-
interrupt_handler!((USART1, USART1_CB), (EXTI1, EXTI1_CB),);
197+
interrupt_handler!(
198+
(USART1, USART1_CB),
199+
(EXTI1, EXTI1_CB),
200+
(DMA1_CHANNEL4, DMA1_CHANNEL4_CB),
201+
);
179202
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
use super::*;
2+
use crate::common::os::*;
3+
4+
/// A buffer used for DMA cyclic data reception, continuously read by the user.
5+
pub struct DmaCircularBufferRx<T: Sized, CH> {
6+
ch: CH,
7+
buf: CircularBuffer<T>,
8+
}
9+
10+
impl<T, CH> DmaCircularBufferRx<T, CH>
11+
where
12+
T: Sized + Copy,
13+
CH: DmaChannel,
14+
{
15+
pub fn new(mut ch: CH, peripheral_addr: usize, buf_size: usize) -> Self {
16+
let buf = CircularBuffer::<T>::new(buf_size);
17+
ch.stop();
18+
ch.set_memory_buf_for_peripheral(buf.as_slice());
19+
ch.set_peripheral_address::<T>(peripheral_addr, false, false, true);
20+
ch.start();
21+
Self { ch, buf }
22+
}
23+
24+
pub fn read(&mut self, max: usize) -> Option<&[T]> {
25+
self.buf.read(self.ch.get_left_len(), max)
26+
}
27+
}
28+
29+
pub struct CircularBuffer<T: Sized> {
30+
recv_buf: Vec<T>,
31+
read_idx: usize,
32+
}
33+
34+
impl<T> CircularBuffer<T> {
35+
fn new(buf_size: usize) -> Self {
36+
let mut recv_buf = Vec::<T>::with_capacity(buf_size);
37+
unsafe { recv_buf.set_len(buf_size) }
38+
39+
Self {
40+
recv_buf,
41+
read_idx: 0,
42+
}
43+
}
44+
45+
fn read(&mut self, left_len: usize, max: usize) -> Option<&[T]> {
46+
let dma_recv_idx = if left_len == 0 {
47+
0
48+
} else {
49+
self.recv_buf.len() - left_len
50+
};
51+
52+
if self.read_idx == dma_recv_idx {
53+
return None;
54+
}
55+
56+
let ret;
57+
if dma_recv_idx < self.read_idx {
58+
if max > self.recv_buf.len() - self.read_idx {
59+
ret = Some(&self.recv_buf[self.read_idx..]);
60+
self.read_idx = 0;
61+
} else {
62+
let end = self.read_idx + max;
63+
ret = Some(&self.recv_buf[self.read_idx..end]);
64+
self.read_idx = end;
65+
}
66+
} else {
67+
if max > dma_recv_idx - self.read_idx {
68+
ret = Some(&self.recv_buf[self.read_idx..dma_recv_idx]);
69+
self.read_idx = dma_recv_idx;
70+
} else {
71+
let end = self.read_idx + max;
72+
ret = Some(&self.recv_buf[self.read_idx..end]);
73+
self.read_idx = end;
74+
};
75+
}
76+
77+
return ret;
78+
}
79+
80+
fn as_slice(&self) -> &[T] {
81+
self.recv_buf.as_slice()
82+
}
83+
}
84+
85+
#[cfg(test)]
86+
mod tests {
87+
use super::*;
88+
89+
#[test]
90+
fn circular_buffer() {
91+
let buf_size = 13;
92+
let mut buf = CircularBuffer::new(buf_size);
93+
assert_eq!(buf.recv_buf.len(), buf_size);
94+
95+
for i in 0..buf_size {
96+
buf.recv_buf[i] = i as u8;
97+
}
98+
99+
assert_eq!(
100+
buf.read(5, usize::MAX),
101+
Some([0u8, 1, 2, 3, 4, 5, 6, 7].as_slice())
102+
);
103+
assert_eq!(buf.read(5, usize::MAX), None);
104+
// Single wraparound
105+
assert_eq!(
106+
buf.read(0, usize::MAX),
107+
Some([8u8, 9, 10, 11, 12].as_slice())
108+
);
109+
assert_eq!(buf.read(0, usize::MAX), None);
110+
assert_eq!(buf.read(buf_size, usize::MAX), None);
111+
// small max
112+
assert_eq!(buf.read(5, 5), Some([0u8, 1, 2, 3, 4].as_slice()));
113+
assert_eq!(buf.read(5, 5), Some([5u8, 6, 7].as_slice()));
114+
assert_eq!(buf.read(5, 5), None);
115+
assert_eq!(
116+
buf.read(0, usize::MAX),
117+
Some([8u8, 9, 10, 11, 12].as_slice())
118+
);
119+
// Multiple wraparounds
120+
assert_eq!(
121+
buf.read(5, usize::MAX),
122+
Some([0u8, 1, 2, 3, 4, 5, 6, 7].as_slice())
123+
);
124+
assert_eq!(
125+
buf.read(10, usize::MAX),
126+
Some([8u8, 9, 10, 11, 12].as_slice())
127+
);
128+
assert_eq!(buf.read(10, usize::MAX), Some([0u8, 1, 2].as_slice()));
129+
assert_eq!(buf.read(10, usize::MAX), None);
130+
}
131+
}

src/common/dma/mod.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
mod circular_buffer_rx;
2+
pub use circular_buffer_rx::*;
3+
mod ringbuf_tx;
4+
pub use ringbuf_tx::*;
5+
16
pub trait DmaChannel {
27
fn start(&mut self);
38
fn stop(&mut self);
49

5-
fn set_peripheral_address<T: Sized>(
10+
fn set_peripheral_address<T: Sized + Copy>(
611
&mut self,
712
address: usize,
813
mem_to_periph: bool,
@@ -11,12 +16,17 @@ pub trait DmaChannel {
1116
);
1217
fn set_memory_address(&mut self, address: usize, increase: bool);
1318
fn set_transfer_length(&mut self, len: usize);
14-
fn set_memory_buf_for_peripheral<T: Sized>(&mut self, buf: &[T]) {
19+
fn set_memory_buf_for_peripheral<T: Sized + Copy>(&mut self, buf: &[T]) {
1520
self.set_memory_address(buf.as_ptr() as usize, true);
1621
self.set_transfer_length(buf.len());
1722
}
1823

19-
fn set_memory_to_memory<T: Sized>(&mut self, src_addr: usize, dst_addr: usize, len: usize);
24+
fn set_memory_to_memory<T: Sized + Copy>(
25+
&mut self,
26+
src_addr: usize,
27+
dst_addr: usize,
28+
len: usize,
29+
);
2030

2131
fn get_left_len(&self) -> usize;
2232
fn in_progress(&self) -> bool;

0 commit comments

Comments
 (0)