-
Notifications
You must be signed in to change notification settings - Fork 842
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
379 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
[target.thumbv7em-none-eabihf] | ||
runner = 'probe-rs run --chip STM32H723ZGTx' | ||
|
||
[build] | ||
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) | ||
|
||
[env] | ||
DEFMT_LOG = "trace" |
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,69 @@ | ||
[package] | ||
edition = "2021" | ||
name = "embassy-stm32h7-examples" | ||
version = "0.1.0" | ||
license = "MIT OR Apache-2.0" | ||
|
||
[dependencies] | ||
# Change stm32h723zg to your chip name, if necessary. | ||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h723zg", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } | ||
embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | ||
embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | ||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | ||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||
|
||
defmt = "0.3" | ||
defmt-rtt = "0.4" | ||
|
||
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
cortex-m-rt = "0.7.0" | ||
embedded-hal = "0.2.6" | ||
embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | ||
embedded-hal-async = { version = "1.0" } | ||
embedded-nal-async = "0.8.0" | ||
embedded-io-async = { version = "0.6.1" } | ||
panic-probe = { version = "0.3", features = ["print-defmt"] } | ||
heapless = { version = "0.8", default-features = false } | ||
rand_core = "0.6.3" | ||
critical-section = "1.1" | ||
static_cell = "2" | ||
chrono = { version = "^0.4", default-features = false } | ||
grounded = "0.2.0" | ||
|
||
# cargo build/run | ||
[profile.dev] | ||
codegen-units = 1 | ||
debug = 2 | ||
debug-assertions = true # <- | ||
incremental = false | ||
opt-level = 3 # <- | ||
overflow-checks = true # <- | ||
|
||
# cargo test | ||
[profile.test] | ||
codegen-units = 1 | ||
debug = 2 | ||
debug-assertions = true # <- | ||
incremental = false | ||
opt-level = 3 # <- | ||
overflow-checks = true # <- | ||
|
||
# cargo build/run --release | ||
[profile.release] | ||
codegen-units = 1 | ||
debug = 2 | ||
debug-assertions = false # <- | ||
incremental = false | ||
lto = 'fat' | ||
opt-level = 3 # <- | ||
overflow-checks = false # <- | ||
|
||
# cargo test --release | ||
[profile.bench] | ||
codegen-units = 1 | ||
debug = 2 | ||
debug-assertions = false # <- | ||
incremental = false | ||
lto = 'fat' | ||
opt-level = 3 # <- | ||
overflow-checks = false # <- |
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,35 @@ | ||
//! 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() { | ||
// 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=-Tlink.x"); | ||
println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
} |
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,106 @@ | ||
MEMORY | ||
{ | ||
/* This file is intended for parts in the STM32H723 family. (RM0468) */ | ||
/* - FLASH and RAM are mandatory memory sections. */ | ||
/* - The sum of all non-FLASH sections must add to 564k total device RAM. */ | ||
/* - The FLASH section size must match your device, see table below. */ | ||
|
||
/* FLASH */ | ||
/* Select the appropriate FLASH size for your device. */ | ||
/* - STM32H730xB 128K */ | ||
/* - STM32H723xE/725xE 512K */ | ||
/* - STM32H723xG/725xG/733xG/735xG 1M */ | ||
FLASH1 : ORIGIN = 0x08000000, LENGTH = 1M | ||
|
||
/* Data TCM */ | ||
/* - Two contiguous 64KB RAMs. */ | ||
/* - Used for interrupt handlers, stacks and general RAM. */ | ||
/* - Zero wait-states. */ | ||
/* - The DTCM is taken as the origin of the base ram. (See below.) */ | ||
/* This is also where the interrupt table and such will live, */ | ||
/* which is required for deterministic performance. */ | ||
DTCM : ORIGIN = 0x20000000, LENGTH = 128K | ||
|
||
/* Instruction TCM */ | ||
/* - More memory can be assigned to ITCM. See AXI SRAM notes, below. */ | ||
/* - Used for latency-critical interrupt handlers etc. */ | ||
/* - Zero wait-states. */ | ||
ITCM : ORIGIN = 0x00000000, LENGTH = 64K + 0K | ||
|
||
/* AXI SRAM */ | ||
/* - AXISRAM is in D1 and accessible by all system masters except BDMA. */ | ||
/* - Suitable for application data not stored in DTCM. */ | ||
/* - Zero wait-states. */ | ||
/* - The 192k of extra shared RAM is fully allotted to the AXI SRAM by default. */ | ||
/* As a result: 64k (64k + 0k) for ITCM and 320k (128k + 192k) for AXI SRAM. */ | ||
/* This can be re-configured via the TCM_AXI_SHARED[1,0] register when more */ | ||
/* ITCM is required. */ | ||
AXISRAM : ORIGIN = 0x24000000, LENGTH = 128K + 192K | ||
|
||
/* AHB SRAM */ | ||
/* - SRAM1-2 are in D2 and accessible by all system masters except BDMA, LTDC */ | ||
/* and SDMMC1. Suitable for use as DMA buffers. */ | ||
/* - SRAM4 is in D3 and additionally accessible by the BDMA. Used for BDMA */ | ||
/* buffers, for storing application data in lower-power modes. */ | ||
/* - Zero wait-states. */ | ||
SRAM1 : ORIGIN = 0x30000000, LENGTH = 16K | ||
SRAM2 : ORIGIN = 0x30040000, LENGTH = 16K | ||
SRAM4 : ORIGIN = 0x38000000, LENGTH = 16K | ||
|
||
/* Backup SRAM */ | ||
/* Used to store data during low-power sleeps. */ | ||
BSRAM : ORIGIN = 0x38800000, LENGTH = 4K | ||
} | ||
|
||
/* | ||
/* Assign the memory regions defined above for use. */ | ||
/* | ||
|
||
/* Provide the mandatory FLASH and RAM definitions for cortex-m-rt's linker script. */ | ||
REGION_ALIAS(FLASH, FLASH1); | ||
REGION_ALIAS(RAM, DTCM); | ||
|
||
/* The location of the stack can be overridden using the `_stack_start` symbol. */ | ||
/* - Set the stack location at the end of RAM, using all remaining space. */ | ||
_stack_start = ORIGIN(RAM) + LENGTH(RAM); | ||
|
||
/* The location of the .text section can be overridden using the */ | ||
/* `_stext` symbol. By default it will place after .vector_table. */ | ||
/* _stext = ORIGIN(FLASH) + 0x40c; */ | ||
|
||
/* Define sections for placing symbols into the extra memory regions above. */ | ||
/* This makes them accessible from code. */ | ||
/* - ITCM, DTCM and AXISRAM connect to a 64-bit wide bus -> align to 8 bytes. */ | ||
/* - All other memories connect to a 32-bit wide bus -> align to 4 bytes. */ | ||
SECTIONS { | ||
.itcm (NOLOAD) : ALIGN(8) { | ||
*(.itcm .itcm.*); | ||
. = ALIGN(8); | ||
} > ITCM | ||
|
||
.axisram (NOLOAD) : ALIGN(8) { | ||
*(.axisram .axisram.*); | ||
. = ALIGN(8); | ||
} > AXISRAM | ||
|
||
.sram1 (NOLOAD) : ALIGN(4) { | ||
*(.sram1 .sram1.*); | ||
. = ALIGN(4); | ||
} > SRAM1 | ||
|
||
.sram2 (NOLOAD) : ALIGN(4) { | ||
*(.sram2 .sram2.*); | ||
. = ALIGN(4); | ||
} > SRAM2 | ||
|
||
.sram4 (NOLOAD) : ALIGN(4) { | ||
*(.sram4 .sram4.*); | ||
. = ALIGN(4); | ||
} > SRAM4 | ||
|
||
.bsram (NOLOAD) : ALIGN(4) { | ||
*(.bsram .bsram.*); | ||
. = ALIGN(4); | ||
} > BSRAM | ||
|
||
}; |
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,161 @@ | ||
//! This example receives inputs on SPDIFRX and outputs on SAI4. | ||
//! | ||
//! Only very few controllers connect the SPDIFRX symbol clock to a SAI peripheral's clock input. | ||
//! However, this is necessary for synchronizing the symbol rates and avoiding glitches. | ||
#![no_std] | ||
#![no_main] | ||
|
||
use defmt::{info, trace}; | ||
use embassy_executor::Spawner; | ||
use embassy_stm32::spdifrx::{self, Spdifrx}; | ||
use embassy_stm32::{bind_interrupts, peripherals, sai}; | ||
use grounded::uninit::GroundedArrayCell; | ||
use hal::sai::*; | ||
use {defmt_rtt as _, embassy_stm32 as hal, panic_probe as _}; | ||
|
||
bind_interrupts!(struct Irqs { | ||
SPDIF_RX => spdifrx::GlobalInterruptHandler<peripherals::SPDIFRX1>; | ||
}); | ||
|
||
const CHANNEL_COUNT: usize = 2; | ||
const BLOCK_LENGTH: usize = 64; | ||
const HALF_DMA_BUFFER_LENGTH: usize = BLOCK_LENGTH * CHANNEL_COUNT; | ||
const DMA_BUFFER_LENGTH: usize = HALF_DMA_BUFFER_LENGTH * 2; // 2 half-blocks | ||
|
||
// DMA buffers must be in special regions. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions | ||
#[link_section = ".sram1"] | ||
static mut SPDIFRX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit(); | ||
|
||
#[link_section = ".sram4"] | ||
static mut SAI_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit(); | ||
|
||
#[embassy_executor::main] | ||
async fn main(_spawner: Spawner) { | ||
let mut peripheral_config = embassy_stm32::Config::default(); | ||
{ | ||
use embassy_stm32::rcc::*; | ||
peripheral_config.rcc.hsi = Some(HSIPrescaler::DIV1); | ||
peripheral_config.rcc.pll1 = Some(Pll { | ||
source: PllSource::HSI, | ||
prediv: PllPreDiv::DIV16, | ||
mul: PllMul::MUL200, | ||
divp: Some(PllDiv::DIV2), // 400 MHz | ||
divq: Some(PllDiv::DIV2), | ||
divr: Some(PllDiv::DIV2), | ||
}); | ||
peripheral_config.rcc.sys = Sysclk::PLL1_P; | ||
peripheral_config.rcc.ahb_pre = AHBPrescaler::DIV2; | ||
peripheral_config.rcc.apb1_pre = APBPrescaler::DIV2; | ||
peripheral_config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
peripheral_config.rcc.apb3_pre = APBPrescaler::DIV2; | ||
peripheral_config.rcc.apb4_pre = APBPrescaler::DIV2; | ||
|
||
peripheral_config.rcc.mux.spdifrxsel = mux::Spdifrxsel::PLL1_Q; | ||
} | ||
let mut p = embassy_stm32::init(peripheral_config); | ||
|
||
info!("SPDIFRX to SAI4 bridge"); | ||
|
||
// Use SPDIFRX clock for SAI. | ||
// This ensures equal rates of sample production and consumption. | ||
let clk_source = embassy_stm32::pac::rcc::vals::Saiasel::_RESERVED_5; | ||
embassy_stm32::pac::RCC.d3ccipr().modify(|w| { | ||
w.set_sai4asel(clk_source); | ||
}); | ||
|
||
let sai_buffer: &mut [u32] = unsafe { | ||
SAI_BUFFER.initialize_all_copied(0); | ||
let (ptr, len) = SAI_BUFFER.get_ptr_len(); | ||
core::slice::from_raw_parts_mut(ptr, len) | ||
}; | ||
|
||
let spdifrx_buffer: &mut [u32] = unsafe { | ||
SPDIFRX_BUFFER.initialize_all_copied(0); | ||
let (ptr, len) = SPDIFRX_BUFFER.get_ptr_len(); | ||
core::slice::from_raw_parts_mut(ptr, len) | ||
}; | ||
|
||
let mut spdif_receiver = new_spdif_receiver(&mut p.SPDIFRX1, &mut p.PD7, &mut p.DMA2_CH7, spdifrx_buffer); | ||
let mut sai_transmitter = new_sai_transmitter( | ||
&mut p.SAI4, | ||
&mut p.PD13, | ||
&mut p.PC1, | ||
&mut p.PD12, | ||
&mut p.BDMA_CH0, | ||
sai_buffer, | ||
); | ||
|
||
spdif_receiver.start(); | ||
sai_transmitter.start(); | ||
|
||
loop { | ||
let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH]; | ||
|
||
match spdif_receiver.read_data(&mut buf).await { | ||
Ok(_) => (), | ||
Err(spdifrx::Error::RingbufferError(_)) => { | ||
trace!("SPDIFRX ringbuffer error. Renew."); | ||
drop(spdif_receiver); | ||
spdif_receiver = new_spdif_receiver(&mut p.SPDIFRX1, &mut p.PD7, &mut p.DMA2_CH7, spdifrx_buffer); | ||
spdif_receiver.start(); | ||
continue; | ||
} | ||
Err(spdifrx::Error::ChannelSyncError) => { | ||
trace!("SPDIFRX channel sync (left/right assignment) error."); | ||
continue; | ||
} | ||
Err(spdifrx::Error::SourceSyncError) => { | ||
trace!("SPDIFRX source sync error, e.g. disconnect."); | ||
continue; | ||
} | ||
}; | ||
|
||
if sai_transmitter.write(&buf).await.is_err() { | ||
trace!("Renew SAI."); | ||
drop(sai_transmitter); | ||
sai_transmitter = new_sai_transmitter( | ||
&mut p.SAI4, | ||
&mut p.PD13, | ||
&mut p.PC1, | ||
&mut p.PD12, | ||
&mut p.BDMA_CH0, | ||
sai_buffer, | ||
); | ||
sai_transmitter.start(); | ||
} | ||
} | ||
} | ||
|
||
/// Creates a new SPDIFRX instance for receiving sample data. | ||
/// | ||
/// Used (again) after dropping the SPDIFRX instance, in case of errors (e.g. source disconnect). | ||
fn new_spdif_receiver<'d>( | ||
spdifrx: &'d mut peripherals::SPDIFRX1, | ||
input_pin: &'d mut peripherals::PD7, | ||
dma: &'d mut peripherals::DMA2_CH7, | ||
buf: &'d mut [u32], | ||
) -> Spdifrx<'d, peripherals::SPDIFRX1> { | ||
Spdifrx::new_data_only(spdifrx, Irqs, spdifrx::Config::default(), input_pin, dma, buf) | ||
} | ||
|
||
/// Creates a new SAI4 instance for transmitting sample data. | ||
/// | ||
/// Used (again) after dropping the SAI4 instance, in case of errors (e.g. buffer overrun). | ||
fn new_sai_transmitter<'d>( | ||
sai: &'d mut peripherals::SAI4, | ||
sck: &'d mut peripherals::PD13, | ||
sd: &'d mut peripherals::PC1, | ||
fs: &'d mut peripherals::PD12, | ||
dma: &'d mut peripherals::BDMA_CH0, | ||
buf: &'d mut [u32], | ||
) -> Sai<'d, peripherals::SAI4, u32> { | ||
let mut sai_config = hal::sai::Config::default(); | ||
sai_config.slot_count = hal::sai::word::U4(CHANNEL_COUNT as u8); | ||
sai_config.slot_enable = 0xFFFF; // All slots | ||
sai_config.data_size = sai::DataSize::Data32; | ||
sai_config.frame_length = (CHANNEL_COUNT * 32) as u8; | ||
sai_config.master_clock_divider = hal::sai::MasterClockDivider::MasterClockDisabled; | ||
|
||
let (sub_block_tx, _) = hal::sai::split_subblocks(sai); | ||
Sai::new_asynchronous(sub_block_tx, sck, sd, fs, dma, buf, sai_config) | ||
} |