-
Notifications
You must be signed in to change notification settings - Fork 105
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <962920+richardeoin@users.noreply.github.com> Co-authored-by: Antoine van Gelder <antoine@flowdsp.io> Co-authored-by: Richard Meadows <richardeoin@gmail.com> Co-authored-by: Ryan Summers <ryan.summers@vertigo-designs.com> Co-authored-by: Robert Jördens <rj@quartiq.de>
- Loading branch information
Showing
18 changed files
with
3,619 additions
and
582 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
//! Example of Memory to Memory Transfer with the DMA | ||
#![allow(clippy::transmute_ptr_to_ptr)] | ||
#![deny(warnings)] | ||
#![no_main] | ||
#![no_std] | ||
|
||
use core::{mem, mem::MaybeUninit}; | ||
|
||
use cortex_m_rt::entry; | ||
#[macro_use] | ||
mod utilities; | ||
use stm32h7xx_hal::{pac, prelude::*}; | ||
|
||
use stm32h7xx_hal::dma::{ | ||
dma::{DmaConfig, StreamsTuple}, | ||
traits::Direction, | ||
MemoryToMemory, Transfer, | ||
}; | ||
|
||
use log::info; | ||
|
||
// DMA1/DMA2 cannot interact with our stack. Instead, buffers for use with the | ||
// DMA must be placed somewhere that DMA1/DMA2 can access. | ||
// | ||
// The runtime does not initialise these SRAM banks. | ||
#[link_section = ".sram4.buffers"] | ||
static mut SOURCE_BUFFER: MaybeUninit<[u32; 20]> = MaybeUninit::uninit(); | ||
#[link_section = ".axisram.buffers"] | ||
static mut TARGET_BUFFER: MaybeUninit<[u32; 20]> = MaybeUninit::uninit(); | ||
|
||
#[entry] | ||
fn main() -> ! { | ||
utilities::logger::init(); | ||
let dp = pac::Peripherals::take().unwrap(); | ||
|
||
// Constrain and Freeze power | ||
info!("Setup PWR... "); | ||
let pwr = dp.PWR.constrain(); | ||
let pwrcfg = example_power!(pwr).freeze(); | ||
|
||
// Constrain and Freeze clock | ||
info!("Setup RCC... "); | ||
let rcc = dp.RCC.constrain(); | ||
let ccdr = rcc | ||
.sys_ck(400.mhz()) | ||
.pll1_q_ck(200.mhz()) | ||
.freeze(pwrcfg, &dp.SYSCFG); | ||
|
||
// True RNG | ||
let mut rng = dp.RNG.constrain(ccdr.peripheral.RNG, &ccdr.clocks); | ||
|
||
info!(""); | ||
info!("stm32h7xx-hal example - Memory to Memory DMA"); | ||
info!(""); | ||
|
||
// Initialise the source buffer with truly random data, without taking any | ||
// references to uninitialisated memory | ||
let source_buffer: &'static mut [u32; 20] = { | ||
let buf: &mut [MaybeUninit<u32>; 20] = | ||
unsafe { mem::transmute(&mut SOURCE_BUFFER) }; | ||
|
||
for value in buf.iter_mut() { | ||
unsafe { | ||
value.as_mut_ptr().write(rng.gen().unwrap()); | ||
} | ||
} | ||
unsafe { mem::transmute(buf) } | ||
}; | ||
// Save a copy on the stack so we can check it later | ||
let source_buffer_cloned = *source_buffer; | ||
|
||
// Setup DMA | ||
// | ||
// We need to specify the transfer size with a type annotation | ||
|
||
let streams = StreamsTuple::new(dp.DMA1, ccdr.peripheral.DMA1); | ||
|
||
let config = DmaConfig::default() | ||
.memory_increment(true) // destination mem | ||
.peripheral_increment(true) // source mem | ||
.fifo_enable(true); | ||
|
||
let mut transfer: Transfer<_, _, MemoryToMemory<u32>, _, _> = | ||
Transfer::init( | ||
streams.4, | ||
MemoryToMemory::new(), | ||
unsafe { mem::transmute(&mut TARGET_BUFFER) }, // Uninitialised memory | ||
Some(source_buffer), | ||
config, | ||
); | ||
|
||
transfer.start(|_| {}); | ||
|
||
// Wait for transfer to complete | ||
while !transfer.get_transfer_complete_flag() {} | ||
|
||
// Now the target memory is actually initialised | ||
let target_buffer: &'static mut [u32; 20] = | ||
unsafe { mem::transmute(&mut TARGET_BUFFER) }; | ||
|
||
// Comparison check | ||
assert_eq!(&source_buffer_cloned, target_buffer); | ||
|
||
info!("Memory to Memory DMA completed successfully"); | ||
|
||
loop {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
//! I2C4 in low power mode. | ||
//! | ||
//! | ||
#![allow(clippy::transmute_ptr_to_ptr)] | ||
#![deny(warnings)] | ||
#![no_std] | ||
#![no_main] | ||
|
||
use core::{mem, mem::MaybeUninit}; | ||
|
||
#[macro_use] | ||
mod utilities; | ||
|
||
use stm32h7xx_hal::dma::{ | ||
bdma::{BdmaConfig, StreamsTuple}, | ||
PeripheralToMemory, Transfer, | ||
}; | ||
|
||
use stm32h7xx_hal::prelude::*; | ||
use stm32h7xx_hal::{i2c, pac, pac::interrupt, rcc::LowPowerMode}; | ||
|
||
use cortex_m_rt::entry; | ||
|
||
use log::info; | ||
|
||
// The BDMA can only interact with SRAM4. | ||
// | ||
// The runtime does not initialise this SRAM bank | ||
#[link_section = ".sram4.buffers"] | ||
static mut BUFFER: MaybeUninit<[u8; 10]> = MaybeUninit::uninit(); | ||
|
||
#[entry] | ||
fn main() -> ! { | ||
utilities::logger::init(); | ||
let cp = cortex_m::Peripherals::take().unwrap(); | ||
let dp = pac::Peripherals::take().expect("Cannot take peripherals"); | ||
|
||
// Run D3 / SRD domain | ||
dp.PWR.cpucr.modify(|_, w| w.run_d3().set_bit()); | ||
|
||
let pwr = dp.PWR.constrain(); | ||
let pwrcfg = example_power!(pwr).freeze(); | ||
|
||
// RCC | ||
let rcc = dp.RCC.constrain(); | ||
let ccdr = rcc | ||
.sys_ck(400.mhz()) | ||
// D3 / SRD domain | ||
.hclk(200.mhz()) // rcc_hclk4 | ||
.pclk4(50.mhz()) // rcc_pclk4 | ||
.freeze(pwrcfg, &dp.SYSCFG); | ||
|
||
// GPIO | ||
let gpiod = dp.GPIOD.split(ccdr.peripheral.GPIOD); | ||
|
||
// Configure the SCL and the SDA pin for our I2C bus | ||
let scl = gpiod.pd12.into_alternate_af4().set_open_drain(); | ||
let sda = gpiod.pd13.into_alternate_af4().set_open_drain(); | ||
|
||
let mut i2c = dp.I2C4.i2c( | ||
(scl, sda), | ||
100.khz(), | ||
ccdr.peripheral.I2C4.low_power(LowPowerMode::Autonomous), | ||
&ccdr.clocks, | ||
); | ||
|
||
// Use RX DMA | ||
i2c.rx_dma(true); | ||
|
||
// Listen for the end of i2c transactions | ||
i2c.clear_irq(i2c::Event::Stop); | ||
i2c.listen(i2c::Event::Stop); | ||
unsafe { | ||
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::I2C4_EV); | ||
} | ||
|
||
// Setup the DMA transfer on stream 0 | ||
#[cfg(not(feature = "rm0455"))] | ||
let streams = StreamsTuple::new( | ||
dp.BDMA, | ||
ccdr.peripheral.BDMA.low_power(LowPowerMode::Autonomous), | ||
); | ||
#[cfg(feature = "rm0455")] | ||
let streams = StreamsTuple::new( | ||
dp.BDMA2, | ||
ccdr.peripheral.BDMA.low_power(LowPowerMode::Autonomous), | ||
); | ||
|
||
let config = BdmaConfig::default().memory_increment(true); | ||
|
||
// We need to specify the direction with a type annotation | ||
let mut transfer: Transfer<_, _, PeripheralToMemory, _, _> = Transfer::init( | ||
streams.0, | ||
i2c, | ||
unsafe { &mut BUFFER }, // uninitialised memory | ||
None, | ||
config, | ||
); | ||
|
||
transfer.start(|i2c| { | ||
// This closure runs right after enabling the stream | ||
|
||
// Issue the first part of an I2C transaction to read data from a | ||
// touchscreen | ||
|
||
// Write register index | ||
i2c.write(0xBA >> 1, &[0x41, 0xE4]).unwrap(); | ||
|
||
// Start a read of 10 bytes | ||
i2c.master_read(0xBA >> 1, 10, i2c::Stop::Automatic); | ||
}); | ||
|
||
// Enter CStop mode on wfi | ||
let mut scb = cp.SCB; | ||
scb.set_sleepdeep(); | ||
|
||
loop { | ||
cortex_m::asm::wfi(); | ||
} | ||
} | ||
|
||
#[interrupt] | ||
fn I2C4_EV() { | ||
info!("I2C transfer complete!"); | ||
|
||
// Look at BUFFER, which we expect to be initialised | ||
let buffer: &'static mut [u8; 10] = unsafe { mem::transmute(&mut BUFFER) }; | ||
|
||
assert_eq!(buffer[0], 0xBE); | ||
|
||
loop {} | ||
} |
Oops, something went wrong.