mirror of
https://github.com/embassy-rs/embassy.git
synced 2025-09-28 04:40:39 +00:00
Rewrite documentation and generally improve it
This commit is contained in:
parent
04c0bd84e6
commit
f28934cb46
@ -1,18 +1,32 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
// IMPORTANT: This must match EXACTLY the definition in CM7!
|
||||
//! STM32H7 Secondary Core (CM4) Intercore Communication Example
|
||||
//!
|
||||
//! This example demonstrates reliable communication between the Cortex-M7 and
|
||||
//! Cortex-M4 cores. This secondary core monitors shared memory for LED state
|
||||
//! changes and updates the physical LEDs accordingly.
|
||||
//!
|
||||
//! The CM4 core handles:
|
||||
//! - Responding to state changes from CM7
|
||||
//! - Controlling the physical green and yellow LEDs
|
||||
//! - Providing visual feedback via a heartbeat on the red LED
|
||||
//!
|
||||
//! Usage:
|
||||
//! 1. Flash this CM4 (secondary) core binary first
|
||||
//! 2. Then flash the CM7 (primary) core binary
|
||||
//! 3. The red LED should blink continuously as a heartbeat
|
||||
//! 4. Green and yellow LEDs should toggle according to CM7 core signals
|
||||
|
||||
/// Module providing shared memory constructs for intercore communication
|
||||
mod shared {
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
/// Shared LED state between CM7 and CM4 cores
|
||||
/// State shared between CM7 and CM4 cores for LED control
|
||||
#[repr(C, align(4))]
|
||||
pub struct SharedLedState {
|
||||
// Magic number for validation
|
||||
pub magic: AtomicU32,
|
||||
// Counter for synchronization testing
|
||||
pub counter: AtomicU32,
|
||||
// LED states packed into a single atomic
|
||||
pub led_states: AtomicU32,
|
||||
}
|
||||
|
||||
@ -23,19 +37,17 @@ mod shared {
|
||||
impl SharedLedState {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
magic: AtomicU32::new(0xDEADBEEF), // Magic number
|
||||
magic: AtomicU32::new(0xDEADBEEF),
|
||||
counter: AtomicU32::new(0),
|
||||
led_states: AtomicU32::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set LED state using safe bit operations
|
||||
/// Set LED state by manipulating the appropriate bit in the led_states field
|
||||
#[inline(never)]
|
||||
#[allow(dead_code)]
|
||||
pub fn set_led(&self, is_green: bool, state: bool) {
|
||||
let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT };
|
||||
|
||||
// Use bit operations to avoid complex atomic operations
|
||||
let current = self.led_states.load(Ordering::SeqCst);
|
||||
|
||||
let new_value = if state {
|
||||
@ -48,7 +60,7 @@ mod shared {
|
||||
core::sync::atomic::fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
/// Get LED state using safe bit operations
|
||||
/// Get current LED state
|
||||
#[inline(never)]
|
||||
pub fn get_led(&self, is_green: bool) -> bool {
|
||||
let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT };
|
||||
@ -59,7 +71,7 @@ mod shared {
|
||||
(value & (1 << bit)) != 0
|
||||
}
|
||||
|
||||
/// Increment counter safely
|
||||
/// Increment counter and return new value
|
||||
#[inline(never)]
|
||||
#[allow(dead_code)]
|
||||
pub fn increment_counter(&self) -> u32 {
|
||||
@ -70,7 +82,7 @@ mod shared {
|
||||
new_value
|
||||
}
|
||||
|
||||
/// Get counter without incrementing
|
||||
/// Get current counter value
|
||||
#[inline(never)]
|
||||
pub fn get_counter(&self) -> u32 {
|
||||
let value = self.counter.load(Ordering::SeqCst);
|
||||
@ -84,19 +96,18 @@ mod shared {
|
||||
}
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::gpio::{Level, Output, Speed};
|
||||
use embassy_stm32::SharedData;
|
||||
use embassy_time::Timer;
|
||||
// Use our shared state from the module
|
||||
use shared::SHARED_LED_STATE;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[link_section = ".ram_d3"]
|
||||
static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();
|
||||
|
||||
/// Task that continuously blinks the red LED as a heartbeat indicator
|
||||
#[embassy_executor::task]
|
||||
async fn blink_heartbeat(mut led: Output<'static>) {
|
||||
loop {
|
||||
@ -112,11 +123,11 @@ async fn main(spawner: Spawner) -> ! {
|
||||
let p = embassy_stm32::init_secondary(&SHARED_DATA);
|
||||
info!("CM4 core initialized!");
|
||||
|
||||
// Read the magic value to ensure shared memory is accessible
|
||||
// Verify shared memory is accessible
|
||||
let magic = SHARED_LED_STATE.magic.load(core::sync::atomic::Ordering::SeqCst);
|
||||
info!("CM4: Magic value = 0x{:X}", magic);
|
||||
|
||||
// Initialize LEDs
|
||||
// Set up LEDs
|
||||
let mut green_led = Output::new(p.PB0, Level::Low, Speed::Low); // LD1
|
||||
let mut yellow_led = Output::new(p.PE1, Level::Low, Speed::Low); // LD2
|
||||
let red_led = Output::new(p.PB14, Level::Low, Speed::Low); // LD3 (heartbeat)
|
||||
@ -124,31 +135,30 @@ async fn main(spawner: Spawner) -> ! {
|
||||
// Start heartbeat task
|
||||
unwrap!(spawner.spawn(blink_heartbeat(red_led)));
|
||||
|
||||
// Previous values for detecting changes
|
||||
// Track previous values to detect changes
|
||||
let mut prev_green = false;
|
||||
let mut prev_yellow = false;
|
||||
let mut prev_counter = 0;
|
||||
|
||||
info!("CM4: Starting main loop");
|
||||
loop {
|
||||
// Read values from shared memory
|
||||
// Read current values from shared memory
|
||||
let green_state = SHARED_LED_STATE.get_led(true);
|
||||
let yellow_state = SHARED_LED_STATE.get_led(false);
|
||||
let counter = SHARED_LED_STATE.get_counter();
|
||||
|
||||
// Check for state changes
|
||||
// Detect changes
|
||||
let green_changed = green_state != prev_green;
|
||||
let yellow_changed = yellow_state != prev_yellow;
|
||||
let counter_changed = counter != prev_counter;
|
||||
|
||||
// If any state changed, log it and update LEDs
|
||||
// Update LEDs and logs when values change
|
||||
if green_changed || yellow_changed || counter_changed {
|
||||
if counter_changed {
|
||||
info!("CM4: Counter = {}", counter);
|
||||
prev_counter = counter;
|
||||
}
|
||||
|
||||
// Update LED states
|
||||
if green_changed {
|
||||
if green_state {
|
||||
green_led.set_high();
|
||||
@ -172,7 +182,6 @@ async fn main(spawner: Spawner) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
// Poll at a reasonable rate
|
||||
Timer::after_millis(10).await;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,23 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
//! STM32H7 Primary Core (CM7) Intercore Communication Example
|
||||
//!
|
||||
//! This example demonstrates reliable communication between the Cortex-M7 and
|
||||
//! Cortex-M4 cores using a shared memory region configured as non-cacheable
|
||||
//! via MPU settings.
|
||||
//!
|
||||
//! The CM7 core handles:
|
||||
//! - MPU configuration to make shared memory non-cacheable
|
||||
//! - Clock initialization
|
||||
//! - Toggling LED states in shared memory
|
||||
//!
|
||||
//! Usage:
|
||||
//! 1. Flash the CM4 (secondary) core binary first
|
||||
//! 2. Then flash this CM7 (primary) core binary
|
||||
//! 3. The system will start with CM7 toggling LED states and CM4 responding by
|
||||
//! physically toggling the LEDs
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use cortex_m::asm;
|
||||
@ -12,17 +29,15 @@ use embassy_time::Timer;
|
||||
use shared::{SHARED_LED_STATE, SRAM4_BASE_ADDRESS, SRAM4_REGION_NUMBER, SRAM4_SIZE_LOG2};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
/// Module providing shared memory constructs for intercore communication
|
||||
mod shared {
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
/// Shared LED state between CM7 and CM4 cores
|
||||
/// State shared between CM7 and CM4 cores for LED control
|
||||
#[repr(C, align(4))]
|
||||
pub struct SharedLedState {
|
||||
// Magic number for validation
|
||||
pub magic: AtomicU32,
|
||||
// Counter for synchronization testing
|
||||
pub counter: AtomicU32,
|
||||
// LED states packed into a single atomic
|
||||
pub led_states: AtomicU32,
|
||||
}
|
||||
|
||||
@ -33,18 +48,16 @@ mod shared {
|
||||
impl SharedLedState {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
magic: AtomicU32::new(0xDEADBEEF), // Magic number
|
||||
magic: AtomicU32::new(0xDEADBEEF),
|
||||
counter: AtomicU32::new(0),
|
||||
led_states: AtomicU32::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set LED state using safe bit operations
|
||||
/// Set LED state by manipulating the appropriate bit in the led_states field
|
||||
#[inline(never)]
|
||||
pub fn set_led(&self, is_green: bool, state: bool) {
|
||||
let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT };
|
||||
|
||||
// Use bit operations to avoid complex atomic operations
|
||||
let current = self.led_states.load(Ordering::SeqCst);
|
||||
|
||||
let new_value = if state {
|
||||
@ -57,7 +70,7 @@ mod shared {
|
||||
core::sync::atomic::compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
/// Get LED state using safe bit operations
|
||||
/// Get current LED state
|
||||
#[inline(never)]
|
||||
#[allow(dead_code)]
|
||||
pub fn get_led(&self, is_green: bool) -> bool {
|
||||
@ -69,7 +82,7 @@ mod shared {
|
||||
(value & (1 << bit)) != 0
|
||||
}
|
||||
|
||||
/// Increment counter safely
|
||||
/// Increment counter and return new value
|
||||
#[inline(never)]
|
||||
pub fn increment_counter(&self) -> u32 {
|
||||
let current = self.counter.load(Ordering::SeqCst);
|
||||
@ -79,7 +92,7 @@ mod shared {
|
||||
new_value
|
||||
}
|
||||
|
||||
/// Get counter without incrementing
|
||||
/// Get current counter value
|
||||
#[inline(never)]
|
||||
#[allow(dead_code)]
|
||||
pub fn get_counter(&self) -> u32 {
|
||||
@ -92,37 +105,29 @@ mod shared {
|
||||
#[link_section = ".ram_d3"]
|
||||
pub static SHARED_LED_STATE: SharedLedState = SharedLedState::new();
|
||||
|
||||
// SRAM4 memory region constants for MPU configuration
|
||||
// Memory region constants for MPU configuration
|
||||
pub const SRAM4_BASE_ADDRESS: u32 = 0x38000000;
|
||||
pub const SRAM4_SIZE_LOG2: u32 = 15; // 64KB = 2^(15+1)
|
||||
pub const SRAM4_REGION_NUMBER: u8 = 0; // MPU region number to use
|
||||
pub const SRAM4_REGION_NUMBER: u8 = 0;
|
||||
}
|
||||
|
||||
#[link_section = ".ram_d3"]
|
||||
static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();
|
||||
|
||||
// Function to configure MPU with your provided settings
|
||||
/// Configure MPU to make SRAM4 region non-cacheable
|
||||
fn configure_mpu_non_cacheable(mpu: &mut MPU) {
|
||||
// Ensure all operations complete before reconfiguring MPU/caches
|
||||
asm::dmb();
|
||||
unsafe {
|
||||
// Disable MPU
|
||||
mpu.ctrl.write(0);
|
||||
|
||||
// Configure SRAM4 as non-cacheable
|
||||
// Set region number (0)
|
||||
mpu.rnr.write(SRAM4_REGION_NUMBER as u32);
|
||||
|
||||
// Set base address (SRAM4 = 0x38000000) with VALID bit and region number
|
||||
mpu.rbar.write(
|
||||
SRAM4_BASE_ADDRESS | (1 << 4), // Region number = 0 (explicit in RBAR)
|
||||
);
|
||||
// Set base address with region number
|
||||
mpu.rbar.write(SRAM4_BASE_ADDRESS | (1 << 4));
|
||||
|
||||
// Configure region attributes:
|
||||
// SIZE=15 (64KB = 2^(15+1))
|
||||
// ENABLE=1
|
||||
// AP=3 (Full access)
|
||||
// TEX=1, S=1, C=0, B=0 (Normal memory, Non-cacheable, Shareable)
|
||||
// Configure region attributes
|
||||
let rasr_value: u32 = (SRAM4_SIZE_LOG2 << 1) | // SIZE=15 (64KB)
|
||||
(1 << 0) | // ENABLE=1
|
||||
(3 << 24) | // AP=3 (Full access)
|
||||
@ -135,7 +140,6 @@ fn configure_mpu_non_cacheable(mpu: &mut MPU) {
|
||||
mpu.ctrl.write(1 | (1 << 2)); // MPU_ENABLE | PRIVDEFENA
|
||||
}
|
||||
|
||||
// Ensure changes are committed
|
||||
asm::dsb();
|
||||
asm::isb();
|
||||
|
||||
@ -144,25 +148,26 @@ fn configure_mpu_non_cacheable(mpu: &mut MPU) {
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
// Configure MPU to make SRAM4 non-cacheable
|
||||
// Set up MPU and cache configuration
|
||||
{
|
||||
let mut cp = cortex_m::Peripherals::take().unwrap();
|
||||
let scb = &mut cp.SCB;
|
||||
|
||||
// First disable caches
|
||||
scb.disable_icache();
|
||||
scb.disable_dcache(&mut cp.CPUID);
|
||||
|
||||
// 2. MPU setup
|
||||
// Configure MPU
|
||||
configure_mpu_non_cacheable(&mut cp.MPU);
|
||||
|
||||
// 3. re-enable caches
|
||||
// Re-enable caches
|
||||
scb.enable_icache();
|
||||
scb.enable_dcache(&mut cp.CPUID);
|
||||
asm::dsb();
|
||||
asm::isb();
|
||||
}
|
||||
|
||||
// Configure the clocks
|
||||
// Configure the clock system
|
||||
let mut config = Config::default();
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
@ -191,42 +196,38 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
let _p = embassy_stm32::init_primary(config, &SHARED_DATA);
|
||||
info!("CM7 core initialized with non-cacheable SRAM4!");
|
||||
|
||||
// Read the magic value to ensure shared memory is accessible
|
||||
// Verify shared memory is accessible
|
||||
let magic = SHARED_LED_STATE.magic.load(core::sync::atomic::Ordering::SeqCst);
|
||||
info!("CM7: Magic value = 0x{:X}", magic);
|
||||
|
||||
// Initialize shared memory state
|
||||
// Initialize LED states
|
||||
SHARED_LED_STATE.set_led(true, false); // Green LED off
|
||||
SHARED_LED_STATE.set_led(false, false); // Yellow LED off
|
||||
|
||||
// Main loop - update shared memory values
|
||||
// Main loop - periodically toggle LED states
|
||||
let mut green_state = false;
|
||||
let mut yellow_state = false;
|
||||
let mut loop_count = 0;
|
||||
|
||||
info!("CM7: Starting main loop");
|
||||
loop {
|
||||
// Update loop counter
|
||||
loop_count += 1;
|
||||
|
||||
// Update shared counter
|
||||
let counter = SHARED_LED_STATE.increment_counter();
|
||||
|
||||
// Every second, toggle green LED state
|
||||
// Toggle green LED every second
|
||||
if loop_count % 10 == 0 {
|
||||
green_state = !green_state;
|
||||
SHARED_LED_STATE.set_led(true, green_state);
|
||||
info!("CM7: Counter = {}, Set green LED to {}", counter, green_state);
|
||||
}
|
||||
|
||||
// Every 3 seconds, toggle yellow LED state
|
||||
// Toggle yellow LED every 3 seconds
|
||||
if loop_count % 30 == 0 {
|
||||
yellow_state = !yellow_state;
|
||||
SHARED_LED_STATE.set_led(false, yellow_state);
|
||||
info!("CM7: Counter = {}, Set yellow LED to {}", counter, yellow_state);
|
||||
}
|
||||
|
||||
// Wait 100ms before next cycle
|
||||
Timer::after_millis(100).await;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user