Skip to content

Commit 7d81332

Browse files
bors[bot]richardeoinantoinevgryan-summersjordens
authored
Merge #153
153: Implement DMA based on embedded-dma traits r=richardeoin a=richardeoin DMA implementation based on the [embedded-dma](https://docs.rs/embedded-dma/0.1.2/embedded_dma/) traits and the existing implementation in the [stm32f4xx-hal](https://github.com/stm32-rs/stm32f4xx-hal/tree/master/src/dma). ~Currently some parts are very similar - the `Stream` trait and its implementation are completely identical similar, and much of `dma/mod.rs` is similar. However it remains to be seen how much will stay the same when implementing the BDMA and/or MDMA.~ However some changes are required since we'd like to support the BDMA and MDMA also. Implementation / examples for: - [x] Memory-to-Memory - [x] SPI TX - [x] I2C RX TODO: - [x] BDMA implementation - [ ] Confirm that owning the target peripheral is the right API design - [x] ~Do the compiler fences also need matching synchronisation~ Memory barriers for the Cortex-M7 - [x] Add an example using RTIC - Thanks ryan-summers! Contributions to this branch are very welcome, especially implementations and examples for other peripherals. Ref #80 Co-authored-by: Richard Meadows <[email protected]> Co-authored-by: Antoine van Gelder <[email protected]> Co-authored-by: Richard Meadows <[email protected]> Co-authored-by: Ryan Summers <[email protected]> Co-authored-by: Robert Jördens <[email protected]>
2 parents 5471426 + 2b8a04c commit 7d81332

File tree

18 files changed

+3619
-582
lines changed

18 files changed

+3619
-582
lines changed

Cargo.toml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ targets = ["thumbv7em-none-eabihf"]
2525

2626
[dependencies]
2727
embedded-hal = "0.2.4"
28+
embedded-dma = "0.1.2"
2829
cortex-m = "^0.6.2"
2930
cortex-m-rt = "^0.6.12"
3031
stm32h7 = "^0.12.1"
@@ -172,4 +173,20 @@ required-features = ["rt", "rtc"]
172173

173174
[[example]]
174175
name = "sai-i2s-passthru"
175-
required-features = ["rm0433"]
176+
required-features = ["rm0433"]
177+
178+
[[example]]
179+
name = "sai_dma_passthru"
180+
required-features = ["rm0433"]
181+
182+
[[example]]
183+
name = "dma"
184+
required-features = ["rm0433"]
185+
186+
[[example]]
187+
name = "spi-dma"
188+
required-features = ["rm0433"]
189+
190+
[[example]]
191+
name = "spi-dma-rtic"
192+
required-features = ["rm0433","rt"]

examples/dma.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//! Example of Memory to Memory Transfer with the DMA
2+
3+
#![allow(clippy::transmute_ptr_to_ptr)]
4+
#![deny(warnings)]
5+
#![no_main]
6+
#![no_std]
7+
8+
use core::{mem, mem::MaybeUninit};
9+
10+
use cortex_m_rt::entry;
11+
#[macro_use]
12+
mod utilities;
13+
use stm32h7xx_hal::{pac, prelude::*};
14+
15+
use stm32h7xx_hal::dma::{
16+
dma::{DmaConfig, StreamsTuple},
17+
traits::Direction,
18+
MemoryToMemory, Transfer,
19+
};
20+
21+
use log::info;
22+
23+
// DMA1/DMA2 cannot interact with our stack. Instead, buffers for use with the
24+
// DMA must be placed somewhere that DMA1/DMA2 can access.
25+
//
26+
// The runtime does not initialise these SRAM banks.
27+
#[link_section = ".sram4.buffers"]
28+
static mut SOURCE_BUFFER: MaybeUninit<[u32; 20]> = MaybeUninit::uninit();
29+
#[link_section = ".axisram.buffers"]
30+
static mut TARGET_BUFFER: MaybeUninit<[u32; 20]> = MaybeUninit::uninit();
31+
32+
#[entry]
33+
fn main() -> ! {
34+
utilities::logger::init();
35+
let dp = pac::Peripherals::take().unwrap();
36+
37+
// Constrain and Freeze power
38+
info!("Setup PWR... ");
39+
let pwr = dp.PWR.constrain();
40+
let pwrcfg = example_power!(pwr).freeze();
41+
42+
// Constrain and Freeze clock
43+
info!("Setup RCC... ");
44+
let rcc = dp.RCC.constrain();
45+
let ccdr = rcc
46+
.sys_ck(400.mhz())
47+
.pll1_q_ck(200.mhz())
48+
.freeze(pwrcfg, &dp.SYSCFG);
49+
50+
// True RNG
51+
let mut rng = dp.RNG.constrain(ccdr.peripheral.RNG, &ccdr.clocks);
52+
53+
info!("");
54+
info!("stm32h7xx-hal example - Memory to Memory DMA");
55+
info!("");
56+
57+
// Initialise the source buffer with truly random data, without taking any
58+
// references to uninitialisated memory
59+
let source_buffer: &'static mut [u32; 20] = {
60+
let buf: &mut [MaybeUninit<u32>; 20] =
61+
unsafe { mem::transmute(&mut SOURCE_BUFFER) };
62+
63+
for value in buf.iter_mut() {
64+
unsafe {
65+
value.as_mut_ptr().write(rng.gen().unwrap());
66+
}
67+
}
68+
unsafe { mem::transmute(buf) }
69+
};
70+
// Save a copy on the stack so we can check it later
71+
let source_buffer_cloned = *source_buffer;
72+
73+
// Setup DMA
74+
//
75+
// We need to specify the transfer size with a type annotation
76+
77+
let streams = StreamsTuple::new(dp.DMA1, ccdr.peripheral.DMA1);
78+
79+
let config = DmaConfig::default()
80+
.memory_increment(true) // destination mem
81+
.peripheral_increment(true) // source mem
82+
.fifo_enable(true);
83+
84+
let mut transfer: Transfer<_, _, MemoryToMemory<u32>, _, _> =
85+
Transfer::init(
86+
streams.4,
87+
MemoryToMemory::new(),
88+
unsafe { mem::transmute(&mut TARGET_BUFFER) }, // Uninitialised memory
89+
Some(source_buffer),
90+
config,
91+
);
92+
93+
transfer.start(|_| {});
94+
95+
// Wait for transfer to complete
96+
while !transfer.get_transfer_complete_flag() {}
97+
98+
// Now the target memory is actually initialised
99+
let target_buffer: &'static mut [u32; 20] =
100+
unsafe { mem::transmute(&mut TARGET_BUFFER) };
101+
102+
// Comparison check
103+
assert_eq!(&source_buffer_cloned, target_buffer);
104+
105+
info!("Memory to Memory DMA completed successfully");
106+
107+
loop {}
108+
}

examples/i2c4_bdma.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
//! I2C4 in low power mode.
2+
//!
3+
//!
4+
5+
#![allow(clippy::transmute_ptr_to_ptr)]
6+
#![deny(warnings)]
7+
#![no_std]
8+
#![no_main]
9+
10+
use core::{mem, mem::MaybeUninit};
11+
12+
#[macro_use]
13+
mod utilities;
14+
15+
use stm32h7xx_hal::dma::{
16+
bdma::{BdmaConfig, StreamsTuple},
17+
PeripheralToMemory, Transfer,
18+
};
19+
20+
use stm32h7xx_hal::prelude::*;
21+
use stm32h7xx_hal::{i2c, pac, pac::interrupt, rcc::LowPowerMode};
22+
23+
use cortex_m_rt::entry;
24+
25+
use log::info;
26+
27+
// The BDMA can only interact with SRAM4.
28+
//
29+
// The runtime does not initialise this SRAM bank
30+
#[link_section = ".sram4.buffers"]
31+
static mut BUFFER: MaybeUninit<[u8; 10]> = MaybeUninit::uninit();
32+
33+
#[entry]
34+
fn main() -> ! {
35+
utilities::logger::init();
36+
let cp = cortex_m::Peripherals::take().unwrap();
37+
let dp = pac::Peripherals::take().expect("Cannot take peripherals");
38+
39+
// Run D3 / SRD domain
40+
dp.PWR.cpucr.modify(|_, w| w.run_d3().set_bit());
41+
42+
let pwr = dp.PWR.constrain();
43+
let pwrcfg = example_power!(pwr).freeze();
44+
45+
// RCC
46+
let rcc = dp.RCC.constrain();
47+
let ccdr = rcc
48+
.sys_ck(400.mhz())
49+
// D3 / SRD domain
50+
.hclk(200.mhz()) // rcc_hclk4
51+
.pclk4(50.mhz()) // rcc_pclk4
52+
.freeze(pwrcfg, &dp.SYSCFG);
53+
54+
// GPIO
55+
let gpiod = dp.GPIOD.split(ccdr.peripheral.GPIOD);
56+
57+
// Configure the SCL and the SDA pin for our I2C bus
58+
let scl = gpiod.pd12.into_alternate_af4().set_open_drain();
59+
let sda = gpiod.pd13.into_alternate_af4().set_open_drain();
60+
61+
let mut i2c = dp.I2C4.i2c(
62+
(scl, sda),
63+
100.khz(),
64+
ccdr.peripheral.I2C4.low_power(LowPowerMode::Autonomous),
65+
&ccdr.clocks,
66+
);
67+
68+
// Use RX DMA
69+
i2c.rx_dma(true);
70+
71+
// Listen for the end of i2c transactions
72+
i2c.clear_irq(i2c::Event::Stop);
73+
i2c.listen(i2c::Event::Stop);
74+
unsafe {
75+
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::I2C4_EV);
76+
}
77+
78+
// Setup the DMA transfer on stream 0
79+
#[cfg(not(feature = "rm0455"))]
80+
let streams = StreamsTuple::new(
81+
dp.BDMA,
82+
ccdr.peripheral.BDMA.low_power(LowPowerMode::Autonomous),
83+
);
84+
#[cfg(feature = "rm0455")]
85+
let streams = StreamsTuple::new(
86+
dp.BDMA2,
87+
ccdr.peripheral.BDMA.low_power(LowPowerMode::Autonomous),
88+
);
89+
90+
let config = BdmaConfig::default().memory_increment(true);
91+
92+
// We need to specify the direction with a type annotation
93+
let mut transfer: Transfer<_, _, PeripheralToMemory, _, _> = Transfer::init(
94+
streams.0,
95+
i2c,
96+
unsafe { &mut BUFFER }, // uninitialised memory
97+
None,
98+
config,
99+
);
100+
101+
transfer.start(|i2c| {
102+
// This closure runs right after enabling the stream
103+
104+
// Issue the first part of an I2C transaction to read data from a
105+
// touchscreen
106+
107+
// Write register index
108+
i2c.write(0xBA >> 1, &[0x41, 0xE4]).unwrap();
109+
110+
// Start a read of 10 bytes
111+
i2c.master_read(0xBA >> 1, 10, i2c::Stop::Automatic);
112+
});
113+
114+
// Enter CStop mode on wfi
115+
let mut scb = cp.SCB;
116+
scb.set_sleepdeep();
117+
118+
loop {
119+
cortex_m::asm::wfi();
120+
}
121+
}
122+
123+
#[interrupt]
124+
fn I2C4_EV() {
125+
info!("I2C transfer complete!");
126+
127+
// Look at BUFFER, which we expect to be initialised
128+
let buffer: &'static mut [u8; 10] = unsafe { mem::transmute(&mut BUFFER) };
129+
130+
assert_eq!(buffer[0], 0xBE);
131+
132+
loop {}
133+
}

0 commit comments

Comments
 (0)