add example for bdma

This commit is contained in:
Pegasis 2024-05-03 08:12:30 -04:00
parent 45a2abc392
commit 3e87dae578
4 changed files with 132 additions and 2 deletions

View File

@ -279,6 +279,14 @@ If you see errors that look like this:
DMA: error on BDMA@1234ABCD channel 4 DMA: error on BDMA@1234ABCD channel 4
---- ----
You likely need to set up your linker script to define a special region for this area, and copy data to that region before using with BDMA. You need to set up your linker script to define a special region for this area, and copy data to that region before using with BDMA.
TODO: show how to do that General steps:
1. Find out which memory region BDMA has access to. You can get this information from the bus matrix and the memory mapping table in the STM32 datasheet.
2. Add the memory region to `memory.x`, you can modify the generated one from https://github.com/embassy-rs/stm32-data-generated/tree/main/data/chips.
3. You might need to modify `build.rs` to make cargo pick up the modified `memory.x`.
4. In your code, access the defined memory region using `#[link_section = ".xxx"]`
5. Copy data to that region before using BDMA.
See link:/examples/stm32h7/src/bin/spi_bdma.rs[this example] for more details.

View File

@ -1,4 +1,34 @@
//! This build script copies the `memory.x` file from the crate root into
//! a directory where the linker can always find it at build time.
//! For many projects this is optional, as the linker always searches the
//! project root directory -- wherever `Cargo.toml` is. However, if you
//! are using a workspace or have a more complicated build setup, this
//! build script becomes required. Additionally, by requesting that
//! Cargo re-run the build script whenever `memory.x` is changed,
//! updating `memory.x` ensures a rebuild of the application with the
//! new memory settings.
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() { fn main() {
// Put `memory.x` in our output directory and ensure it's
// on the linker search path.
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("memory.x"))
.unwrap()
.write_all(include_bytes!("memory.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
// By default, Cargo will re-run a build script whenever
// any file in the project changes. By specifying `memory.x`
// here, we ensure the build script is only re-run when
// `memory.x` is changed.
println!("cargo:rerun-if-changed=memory.x");
println!("cargo:rustc-link-arg-bins=--nmagic"); println!("cargo:rustc-link-arg-bins=--nmagic");
println!("cargo:rustc-link-arg-bins=-Tlink.x"); println!("cargo:rustc-link-arg-bins=-Tlink.x");
println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); println!("cargo:rustc-link-arg-bins=-Tdefmt.x");

14
examples/stm32h7/memory.x Normal file
View File

@ -0,0 +1,14 @@
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 2048K /* BANK_1 + BANK_2 */
RAM : ORIGIN = 0x24000000, LENGTH = 512K /* SRAM */
RAM_D3 : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 */
}
SECTIONS
{
.ram_d3 :
{
*(.ram_d3)
} > RAM_D3
}

View File

@ -0,0 +1,78 @@
#![no_std]
#![no_main]
use core::fmt::Write;
use core::str::from_utf8;
use cortex_m_rt::entry;
use defmt::*;
use embassy_executor::Executor;
use embassy_stm32::mode::Async;
use embassy_stm32::time::mhz;
use embassy_stm32::{peripherals, spi, Config};
use heapless::String;
use static_cell::StaticCell;
use {defmt_rtt as _, panic_probe as _};
// Defined in memory.x
#[link_section = ".ram_d3"]
static mut RAM_D3: [u8; 64 * 1024] = [0u8; 64 * 1024];
#[embassy_executor::task]
async fn main_task(mut spi: spi::Spi<'static, peripherals::SPI6, Async>) {
let read_buffer = unsafe { &mut RAM_D3[0..128] };
let write_buffer = unsafe { &mut RAM_D3[128..256] };
for n in 0u32.. {
let mut write: String<128> = String::new();
core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap();
let read_buffer = &mut read_buffer[..write.len()];
let write_buffer = &mut write_buffer[..write.len()];
// copy data to write_buffer which is located in D3 domain, accessable by BDMA
write_buffer.clone_from_slice(write.as_bytes());
spi.transfer(read_buffer, write_buffer).await.ok();
info!("read via spi+dma: {}", from_utf8(read_buffer).unwrap());
}
}
static EXECUTOR: StaticCell<Executor> = StaticCell::new();
#[entry]
fn main() -> ! {
info!("Hello World!");
let mut config = Config::default();
{
use embassy_stm32::rcc::*;
config.rcc.hsi = Some(HSIPrescaler::DIV1);
config.rcc.csi = true;
config.rcc.pll1 = Some(Pll {
source: PllSource::HSI,
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL50,
divp: Some(PllDiv::DIV2),
divq: Some(PllDiv::DIV8), // used by SPI3. 100Mhz.
divr: None,
});
config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.voltage_scale = VoltageScale::Scale1;
}
let p = embassy_stm32::init(config);
let mut spi_config = spi::Config::default();
spi_config.frequency = mhz(1);
let spi = spi::Spi::new(p.SPI6, p.PA5, p.PA7, p.PA6, p.BDMA_CH1, p.BDMA_CH0, spi_config);
let executor = EXECUTOR.init(Executor::new());
executor.run(|spawner| {
unwrap!(spawner.spawn(main_task(spi)));
})
}