mirror of
https://github.com/embassy-rs/embassy.git
synced 2025-10-02 06:40:32 +00:00
Merge branch 'main' into feat/stm32wba-rcc-pll-support
This commit is contained in:
commit
b9e643d5c2
2
.github/ci/book.sh
vendored
2
.github/ci/book.sh
vendored
@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
## on push branch=main
|
## on push branch=main
|
||||||
## priority -9
|
## priority -100
|
||||||
## dedup dequeue
|
## dedup dequeue
|
||||||
|
|
||||||
set -euxo pipefail
|
set -euxo pipefail
|
||||||
|
2
.github/ci/doc.sh
vendored
2
.github/ci/doc.sh
vendored
@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
## on push branch=main
|
## on push branch=main
|
||||||
## priority -10
|
## priority -100
|
||||||
## dedup dequeue
|
## dedup dequeue
|
||||||
|
|
||||||
set -euxo pipefail
|
set -euxo pipefail
|
||||||
|
@ -24,7 +24,7 @@ features = ["defmt", "tcp", "udp", "raw", "dns", "icmp", "dhcpv4", "proto-ipv6",
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
## Enable defmt
|
## Enable defmt
|
||||||
defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt", "heapless/defmt-03", "defmt?/ip_in_core"]
|
defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt", "embassy-time/defmt", "heapless/defmt-03", "defmt?/ip_in_core"]
|
||||||
|
|
||||||
## Trace all raw received and transmitted packets using defmt or log.
|
## Trace all raw received and transmitted packets using defmt or log.
|
||||||
packet-trace = []
|
packet-trace = []
|
||||||
|
@ -106,6 +106,7 @@ impl<const SOCK: usize> StackResources<SOCK> {
|
|||||||
/// Static IP address configuration.
|
/// Static IP address configuration.
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct StaticConfigV4 {
|
pub struct StaticConfigV4 {
|
||||||
/// IP address and subnet mask.
|
/// IP address and subnet mask.
|
||||||
pub address: Ipv4Cidr,
|
pub address: Ipv4Cidr,
|
||||||
@ -118,6 +119,7 @@ pub struct StaticConfigV4 {
|
|||||||
/// Static IPv6 address configuration
|
/// Static IPv6 address configuration
|
||||||
#[cfg(feature = "proto-ipv6")]
|
#[cfg(feature = "proto-ipv6")]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct StaticConfigV6 {
|
pub struct StaticConfigV6 {
|
||||||
/// IP address and subnet mask.
|
/// IP address and subnet mask.
|
||||||
pub address: Ipv6Cidr,
|
pub address: Ipv6Cidr,
|
||||||
@ -130,6 +132,7 @@ pub struct StaticConfigV6 {
|
|||||||
/// DHCP configuration.
|
/// DHCP configuration.
|
||||||
#[cfg(feature = "dhcpv4")]
|
#[cfg(feature = "dhcpv4")]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct DhcpConfig {
|
pub struct DhcpConfig {
|
||||||
/// Maximum lease duration.
|
/// Maximum lease duration.
|
||||||
@ -169,6 +172,7 @@ impl Default for DhcpConfig {
|
|||||||
|
|
||||||
/// Network stack configuration.
|
/// Network stack configuration.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
/// IPv4 configuration
|
/// IPv4 configuration
|
||||||
@ -220,6 +224,7 @@ impl Config {
|
|||||||
/// Network stack IPv4 configuration.
|
/// Network stack IPv4 configuration.
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum ConfigV4 {
|
pub enum ConfigV4 {
|
||||||
/// Do not configure IPv4.
|
/// Do not configure IPv4.
|
||||||
#[default]
|
#[default]
|
||||||
@ -234,6 +239,7 @@ pub enum ConfigV4 {
|
|||||||
/// Network stack IPv6 configuration.
|
/// Network stack IPv6 configuration.
|
||||||
#[cfg(feature = "proto-ipv6")]
|
#[cfg(feature = "proto-ipv6")]
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum ConfigV6 {
|
pub enum ConfigV6 {
|
||||||
/// Do not configure IPv6.
|
/// Do not configure IPv6.
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -1599,7 +1599,7 @@ fn main() {
|
|||||||
for e in rcc_registers.ir.enums {
|
for e in rcc_registers.ir.enums {
|
||||||
fn is_rcc_name(e: &str) -> bool {
|
fn is_rcc_name(e: &str) -> bool {
|
||||||
match e {
|
match e {
|
||||||
"Pllp" | "Pllq" | "Pllr" | "Pllm" | "Plln" | "Prediv1" | "Prediv2" | "Hpre5" => true,
|
"Pllp" | "Pllq" | "Pllr" | "Plldivst" | "Pllm" | "Plln" | "Prediv1" | "Prediv2" | "Hpre5" => true,
|
||||||
"Timpre" | "Pllrclkpre" => false,
|
"Timpre" | "Pllrclkpre" => false,
|
||||||
e if e.ends_with("pre") || e.ends_with("pres") || e.ends_with("div") || e.ends_with("mul") => true,
|
e if e.ends_with("pre") || e.ends_with("pres") || e.ends_with("div") || e.ends_with("mul") => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
|
@ -95,6 +95,18 @@ cfg_if! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Number of samples used for averaging.
|
||||||
|
pub enum Averaging {
|
||||||
|
Disabled,
|
||||||
|
Samples2,
|
||||||
|
Samples4,
|
||||||
|
Samples8,
|
||||||
|
Samples16,
|
||||||
|
Samples32,
|
||||||
|
Samples64,
|
||||||
|
Samples128,
|
||||||
|
Samples256,
|
||||||
|
}
|
||||||
impl<'d, T: Instance> Adc<'d, T> {
|
impl<'d, T: Instance> Adc<'d, T> {
|
||||||
pub fn new(adc: Peri<'d, T>) -> Self {
|
pub fn new(adc: Peri<'d, T>) -> Self {
|
||||||
rcc::enable_and_reset::<T>();
|
rcc::enable_and_reset::<T>();
|
||||||
@ -225,6 +237,30 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));
|
T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_averaging(&mut self, averaging: Averaging) {
|
||||||
|
let (enable, samples, right_shift) = match averaging {
|
||||||
|
Averaging::Disabled => (false, 0, 0),
|
||||||
|
Averaging::Samples2 => (true, 0, 1),
|
||||||
|
Averaging::Samples4 => (true, 1, 2),
|
||||||
|
Averaging::Samples8 => (true, 2, 3),
|
||||||
|
Averaging::Samples16 => (true, 3, 4),
|
||||||
|
Averaging::Samples32 => (true, 4, 5),
|
||||||
|
Averaging::Samples64 => (true, 5, 6),
|
||||||
|
Averaging::Samples128 => (true, 6, 7),
|
||||||
|
Averaging::Samples256 => (true, 7, 8),
|
||||||
|
};
|
||||||
|
T::regs().cfgr2().modify(|reg| {
|
||||||
|
#[cfg(not(any(adc_g0, adc_u0)))]
|
||||||
|
reg.set_rovse(enable);
|
||||||
|
#[cfg(any(adc_g0, adc_u0))]
|
||||||
|
reg.set_ovse(enable);
|
||||||
|
#[cfg(any(adc_h5, adc_h7rs))]
|
||||||
|
reg.set_ovsr(samples.into());
|
||||||
|
#[cfg(not(any(adc_h5, adc_h7rs)))]
|
||||||
|
reg.set_ovsr(samples.into());
|
||||||
|
reg.set_ovss(right_shift.into());
|
||||||
|
})
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
/// Convert a raw sample from the `Temperature` to deg C
|
/// Convert a raw sample from the `Temperature` to deg C
|
||||||
pub fn to_degrees_centigrade(sample: u16) -> f32 {
|
pub fn to_degrees_centigrade(sample: u16) -> f32 {
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use core::ops::RangeInclusive;
|
use core::ops::RangeInclusive;
|
||||||
|
|
||||||
|
#[cfg(stm32h7rs)]
|
||||||
|
use stm32_metapac::rcc::vals::Plldivst;
|
||||||
|
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
pub use crate::pac::rcc::vals::{
|
pub use crate::pac::rcc::vals::{
|
||||||
Hsidiv as HSIPrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, Pllsrc as PllSource, Sw as Sysclk,
|
Hsidiv as HSIPrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, Pllsrc as PllSource, Sw as Sysclk,
|
||||||
@ -78,6 +81,12 @@ pub struct Pll {
|
|||||||
pub divq: Option<PllDiv>,
|
pub divq: Option<PllDiv>,
|
||||||
/// PLL R division factor. If None, PLL R output is disabled.
|
/// PLL R division factor. If None, PLL R output is disabled.
|
||||||
pub divr: Option<PllDiv>,
|
pub divr: Option<PllDiv>,
|
||||||
|
#[cfg(stm32h7rs)]
|
||||||
|
/// PLL S division factor. If None, PLL S output is disabled.
|
||||||
|
pub divs: Option<Plldivst>,
|
||||||
|
#[cfg(stm32h7rs)]
|
||||||
|
/// PLL T division factor. If None, PLL T output is disabled.
|
||||||
|
pub divt: Option<Plldivst>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apb_div_tim(apb: &APBPrescaler, clk: Hertz, tim: TimerPrescaler) -> Hertz {
|
fn apb_div_tim(apb: &APBPrescaler, clk: Hertz, tim: TimerPrescaler) -> Hertz {
|
||||||
@ -749,6 +758,12 @@ struct PllOutput {
|
|||||||
q: Option<Hertz>,
|
q: Option<Hertz>,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
r: Option<Hertz>,
|
r: Option<Hertz>,
|
||||||
|
#[cfg(stm32h7rs)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
s: Option<Hertz>,
|
||||||
|
#[cfg(stm32h7rs)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
t: Option<Hertz>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
||||||
@ -767,6 +782,10 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
|||||||
p: None,
|
p: None,
|
||||||
q: None,
|
q: None,
|
||||||
r: None,
|
r: None,
|
||||||
|
#[cfg(stm32h7rs)]
|
||||||
|
s: None,
|
||||||
|
#[cfg(stm32h7rs)]
|
||||||
|
t: None,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -814,6 +833,10 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
|||||||
});
|
});
|
||||||
let q = config.divq.map(|div| vco_clk / div);
|
let q = config.divq.map(|div| vco_clk / div);
|
||||||
let r = config.divr.map(|div| vco_clk / div);
|
let r = config.divr.map(|div| vco_clk / div);
|
||||||
|
#[cfg(stm32h7rs)]
|
||||||
|
let s = config.divs.map(|div| vco_clk / div);
|
||||||
|
#[cfg(stm32h7rs)]
|
||||||
|
let t = config.divt.map(|div| vco_clk / div);
|
||||||
|
|
||||||
#[cfg(stm32h5)]
|
#[cfg(stm32h5)]
|
||||||
RCC.pllcfgr(num).write(|w| {
|
RCC.pllcfgr(num).write(|w| {
|
||||||
@ -840,6 +863,10 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
|||||||
w.set_divpen(num, p.is_some());
|
w.set_divpen(num, p.is_some());
|
||||||
w.set_divqen(num, q.is_some());
|
w.set_divqen(num, q.is_some());
|
||||||
w.set_divren(num, r.is_some());
|
w.set_divren(num, r.is_some());
|
||||||
|
#[cfg(stm32h7rs)]
|
||||||
|
w.set_divsen(num, s.is_some());
|
||||||
|
#[cfg(stm32h7rs)]
|
||||||
|
w.set_divten(num, t.is_some());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -850,10 +877,24 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
|||||||
w.set_pllr(config.divr.unwrap_or(PllDiv::DIV2));
|
w.set_pllr(config.divr.unwrap_or(PllDiv::DIV2));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#[cfg(stm32h7rs)]
|
||||||
|
RCC.plldivr2(num).write(|w| {
|
||||||
|
w.set_plls(config.divs.unwrap_or(Plldivst::DIV2));
|
||||||
|
w.set_pllt(config.divt.unwrap_or(Plldivst::DIV2));
|
||||||
|
});
|
||||||
|
|
||||||
RCC.cr().modify(|w| w.set_pllon(num, true));
|
RCC.cr().modify(|w| w.set_pllon(num, true));
|
||||||
while !RCC.cr().read().pllrdy(num) {}
|
while !RCC.cr().read().pllrdy(num) {}
|
||||||
|
|
||||||
PllOutput { p, q, r }
|
PllOutput {
|
||||||
|
p,
|
||||||
|
q,
|
||||||
|
r,
|
||||||
|
#[cfg(stm32h7rs)]
|
||||||
|
s,
|
||||||
|
#[cfg(stm32h7rs)]
|
||||||
|
t,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flash_setup(clk: Hertz, vos: VoltageScale) {
|
fn flash_setup(clk: Hertz, vos: VoltageScale) {
|
||||||
|
@ -36,13 +36,6 @@ chrono = { version = "^0.4", default-features = false }
|
|||||||
grounded = "0.2.0"
|
grounded = "0.2.0"
|
||||||
|
|
||||||
# cargo build/run
|
# cargo build/run
|
||||||
[profile.dev]
|
|
||||||
codegen-units = 1
|
|
||||||
debug = 2
|
|
||||||
debug-assertions = true # <-
|
|
||||||
incremental = false
|
|
||||||
opt-level = 3 # <-
|
|
||||||
overflow-checks = true # <-
|
|
||||||
|
|
||||||
# cargo test
|
# cargo test
|
||||||
[profile.test]
|
[profile.test]
|
||||||
@ -55,11 +48,10 @@ overflow-checks = true # <-
|
|||||||
|
|
||||||
# cargo build/run --release
|
# cargo build/run --release
|
||||||
[profile.release]
|
[profile.release]
|
||||||
codegen-units = 1
|
codegen-units = 16
|
||||||
debug = 2
|
debug = 2
|
||||||
debug-assertions = false # <-
|
debug-assertions = false # <-
|
||||||
incremental = false
|
incremental = false
|
||||||
lto = 'fat'
|
|
||||||
opt-level = 3 # <-
|
opt-level = 3 # <-
|
||||||
overflow-checks = false # <-
|
overflow-checks = false # <-
|
||||||
|
|
||||||
|
182
examples/stm32h755cm4/src/bin/intercore.rs
Normal file
182
examples/stm32h755cm4/src/bin/intercore.rs
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
//! 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};
|
||||||
|
|
||||||
|
/// State shared between CM7 and CM4 cores for LED control
|
||||||
|
#[repr(C, align(4))]
|
||||||
|
pub struct SharedLedState {
|
||||||
|
pub magic: AtomicU32,
|
||||||
|
pub counter: AtomicU32,
|
||||||
|
pub led_states: AtomicU32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bit positions in led_states
|
||||||
|
pub const GREEN_LED_BIT: u32 = 0;
|
||||||
|
pub const YELLOW_LED_BIT: u32 = 1;
|
||||||
|
|
||||||
|
impl SharedLedState {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
magic: AtomicU32::new(0xDEADBEEF),
|
||||||
|
counter: AtomicU32::new(0),
|
||||||
|
led_states: AtomicU32::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 };
|
||||||
|
let current = self.led_states.load(Ordering::SeqCst);
|
||||||
|
|
||||||
|
let new_value = if state {
|
||||||
|
current | (1 << bit) // Set bit
|
||||||
|
} else {
|
||||||
|
current & !(1 << bit) // Clear bit
|
||||||
|
};
|
||||||
|
self.led_states.store(new_value, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 };
|
||||||
|
|
||||||
|
let value = self.led_states.load(Ordering::SeqCst);
|
||||||
|
(value & (1 << bit)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Increment counter and return new value
|
||||||
|
#[inline(never)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn increment_counter(&self) -> u32 {
|
||||||
|
let current = self.counter.load(Ordering::SeqCst);
|
||||||
|
let new_value = current.wrapping_add(1);
|
||||||
|
self.counter.store(new_value, Ordering::SeqCst);
|
||||||
|
new_value
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get current counter value
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn get_counter(&self) -> u32 {
|
||||||
|
let value = self.counter.load(Ordering::SeqCst);
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[link_section = ".ram_d3"]
|
||||||
|
pub static SHARED_LED_STATE: SharedLedState = SharedLedState::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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 {
|
||||||
|
led.toggle();
|
||||||
|
info!("CM4 heartbeat");
|
||||||
|
Timer::after_millis(500).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(spawner: Spawner) -> ! {
|
||||||
|
// Initialize the secondary core
|
||||||
|
let p = embassy_stm32::init_secondary(&SHARED_DATA);
|
||||||
|
info!("CM4 core initialized!");
|
||||||
|
|
||||||
|
// Verify shared memory is accessible
|
||||||
|
let magic = SHARED_LED_STATE.magic.load(core::sync::atomic::Ordering::SeqCst);
|
||||||
|
info!("CM4: Magic value = 0x{:X}", magic);
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
// Start heartbeat task
|
||||||
|
unwrap!(spawner.spawn(blink_heartbeat(red_led)));
|
||||||
|
|
||||||
|
// 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 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();
|
||||||
|
|
||||||
|
// Detect changes
|
||||||
|
let green_changed = green_state != prev_green;
|
||||||
|
let yellow_changed = yellow_state != prev_yellow;
|
||||||
|
let counter_changed = counter != prev_counter;
|
||||||
|
|
||||||
|
// Update LEDs and logs when values change
|
||||||
|
if green_changed || yellow_changed || counter_changed {
|
||||||
|
if counter_changed {
|
||||||
|
info!("CM4: Counter = {}", counter);
|
||||||
|
prev_counter = counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if green_changed {
|
||||||
|
if green_state {
|
||||||
|
green_led.set_high();
|
||||||
|
info!("CM4: Green LED ON");
|
||||||
|
} else {
|
||||||
|
green_led.set_low();
|
||||||
|
info!("CM4: Green LED OFF");
|
||||||
|
}
|
||||||
|
prev_green = green_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
if yellow_changed {
|
||||||
|
if yellow_state {
|
||||||
|
yellow_led.set_high();
|
||||||
|
info!("CM4: Yellow LED ON");
|
||||||
|
} else {
|
||||||
|
yellow_led.set_low();
|
||||||
|
info!("CM4: Yellow LED OFF");
|
||||||
|
}
|
||||||
|
prev_yellow = yellow_state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer::after_millis(10).await;
|
||||||
|
}
|
||||||
|
}
|
@ -35,15 +35,6 @@ static_cell = "2"
|
|||||||
chrono = { version = "^0.4", default-features = false }
|
chrono = { version = "^0.4", default-features = false }
|
||||||
grounded = "0.2.0"
|
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
|
# cargo test
|
||||||
[profile.test]
|
[profile.test]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
@ -55,11 +46,10 @@ overflow-checks = true # <-
|
|||||||
|
|
||||||
# cargo build/run --release
|
# cargo build/run --release
|
||||||
[profile.release]
|
[profile.release]
|
||||||
codegen-units = 1
|
codegen-units = 16
|
||||||
debug = 2
|
debug = 2
|
||||||
debug-assertions = false # <-
|
debug-assertions = false # <-
|
||||||
incremental = false
|
incremental = false
|
||||||
lto = 'fat'
|
|
||||||
opt-level = 3 # <-
|
opt-level = 3 # <-
|
||||||
overflow-checks = false # <-
|
overflow-checks = false # <-
|
||||||
|
|
||||||
|
228
examples/stm32h755cm7/src/bin/intercore.rs
Normal file
228
examples/stm32h755cm7/src/bin/intercore.rs
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
#![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;
|
||||||
|
use cortex_m::peripheral::MPU;
|
||||||
|
use defmt::*;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_stm32::{Config, SharedData};
|
||||||
|
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};
|
||||||
|
|
||||||
|
/// State shared between CM7 and CM4 cores for LED control
|
||||||
|
#[repr(C, align(4))]
|
||||||
|
pub struct SharedLedState {
|
||||||
|
pub magic: AtomicU32,
|
||||||
|
pub counter: AtomicU32,
|
||||||
|
pub led_states: AtomicU32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bit positions in led_states
|
||||||
|
pub const GREEN_LED_BIT: u32 = 0;
|
||||||
|
pub const YELLOW_LED_BIT: u32 = 1;
|
||||||
|
|
||||||
|
impl SharedLedState {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
magic: AtomicU32::new(0xDEADBEEF),
|
||||||
|
counter: AtomicU32::new(0),
|
||||||
|
led_states: AtomicU32::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 };
|
||||||
|
let current = self.led_states.load(Ordering::SeqCst);
|
||||||
|
|
||||||
|
let new_value = if state {
|
||||||
|
current | (1 << bit) // Set bit
|
||||||
|
} else {
|
||||||
|
current & !(1 << bit) // Clear bit
|
||||||
|
};
|
||||||
|
|
||||||
|
self.led_states.store(new_value, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get current LED state
|
||||||
|
#[inline(never)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn get_led(&self, is_green: bool) -> bool {
|
||||||
|
let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT };
|
||||||
|
|
||||||
|
let value = self.led_states.load(Ordering::SeqCst);
|
||||||
|
(value & (1 << bit)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Increment counter and return new value
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn increment_counter(&self) -> u32 {
|
||||||
|
let current = self.counter.load(Ordering::SeqCst);
|
||||||
|
let new_value = current.wrapping_add(1);
|
||||||
|
self.counter.store(new_value, Ordering::SeqCst);
|
||||||
|
new_value
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get current counter value
|
||||||
|
#[inline(never)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn get_counter(&self) -> u32 {
|
||||||
|
let value = self.counter.load(Ordering::SeqCst);
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[link_section = ".ram_d3"]
|
||||||
|
pub static SHARED_LED_STATE: SharedLedState = SharedLedState::new();
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[link_section = ".ram_d3"]
|
||||||
|
static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();
|
||||||
|
|
||||||
|
/// Configure MPU to make SRAM4 region non-cacheable
|
||||||
|
fn configure_mpu_non_cacheable(mpu: &mut MPU) {
|
||||||
|
asm::dmb();
|
||||||
|
unsafe {
|
||||||
|
// Disable MPU
|
||||||
|
mpu.ctrl.write(0);
|
||||||
|
|
||||||
|
// Configure SRAM4 as non-cacheable
|
||||||
|
mpu.rnr.write(SRAM4_REGION_NUMBER as u32);
|
||||||
|
|
||||||
|
// Set base address with region number
|
||||||
|
mpu.rbar.write(SRAM4_BASE_ADDRESS | (1 << 4));
|
||||||
|
|
||||||
|
// Configure region attributes
|
||||||
|
let rasr_value: u32 = (SRAM4_SIZE_LOG2 << 1) | // SIZE=15 (64KB)
|
||||||
|
(1 << 0) | // ENABLE=1
|
||||||
|
(3 << 24) | // AP=3 (Full access)
|
||||||
|
(1 << 19) | // TEX=1
|
||||||
|
(1 << 18); // S=1 (Shareable)
|
||||||
|
|
||||||
|
mpu.rasr.write(rasr_value);
|
||||||
|
|
||||||
|
// Enable MPU with default memory map as background
|
||||||
|
mpu.ctrl.write(1 | (1 << 2)); // MPU_ENABLE | PRIVDEFENA
|
||||||
|
}
|
||||||
|
|
||||||
|
asm::dsb();
|
||||||
|
asm::isb();
|
||||||
|
|
||||||
|
info!("MPU configured - SRAM4 set as non-cacheable");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) -> ! {
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Configure MPU
|
||||||
|
configure_mpu_non_cacheable(&mut cp.MPU);
|
||||||
|
|
||||||
|
// Re-enable caches
|
||||||
|
scb.enable_icache();
|
||||||
|
scb.enable_dcache(&mut cp.CPUID);
|
||||||
|
asm::dsb();
|
||||||
|
asm::isb();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the clock system
|
||||||
|
let mut config = Config::default();
|
||||||
|
{
|
||||||
|
use embassy_stm32::rcc::*;
|
||||||
|
config.rcc.hsi = Some(HSIPrescaler::DIV1);
|
||||||
|
config.rcc.csi = true;
|
||||||
|
config.rcc.hsi48 = Some(Default::default());
|
||||||
|
config.rcc.pll1 = Some(Pll {
|
||||||
|
source: PllSource::HSI,
|
||||||
|
prediv: PllPreDiv::DIV4,
|
||||||
|
mul: PllMul::MUL50,
|
||||||
|
divp: Some(PllDiv::DIV2),
|
||||||
|
divq: Some(PllDiv::DIV8),
|
||||||
|
divr: None,
|
||||||
|
});
|
||||||
|
config.rcc.sys = Sysclk::PLL1_P;
|
||||||
|
config.rcc.ahb_pre = AHBPrescaler::DIV2;
|
||||||
|
config.rcc.apb1_pre = APBPrescaler::DIV2;
|
||||||
|
config.rcc.apb2_pre = APBPrescaler::DIV2;
|
||||||
|
config.rcc.apb3_pre = APBPrescaler::DIV2;
|
||||||
|
config.rcc.apb4_pre = APBPrescaler::DIV2;
|
||||||
|
config.rcc.voltage_scale = VoltageScale::Scale1;
|
||||||
|
config.rcc.supply_config = SupplyConfig::DirectSMPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the CM7 core
|
||||||
|
let _p = embassy_stm32::init_primary(config, &SHARED_DATA);
|
||||||
|
info!("CM7 core initialized with non-cacheable SRAM4!");
|
||||||
|
|
||||||
|
// 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 LED states
|
||||||
|
SHARED_LED_STATE.set_led(true, false); // Green LED off
|
||||||
|
SHARED_LED_STATE.set_led(false, false); // Yellow LED off
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
loop_count += 1;
|
||||||
|
let counter = SHARED_LED_STATE.increment_counter();
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer::after_millis(100).await;
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,8 @@ async fn main(_spawner: Spawner) {
|
|||||||
divp: Some(PllDiv::DIV2),
|
divp: Some(PllDiv::DIV2),
|
||||||
divq: None,
|
divq: None,
|
||||||
divr: None,
|
divr: None,
|
||||||
|
divs: None,
|
||||||
|
divt: None,
|
||||||
});
|
});
|
||||||
config.rcc.sys = Sysclk::PLL1_P; // 600 Mhz
|
config.rcc.sys = Sysclk::PLL1_P; // 600 Mhz
|
||||||
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 Mhz
|
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 Mhz
|
||||||
|
@ -41,6 +41,8 @@ async fn main(spawner: Spawner) -> ! {
|
|||||||
divp: Some(PllDiv::DIV2),
|
divp: Some(PllDiv::DIV2),
|
||||||
divq: None,
|
divq: None,
|
||||||
divr: None,
|
divr: None,
|
||||||
|
divs: None,
|
||||||
|
divt: None,
|
||||||
});
|
});
|
||||||
config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz
|
config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz
|
||||||
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
|
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
|
||||||
|
@ -40,6 +40,8 @@ async fn main(_spawner: Spawner) {
|
|||||||
divp: Some(PllDiv::DIV1), //600 MHz
|
divp: Some(PllDiv::DIV1), //600 MHz
|
||||||
divq: Some(PllDiv::DIV2), // 300 MHz
|
divq: Some(PllDiv::DIV2), // 300 MHz
|
||||||
divr: Some(PllDiv::DIV2), // 300 MHz
|
divr: Some(PllDiv::DIV2), // 300 MHz
|
||||||
|
divs: None,
|
||||||
|
divt: None,
|
||||||
});
|
});
|
||||||
config.rcc.sys = Sysclk::PLL1_P; // 600 MHz
|
config.rcc.sys = Sysclk::PLL1_P; // 600 MHz
|
||||||
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 MHz
|
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 MHz
|
||||||
|
@ -36,6 +36,8 @@ async fn main(_spawner: Spawner) {
|
|||||||
divp: Some(PllDiv::DIV2),
|
divp: Some(PllDiv::DIV2),
|
||||||
divq: None,
|
divq: None,
|
||||||
divr: None,
|
divr: None,
|
||||||
|
divs: None,
|
||||||
|
divt: None,
|
||||||
});
|
});
|
||||||
config.rcc.sys = Sysclk::PLL1_P; // 600 Mhz
|
config.rcc.sys = Sysclk::PLL1_P; // 600 Mhz
|
||||||
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 Mhz
|
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 Mhz
|
||||||
|
@ -681,6 +681,8 @@ pub fn config() -> Config {
|
|||||||
divp: Some(PllDiv::DIV2), // 600Mhz
|
divp: Some(PllDiv::DIV2), // 600Mhz
|
||||||
divq: Some(PllDiv::DIV25), // 48Mhz
|
divq: Some(PllDiv::DIV25), // 48Mhz
|
||||||
divr: None,
|
divr: None,
|
||||||
|
divs: None,
|
||||||
|
divt: None,
|
||||||
});
|
});
|
||||||
config.rcc.sys = Sysclk::PLL1_P; // 600 Mhz
|
config.rcc.sys = Sysclk::PLL1_P; // 600 Mhz
|
||||||
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 Mhz
|
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 Mhz
|
||||||
|
Loading…
x
Reference in New Issue
Block a user