Skip to content

Commit 27c1655

Browse files
committed
gpdma: add GPDMA driver
This adds support for one-shot DMA transfers for all supported transfer types with linear addressing. It does not yet support linked list buffer loading, or support 2D addressing. Memory-to-memory transfers were quite thoroughly tested by the provided example, memory-to-peripheral and peripheral-to-memory transfers were tested with the SPI peripheral (working on a test branch). Peripheral-to-peripheral transfers are assumed to work given their similarity to memory-to-peripheral/peripheral-to-memory transfers, but will be properly tested at a later stage. The driver includes helper structs for peripheral transfers in the gpdma::periph module which handle the common operations for setting up one-directional and full-duplex transfers using one, or two channels, respectively.
1 parent b82da58 commit 27c1655

File tree

9 files changed

+2486
-0
lines changed

9 files changed

+2486
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ defmt = [
6666
cortex-m = { version = "^0.7.7", features = ["critical-section-single-core"] }
6767
stm32h5 = { package = "stm32h5", version = "0.16.0" }
6868
fugit = "0.3.7"
69+
embedded-dma = "0.2"
6970
embedded-hal = "1.0.0"
7071
defmt = { version = "1.0.0", optional = true }
7172
paste = "1.0.15"

examples/dma.rs

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
// #![deny(warnings)]
2+
#![no_main]
3+
#![no_std]
4+
5+
mod utilities;
6+
7+
use core::mem::MaybeUninit;
8+
9+
use cortex_m_rt::entry;
10+
use cortex_m_semihosting::debug;
11+
use stm32h5xx_hal::{
12+
gpdma::{config::transform::*, DmaConfig, DmaTransfer},
13+
pac,
14+
prelude::*,
15+
};
16+
17+
static mut SOURCE_BYTES: MaybeUninit<[u8; 40]> = MaybeUninit::uninit();
18+
static mut DEST_BYTES: MaybeUninit<[u8; 40]> = MaybeUninit::zeroed();
19+
static mut DEST_HALF_WORDS: MaybeUninit<[u16; 20]> = MaybeUninit::uninit();
20+
static mut SOURCE_WORDS: MaybeUninit<[u32; 10]> = MaybeUninit::uninit();
21+
static mut DEST_WORDS: MaybeUninit<[u32; 10]> = MaybeUninit::uninit();
22+
23+
fn u8_to_u8_sequential() -> (&'static [u8; 40], &'static mut [u8; 40]) {
24+
let buf: &mut [MaybeUninit<u8>; 40] = unsafe {
25+
&mut *(core::ptr::addr_of_mut!(SOURCE_BYTES)
26+
as *mut [MaybeUninit<u8>; 40])
27+
};
28+
29+
for (i, value) in buf.iter_mut().enumerate() {
30+
unsafe {
31+
value.as_mut_ptr().write(i as u8);
32+
}
33+
}
34+
#[allow(static_mut_refs)] // TODO: Fix this
35+
let src = unsafe { SOURCE_BYTES.assume_init_ref() };
36+
37+
let dest =
38+
unsafe { (*core::ptr::addr_of_mut!(DEST_BYTES)).assume_init_mut() };
39+
40+
dest.fill(0);
41+
42+
(src, dest)
43+
}
44+
45+
fn u32_to_u32_transform() -> (&'static [u32; 10], &'static mut [u32; 10]) {
46+
let buf: &mut [MaybeUninit<u32>; 10] = unsafe {
47+
&mut *(core::ptr::addr_of_mut!(SOURCE_WORDS)
48+
as *mut [MaybeUninit<u32>; 10])
49+
};
50+
51+
buf.fill(MaybeUninit::new(0x12345678));
52+
53+
#[allow(static_mut_refs)] // TODO: Fix this
54+
let src = unsafe { SOURCE_WORDS.assume_init_ref() };
55+
56+
let dest =
57+
unsafe { (*core::ptr::addr_of_mut!(DEST_WORDS)).assume_init_mut() };
58+
59+
dest.fill(0);
60+
(src, dest)
61+
}
62+
63+
fn u32_to_u16_truncate() -> (&'static [u32; 10], &'static mut [u16; 20]) {
64+
let buf: &mut [MaybeUninit<u32>; 10] = unsafe {
65+
&mut *(core::ptr::addr_of_mut!(SOURCE_WORDS)
66+
as *mut [MaybeUninit<u32>; 10])
67+
};
68+
69+
buf.fill(MaybeUninit::new(0x12345678));
70+
71+
#[allow(static_mut_refs)] // TODO: Fix this
72+
let src = unsafe { SOURCE_WORDS.assume_init_ref() };
73+
74+
let dest = unsafe {
75+
(*core::ptr::addr_of_mut!(DEST_HALF_WORDS)).assume_init_mut()
76+
};
77+
78+
dest.fill(0);
79+
(src, dest)
80+
}
81+
82+
fn u32_to_u8_unpack() -> (&'static [u32; 10], &'static mut [u8; 40]) {
83+
let buf: &mut [MaybeUninit<u32>; 10] = unsafe {
84+
&mut *(core::ptr::addr_of_mut!(SOURCE_WORDS)
85+
as *mut [MaybeUninit<u32>; 10])
86+
};
87+
88+
buf.fill(MaybeUninit::new(0x12345678));
89+
90+
#[allow(static_mut_refs)] // TODO: Fix this
91+
let src = unsafe { SOURCE_WORDS.assume_init_ref() };
92+
93+
let dest =
94+
unsafe { (*core::ptr::addr_of_mut!(DEST_BYTES)).assume_init_mut() };
95+
96+
dest.fill(0);
97+
(src, dest)
98+
}
99+
100+
fn u8_to_u32_pack() -> (&'static [u8; 40], &'static mut [u32; 10]) {
101+
let buf: &mut [MaybeUninit<u8>; 40] = unsafe {
102+
&mut *(core::ptr::addr_of_mut!(SOURCE_BYTES)
103+
as *mut [MaybeUninit<u8>; 40])
104+
};
105+
106+
for chunk in buf.chunks_mut(4) {
107+
unsafe {
108+
chunk[0].as_mut_ptr().write(0x78);
109+
chunk[1].as_mut_ptr().write(0x56);
110+
chunk[2].as_mut_ptr().write(0x34);
111+
chunk[3].as_mut_ptr().write(0x12);
112+
}
113+
}
114+
115+
#[allow(static_mut_refs)] // TODO: Fix this
116+
let src = unsafe { SOURCE_BYTES.assume_init_ref() };
117+
118+
let dest =
119+
unsafe { (*core::ptr::addr_of_mut!(DEST_WORDS)).assume_init_mut() };
120+
121+
dest.fill(0);
122+
(src, dest)
123+
}
124+
125+
#[entry]
126+
fn main() -> ! {
127+
utilities::logger::init();
128+
129+
let dp = pac::Peripherals::take().unwrap();
130+
131+
let pwr = dp.PWR.constrain();
132+
let pwrcfg = pwr.vos0().freeze();
133+
134+
// Constrain and Freeze clock
135+
let rcc = dp.RCC.constrain();
136+
let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS);
137+
138+
let channels = dp.GPDMA1.channels(ccdr.peripheral.GPDMA1);
139+
140+
let (source_buf, dest_buf) = u8_to_u8_sequential();
141+
142+
let channel = channels.0;
143+
let config = DmaConfig::new();
144+
let transfer =
145+
DmaTransfer::memory_to_memory(config, &channel, source_buf, dest_buf);
146+
transfer.start().unwrap();
147+
transfer.wait_for_transfer_complete().unwrap();
148+
assert_eq!(source_buf, dest_buf);
149+
150+
let (source_buf, dest_buf) = u32_to_u32_transform();
151+
let config = DmaConfig::new().with_data_transform(
152+
DataTransform::builder()
153+
.swap_destination_half_words()
154+
.swap_destination_half_word_byte_order(),
155+
);
156+
157+
let transfer =
158+
DmaTransfer::memory_to_memory(config, &channel, source_buf, dest_buf);
159+
160+
transfer.start().unwrap();
161+
transfer.wait_for_transfer_complete().unwrap();
162+
let expected = [0x78563412; 10];
163+
assert_eq!(expected, *dest_buf);
164+
165+
let (source_buf, dest_buf) = u32_to_u16_truncate();
166+
let config = DmaConfig::new().with_data_transform(
167+
DataTransform::builder().left_align_right_truncate(),
168+
);
169+
let transfer =
170+
DmaTransfer::memory_to_memory(config, &channel, source_buf, dest_buf);
171+
172+
transfer.start().unwrap();
173+
transfer.wait_for_transfer_complete().unwrap();
174+
let expected = [0x1234; 10];
175+
assert_eq!(expected, (*dest_buf)[0..10]);
176+
177+
let (source_buf, dest_buf) = u32_to_u8_unpack();
178+
let config =
179+
DmaConfig::new().with_data_transform(DataTransform::builder().unpack());
180+
let transfer =
181+
DmaTransfer::memory_to_memory(config, &channel, source_buf, dest_buf);
182+
183+
transfer.start().unwrap();
184+
transfer.wait_for_transfer_complete().unwrap();
185+
let expected = [0x78, 0x56, 0x34, 0x12];
186+
assert_eq!(expected, (*dest_buf)[0..4]);
187+
assert_eq!(expected, (*dest_buf)[36..40]);
188+
189+
let (source_buf, dest_buf) = u8_to_u32_pack();
190+
let config =
191+
DmaConfig::new().with_data_transform(DataTransform::builder().pack());
192+
let transfer =
193+
DmaTransfer::memory_to_memory(config, &channel, source_buf, dest_buf);
194+
195+
transfer.start().unwrap();
196+
transfer.wait_for_transfer_complete().unwrap();
197+
let expected = [0x12345678; 10];
198+
assert_eq!(expected, (*dest_buf));
199+
assert_eq!(expected, (*dest_buf));
200+
201+
loop {
202+
debug::exit(debug::EXIT_SUCCESS)
203+
}
204+
}

0 commit comments

Comments
 (0)