Compare commits
13 Commits
static-cel
...
l0l1-moder
Author | SHA1 | Date | |
---|---|---|---|
8911a4d855 | |||
056c409443 | |||
3f2abd4fd5 | |||
dc467e89a0 | |||
655ed3aa88 | |||
14ec0d27bf | |||
649f1a122a | |||
413b394d31 | |||
7ea2c3508a | |||
ec744558b2 | |||
1b9292dbcd | |||
44486c5b39 | |||
aa97fe7cbd |
2
ci.sh
2
ci.sh
@ -218,8 +218,6 @@ cargo batch \
|
|||||||
rm out/tests/stm32wb55rg/wpan_mac
|
rm out/tests/stm32wb55rg/wpan_mac
|
||||||
rm out/tests/stm32wb55rg/wpan_ble
|
rm out/tests/stm32wb55rg/wpan_ble
|
||||||
|
|
||||||
# unstable
|
|
||||||
rm out/tests/stm32f429zi/stop
|
|
||||||
|
|
||||||
# unstable, I think it's running out of RAM?
|
# unstable, I think it's running out of RAM?
|
||||||
rm out/tests/stm32f207zg/eth
|
rm out/tests/stm32f207zg/eth
|
||||||
|
@ -90,6 +90,7 @@ defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-emb
|
|||||||
|
|
||||||
exti = []
|
exti = []
|
||||||
low-power = [ "dep:embassy-executor", "embassy-executor/arch-cortex-m" ]
|
low-power = [ "dep:embassy-executor", "embassy-executor/arch-cortex-m" ]
|
||||||
|
low-power-debug-with-sleep = []
|
||||||
embassy-executor = []
|
embassy-executor = []
|
||||||
|
|
||||||
## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/)
|
## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/)
|
||||||
|
@ -556,6 +556,32 @@ fn main() {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
If LP and non-LP peripherals share the same RCC enable bit, then a refcount leak will result.
|
||||||
|
|
||||||
|
This should be checked in stm32-data-gen.
|
||||||
|
*/
|
||||||
|
let stop_refcount = if p.name.starts_with("LP") {
|
||||||
|
quote! { REFCOUNT_STOP2 }
|
||||||
|
} else {
|
||||||
|
quote! { REFCOUNT_STOP1 }
|
||||||
|
};
|
||||||
|
|
||||||
|
let (incr_stop_refcount, decr_stop_refcount) = if p.name != "RTC" {
|
||||||
|
(
|
||||||
|
quote! {
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
unsafe { crate::rcc::#stop_refcount += 1 };
|
||||||
|
},
|
||||||
|
quote! {
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
unsafe { crate::rcc::#stop_refcount -= 1 };
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(quote! {}, quote! {})
|
||||||
|
};
|
||||||
|
|
||||||
g.extend(quote! {
|
g.extend(quote! {
|
||||||
impl crate::rcc::sealed::RccPeripheral for peripherals::#pname {
|
impl crate::rcc::sealed::RccPeripheral for peripherals::#pname {
|
||||||
fn frequency() -> crate::time::Hertz {
|
fn frequency() -> crate::time::Hertz {
|
||||||
@ -563,8 +589,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) {
|
fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) {
|
||||||
#before_enable
|
#before_enable
|
||||||
#[cfg(feature = "low-power")]
|
#incr_stop_refcount
|
||||||
unsafe { crate::rcc::REFCOUNT_STOP2 += 1 };
|
|
||||||
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true));
|
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true));
|
||||||
#after_enable
|
#after_enable
|
||||||
#rst
|
#rst
|
||||||
@ -572,8 +597,7 @@ fn main() {
|
|||||||
fn disable_with_cs(_cs: critical_section::CriticalSection) {
|
fn disable_with_cs(_cs: critical_section::CriticalSection) {
|
||||||
#before_disable
|
#before_disable
|
||||||
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false));
|
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false));
|
||||||
#[cfg(feature = "low-power")]
|
#decr_stop_refcount
|
||||||
unsafe { crate::rcc::REFCOUNT_STOP2 -= 1 };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,8 +228,9 @@ pub fn init(config: Config) -> Peripherals {
|
|||||||
|
|
||||||
#[cfg(feature = "low-power")]
|
#[cfg(feature = "low-power")]
|
||||||
{
|
{
|
||||||
crate::rcc::REFCOUNT_STOP2 = 0
|
crate::rcc::REFCOUNT_STOP2 = 0;
|
||||||
};
|
crate::rcc::REFCOUNT_STOP1 = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p
|
p
|
||||||
|
@ -1,3 +1,50 @@
|
|||||||
|
/// The STM32 line of microcontrollers support various deep-sleep modes which exploit clock-gating
|
||||||
|
/// to reduce power consumption. `embassy-stm32` provides a low-power executor, [`Executor`] which
|
||||||
|
/// can use knowledge of which peripherals are currently blocked upon to transparently and safely
|
||||||
|
/// enter such low-power modes (currently, only `STOP2`) when idle.
|
||||||
|
///
|
||||||
|
/// The executor determines which peripherals are active by their RCC state; consequently,
|
||||||
|
/// low-power states can only be entered if all peripherals have been `drop`'d. There are a few
|
||||||
|
/// exceptions to this rule:
|
||||||
|
///
|
||||||
|
/// * `GPIO`
|
||||||
|
/// * `RCC`
|
||||||
|
///
|
||||||
|
/// Since entering and leaving low-power modes typically incurs a significant latency, the
|
||||||
|
/// low-power executor will only attempt to enter when the next timer event is at least
|
||||||
|
/// [`time_driver::MIN_STOP_PAUSE`] in the future.
|
||||||
|
///
|
||||||
|
/// Currently there is no macro analogous to `embassy_executor::main` for this executor;
|
||||||
|
/// consequently one must define their entrypoint manually. Moveover, you must relinquish control
|
||||||
|
/// of the `RTC` peripheral to the executor. This will typically look like
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// use embassy_executor::Spawner;
|
||||||
|
/// use embassy_stm32::low_power::Executor;
|
||||||
|
/// use embassy_stm32::rtc::{Rtc, RtcConfig};
|
||||||
|
/// use static_cell::make_static;
|
||||||
|
///
|
||||||
|
/// #[cortex_m_rt::entry]
|
||||||
|
/// fn main() -> ! {
|
||||||
|
/// Executor::take().run(|spawner| {
|
||||||
|
/// unwrap!(spawner.spawn(async_main(spawner)));
|
||||||
|
/// });
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[embassy_executor::task]
|
||||||
|
/// async fn async_main(spawner: Spawner) {
|
||||||
|
/// // initialize the platform...
|
||||||
|
/// let mut config = embassy_stm32::Config::default();
|
||||||
|
/// let p = embassy_stm32::init(config);
|
||||||
|
///
|
||||||
|
/// // give the RTC to the executor...
|
||||||
|
/// let mut rtc = Rtc::new(p.RTC, RtcConfig::default());
|
||||||
|
/// let rtc = make_static!(rtc);
|
||||||
|
/// embassy_stm32::low_power::stop_with_rtc(rtc);
|
||||||
|
///
|
||||||
|
/// // your application here...
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::sync::atomic::{compiler_fence, Ordering};
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
@ -33,11 +80,17 @@ pub fn stop_with_rtc(rtc: &'static Rtc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop_ready(stop_mode: StopMode) -> bool {
|
pub fn stop_ready(stop_mode: StopMode) -> bool {
|
||||||
unsafe { EXECUTOR.as_mut().unwrap() }.stop_ready(stop_mode)
|
match unsafe { EXECUTOR.as_mut().unwrap() }.stop_mode() {
|
||||||
|
Some(StopMode::Stop2) => true,
|
||||||
|
Some(StopMode::Stop1) => stop_mode == StopMode::Stop1,
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
#[derive(PartialEq)]
|
||||||
pub enum StopMode {
|
pub enum StopMode {
|
||||||
|
Stop1,
|
||||||
Stop2,
|
Stop2,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,23 +141,39 @@ impl Executor {
|
|||||||
trace!("low power: stop with rtc configured");
|
trace!("low power: stop with rtc configured");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop_ready(&self, stop_mode: StopMode) -> bool {
|
fn stop_mode(&self) -> Option<StopMode> {
|
||||||
match stop_mode {
|
if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 } && unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } {
|
||||||
StopMode::Stop2 => unsafe { crate::rcc::REFCOUNT_STOP2 == 0 },
|
Some(StopMode::Stop2)
|
||||||
|
} else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } {
|
||||||
|
Some(StopMode::Stop1)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn configure_stop(&mut self, _stop_mode: StopMode) {
|
||||||
|
// TODO: configure chip-specific settings for stop
|
||||||
|
}
|
||||||
|
|
||||||
fn configure_pwr(&mut self) {
|
fn configure_pwr(&mut self) {
|
||||||
self.scb.clear_sleepdeep();
|
self.scb.clear_sleepdeep();
|
||||||
|
|
||||||
compiler_fence(Ordering::SeqCst);
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
if !self.stop_ready(StopMode::Stop2) {
|
let stop_mode = self.stop_mode();
|
||||||
|
if stop_mode.is_none() {
|
||||||
trace!("low power: not ready to stop");
|
trace!("low power: not ready to stop");
|
||||||
} else if self.time_driver.pause_time().is_err() {
|
} else if self.time_driver.pause_time().is_err() {
|
||||||
trace!("low power: failed to pause time");
|
trace!("low power: failed to pause time");
|
||||||
} else {
|
} else {
|
||||||
trace!("low power: stop");
|
let stop_mode = stop_mode.unwrap();
|
||||||
|
match stop_mode {
|
||||||
|
StopMode::Stop1 => trace!("low power: stop 1"),
|
||||||
|
StopMode::Stop2 => trace!("low power: stop 2"),
|
||||||
|
}
|
||||||
|
self.configure_stop(stop_mode);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "low-power-debug-with-sleep"))]
|
||||||
self.scb.set_sleepdeep();
|
self.scb.set_sleepdeep();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
||||||
pub use crate::pac::rcc::vals::{
|
pub use crate::pac::rcc::vals::{
|
||||||
Hpre as AHBPrescaler, Msirange as MSIRange, Plldiv as PLLDiv, Pllmul as PLLMul, Ppre as APBPrescaler,
|
Hpre as AHBPrescaler, Msirange as MSIRange, Plldiv as PLLDiv, Plldiv as PllDiv, Pllmul as PLLMul, Pllmul as PllMul,
|
||||||
|
Pllsrc as PLLSource, Ppre as APBPrescaler, Sw as ClockSrc,
|
||||||
};
|
};
|
||||||
use crate::pac::rcc::vals::{Pllsrc, Sw};
|
|
||||||
#[cfg(crs)]
|
#[cfg(crs)]
|
||||||
use crate::pac::{crs, CRS, SYSCFG};
|
use crate::pac::{crs, CRS, SYSCFG};
|
||||||
use crate::pac::{FLASH, PWR, RCC};
|
use crate::pac::{FLASH, PWR, RCC};
|
||||||
@ -12,39 +12,50 @@ use crate::time::Hertz;
|
|||||||
/// HSI speed
|
/// HSI speed
|
||||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
||||||
|
|
||||||
/// System clock mux source
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
#[derive(Clone, Copy)]
|
pub enum HseMode {
|
||||||
pub enum ClockSrc {
|
/// crystal/ceramic oscillator (HSEBYP=0)
|
||||||
MSI(MSIRange),
|
Oscillator,
|
||||||
PLL(PLLSource, PLLMul, PLLDiv),
|
/// external analog clock (low swing) (HSEBYP=1)
|
||||||
HSE(Hertz),
|
Bypass,
|
||||||
HSI,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PLL clock input source
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
#[derive(Clone, Copy)]
|
pub struct Hse {
|
||||||
pub enum PLLSource {
|
/// HSE frequency.
|
||||||
HSI,
|
pub freq: Hertz,
|
||||||
HSE(Hertz),
|
/// HSE mode.
|
||||||
|
pub mode: HseMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PLLSource> for Pllsrc {
|
#[derive(Clone, Copy)]
|
||||||
fn from(val: PLLSource) -> Pllsrc {
|
pub struct Pll {
|
||||||
match val {
|
/// PLL source
|
||||||
PLLSource::HSI => Pllsrc::HSI,
|
pub source: PLLSource,
|
||||||
PLLSource::HSE(_) => Pllsrc::HSE,
|
|
||||||
}
|
/// PLL multiplication factor.
|
||||||
}
|
pub mul: PllMul,
|
||||||
|
|
||||||
|
/// PLL main output division factor.
|
||||||
|
pub div: PllDiv,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clocks configutation
|
/// Clocks configutation
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
// base clock sources
|
||||||
|
pub msi: Option<MSIRange>,
|
||||||
|
pub hsi: bool,
|
||||||
|
pub hse: Option<Hse>,
|
||||||
|
#[cfg(crs)]
|
||||||
|
pub hsi48: bool,
|
||||||
|
|
||||||
|
pub pll: Option<Pll>,
|
||||||
|
|
||||||
pub mux: ClockSrc,
|
pub mux: ClockSrc,
|
||||||
pub ahb_pre: AHBPrescaler,
|
pub ahb_pre: AHBPrescaler,
|
||||||
pub apb1_pre: APBPrescaler,
|
pub apb1_pre: APBPrescaler,
|
||||||
pub apb2_pre: APBPrescaler,
|
pub apb2_pre: APBPrescaler,
|
||||||
#[cfg(crs)]
|
|
||||||
pub enable_hsi48: bool,
|
|
||||||
pub ls: super::LsConfig,
|
pub ls: super::LsConfig,
|
||||||
pub voltage_scale: VoltageScale,
|
pub voltage_scale: VoltageScale,
|
||||||
}
|
}
|
||||||
@ -53,12 +64,18 @@ impl Default for Config {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn default() -> Config {
|
fn default() -> Config {
|
||||||
Config {
|
Config {
|
||||||
mux: ClockSrc::MSI(MSIRange::RANGE5),
|
msi: Some(MSIRange::RANGE5),
|
||||||
|
hse: None,
|
||||||
|
hsi: false,
|
||||||
|
#[cfg(crs)]
|
||||||
|
hsi48: false,
|
||||||
|
|
||||||
|
pll: None,
|
||||||
|
|
||||||
|
mux: ClockSrc::MSI,
|
||||||
ahb_pre: AHBPrescaler::DIV1,
|
ahb_pre: AHBPrescaler::DIV1,
|
||||||
apb1_pre: APBPrescaler::DIV1,
|
apb1_pre: APBPrescaler::DIV1,
|
||||||
apb2_pre: APBPrescaler::DIV1,
|
apb2_pre: APBPrescaler::DIV1,
|
||||||
#[cfg(crs)]
|
|
||||||
enable_hsi48: false,
|
|
||||||
voltage_scale: VoltageScale::RANGE1,
|
voltage_scale: VoltageScale::RANGE1,
|
||||||
ls: Default::default(),
|
ls: Default::default(),
|
||||||
}
|
}
|
||||||
@ -71,71 +88,67 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
PWR.cr().write(|w| w.set_vos(config.voltage_scale));
|
PWR.cr().write(|w| w.set_vos(config.voltage_scale));
|
||||||
while PWR.csr().read().vosf() {}
|
while PWR.csr().read().vosf() {}
|
||||||
|
|
||||||
let (sys_clk, sw) = match config.mux {
|
let rtc = config.ls.init();
|
||||||
ClockSrc::MSI(range) => {
|
|
||||||
// Set MSI range
|
|
||||||
RCC.icscr().write(|w| w.set_msirange(range));
|
|
||||||
|
|
||||||
// Enable MSI
|
let msi = config.msi.map(|range| {
|
||||||
RCC.cr().write(|w| w.set_msion(true));
|
RCC.icscr().modify(|w| w.set_msirange(range));
|
||||||
|
|
||||||
|
RCC.cr().modify(|w| w.set_msion(true));
|
||||||
while !RCC.cr().read().msirdy() {}
|
while !RCC.cr().read().msirdy() {}
|
||||||
|
|
||||||
let freq = 32_768 * (1 << (range as u8 + 1));
|
Hertz(32_768 * (1 << (range as u8 + 1)))
|
||||||
(Hertz(freq), Sw::MSI)
|
});
|
||||||
}
|
|
||||||
ClockSrc::HSI => {
|
let hsi = config.hsi.then(|| {
|
||||||
// Enable HSI
|
RCC.cr().modify(|w| w.set_hsion(true));
|
||||||
RCC.cr().write(|w| w.set_hsion(true));
|
|
||||||
while !RCC.cr().read().hsirdy() {}
|
while !RCC.cr().read().hsirdy() {}
|
||||||
|
|
||||||
(HSI_FREQ, Sw::HSI)
|
|
||||||
}
|
|
||||||
ClockSrc::HSE(freq) => {
|
|
||||||
// Enable HSE
|
|
||||||
RCC.cr().write(|w| w.set_hseon(true));
|
|
||||||
while !RCC.cr().read().hserdy() {}
|
|
||||||
|
|
||||||
(freq, Sw::HSE)
|
|
||||||
}
|
|
||||||
ClockSrc::PLL(src, mul, div) => {
|
|
||||||
let freq = match src {
|
|
||||||
PLLSource::HSE(freq) => {
|
|
||||||
// Enable HSE
|
|
||||||
RCC.cr().write(|w| w.set_hseon(true));
|
|
||||||
while !RCC.cr().read().hserdy() {}
|
|
||||||
freq
|
|
||||||
}
|
|
||||||
PLLSource::HSI => {
|
|
||||||
// Enable HSI
|
|
||||||
RCC.cr().write(|w| w.set_hsion(true));
|
|
||||||
while !RCC.cr().read().hsirdy() {}
|
|
||||||
HSI_FREQ
|
HSI_FREQ
|
||||||
}
|
});
|
||||||
|
|
||||||
|
let hse = config.hse.map(|hse| {
|
||||||
|
RCC.cr().modify(|w| {
|
||||||
|
w.set_hsebyp(hse.mode == HseMode::Bypass);
|
||||||
|
w.set_hseon(true);
|
||||||
|
});
|
||||||
|
while !RCC.cr().read().hserdy() {}
|
||||||
|
|
||||||
|
hse.freq
|
||||||
|
});
|
||||||
|
|
||||||
|
let pll = config.pll.map(|pll| {
|
||||||
|
let freq = match pll.source {
|
||||||
|
PLLSource::HSE => hse.unwrap(),
|
||||||
|
PLLSource::HSI => hsi.unwrap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Disable PLL
|
// Disable PLL
|
||||||
RCC.cr().modify(|w| w.set_pllon(false));
|
RCC.cr().modify(|w| w.set_pllon(false));
|
||||||
while RCC.cr().read().pllrdy() {}
|
while RCC.cr().read().pllrdy() {}
|
||||||
|
|
||||||
let freq = freq * mul / div;
|
let freq = freq * pll.mul / pll.div;
|
||||||
|
|
||||||
assert!(freq <= Hertz(32_000_000));
|
assert!(freq <= Hertz(32_000_000));
|
||||||
|
|
||||||
RCC.cfgr().write(move |w| {
|
RCC.cfgr().write(move |w| {
|
||||||
w.set_pllmul(mul);
|
w.set_pllmul(pll.mul);
|
||||||
w.set_plldiv(div);
|
w.set_plldiv(pll.div);
|
||||||
w.set_pllsrc(src.into());
|
w.set_pllsrc(pll.source);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Enable PLL
|
// Enable PLL
|
||||||
RCC.cr().modify(|w| w.set_pllon(true));
|
RCC.cr().modify(|w| w.set_pllon(true));
|
||||||
while !RCC.cr().read().pllrdy() {}
|
while !RCC.cr().read().pllrdy() {}
|
||||||
|
|
||||||
(freq, Sw::PLL1_P)
|
freq
|
||||||
}
|
});
|
||||||
};
|
|
||||||
|
|
||||||
let rtc = config.ls.init();
|
let sys_clk = match config.mux {
|
||||||
|
ClockSrc::HSE => hse.unwrap(),
|
||||||
|
ClockSrc::HSI => hsi.unwrap(),
|
||||||
|
ClockSrc::MSI => msi.unwrap(),
|
||||||
|
ClockSrc::PLL1_P => pll.unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
let wait_states = match (config.voltage_scale, sys_clk.0) {
|
let wait_states = match (config.voltage_scale, sys_clk.0) {
|
||||||
(VoltageScale::RANGE1, ..=16_000_000) => 0,
|
(VoltageScale::RANGE1, ..=16_000_000) => 0,
|
||||||
@ -150,7 +163,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
FLASH.acr().modify(|w| w.set_latency(wait_states != 0));
|
FLASH.acr().modify(|w| w.set_latency(wait_states != 0));
|
||||||
|
|
||||||
RCC.cfgr().modify(|w| {
|
RCC.cfgr().modify(|w| {
|
||||||
w.set_sw(sw);
|
w.set_sw(config.mux);
|
||||||
w.set_hpre(config.ahb_pre);
|
w.set_hpre(config.ahb_pre);
|
||||||
w.set_ppre1(config.apb1_pre);
|
w.set_ppre1(config.apb1_pre);
|
||||||
w.set_ppre2(config.apb2_pre);
|
w.set_ppre2(config.apb2_pre);
|
||||||
@ -161,7 +174,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre);
|
let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre);
|
||||||
|
|
||||||
#[cfg(crs)]
|
#[cfg(crs)]
|
||||||
if config.enable_hsi48 {
|
if config.hsi48 {
|
||||||
// Reset CRS peripheral
|
// Reset CRS peripheral
|
||||||
RCC.apb1rstr().modify(|w| w.set_crsrst(true));
|
RCC.apb1rstr().modify(|w| w.set_crsrst(true));
|
||||||
RCC.apb1rstr().modify(|w| w.set_crsrst(false));
|
RCC.apb1rstr().modify(|w| w.set_crsrst(false));
|
||||||
|
@ -193,9 +193,6 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
});
|
});
|
||||||
while !RCC.cr().read().msirdy() {}
|
while !RCC.cr().read().msirdy() {}
|
||||||
|
|
||||||
// Enable as clock source for USB, RNG if running at 48 MHz
|
|
||||||
if range == MSIRange::RANGE48M {}
|
|
||||||
|
|
||||||
msirange_to_hertz(range)
|
msirange_to_hertz(range)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -181,6 +181,15 @@ pub struct Clocks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "low-power")]
|
#[cfg(feature = "low-power")]
|
||||||
|
/// Must be written within a critical section
|
||||||
|
///
|
||||||
|
/// May be read without a critical section
|
||||||
|
pub(crate) static mut REFCOUNT_STOP1: u32 = 0;
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
/// Must be written within a critical section
|
||||||
|
///
|
||||||
|
/// May be read without a critical section
|
||||||
pub(crate) static mut REFCOUNT_STOP2: u32 = 0;
|
pub(crate) static mut REFCOUNT_STOP2: u32 = 0;
|
||||||
|
|
||||||
/// Frozen clock frequencies
|
/// Frozen clock frequencies
|
||||||
|
@ -153,14 +153,7 @@ impl Default for RtcCalibrationCyclePeriod {
|
|||||||
impl Rtc {
|
impl Rtc {
|
||||||
pub fn new(_rtc: impl Peripheral<P = RTC>, rtc_config: RtcConfig) -> Self {
|
pub fn new(_rtc: impl Peripheral<P = RTC>, rtc_config: RtcConfig) -> Self {
|
||||||
#[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))]
|
#[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))]
|
||||||
critical_section::with(|cs| {
|
<RTC as crate::rcc::sealed::RccPeripheral>::enable_and_reset();
|
||||||
<RTC as crate::rcc::sealed::RccPeripheral>::enable_and_reset_with_cs(cs);
|
|
||||||
|
|
||||||
#[cfg(feature = "low-power")]
|
|
||||||
unsafe {
|
|
||||||
crate::rcc::REFCOUNT_STOP2 -= 1
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
#[cfg(feature = "low-power")]
|
#[cfg(feature = "low-power")]
|
||||||
|
@ -345,6 +345,10 @@ impl RtcDriver {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
/// The minimum pause time beyond which the executor will enter a low-power state.
|
||||||
|
pub(crate) const MIN_STOP_PAUSE: embassy_time::Duration = embassy_time::Duration::from_millis(250);
|
||||||
|
|
||||||
#[cfg(feature = "low-power")]
|
#[cfg(feature = "low-power")]
|
||||||
/// Pause the timer if ready; return err if not
|
/// Pause the timer if ready; return err if not
|
||||||
pub(crate) fn pause_time(&self) -> Result<(), ()> {
|
pub(crate) fn pause_time(&self) -> Result<(), ()> {
|
||||||
@ -357,7 +361,7 @@ impl RtcDriver {
|
|||||||
self.stop_wakeup_alarm(cs);
|
self.stop_wakeup_alarm(cs);
|
||||||
|
|
||||||
let time_until_next_alarm = self.time_until_next_alarm(cs);
|
let time_until_next_alarm = self.time_until_next_alarm(cs);
|
||||||
if time_until_next_alarm < embassy_time::Duration::from_millis(250) {
|
if time_until_next_alarm < Self::MIN_STOP_PAUSE {
|
||||||
Err(())
|
Err(())
|
||||||
} else {
|
} else {
|
||||||
self.rtc
|
self.rtc
|
||||||
|
81
examples/rp/src/bin/pio_rotary_encoder.rs
Normal file
81
examples/rp/src/bin/pio_rotary_encoder.rs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
//! This example shows how to use the PIO module in the RP2040 to read a quadrature rotary encoder.
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use defmt::info;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_rp::gpio::Pull;
|
||||||
|
use embassy_rp::peripherals::PIO0;
|
||||||
|
use embassy_rp::{bind_interrupts, pio};
|
||||||
|
use fixed::traits::ToFixed;
|
||||||
|
use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
bind_interrupts!(struct Irqs {
|
||||||
|
PIO0_IRQ_0 => InterruptHandler<PIO0>;
|
||||||
|
});
|
||||||
|
|
||||||
|
pub struct PioEncoder<'d, T: Instance, const SM: usize> {
|
||||||
|
sm: StateMachine<'d, T, SM>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> {
|
||||||
|
pub fn new(
|
||||||
|
pio: &mut Common<'d, T>,
|
||||||
|
mut sm: StateMachine<'d, T, SM>,
|
||||||
|
pin_a: impl PioPin,
|
||||||
|
pin_b: impl PioPin,
|
||||||
|
) -> Self {
|
||||||
|
let mut pin_a = pio.make_pio_pin(pin_a);
|
||||||
|
let mut pin_b = pio.make_pio_pin(pin_b);
|
||||||
|
pin_a.set_pull(Pull::Up);
|
||||||
|
pin_b.set_pull(Pull::Up);
|
||||||
|
sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]);
|
||||||
|
|
||||||
|
let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",);
|
||||||
|
|
||||||
|
let mut cfg = Config::default();
|
||||||
|
cfg.set_in_pins(&[&pin_a, &pin_b]);
|
||||||
|
cfg.fifo_join = FifoJoin::RxOnly;
|
||||||
|
cfg.shift_in.direction = ShiftDirection::Left;
|
||||||
|
cfg.clock_divider = 10_000.to_fixed();
|
||||||
|
cfg.use_program(&pio.load_program(&prg.program), &[]);
|
||||||
|
sm.set_config(&cfg);
|
||||||
|
sm.set_enable(true);
|
||||||
|
Self { sm }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn read(&mut self) -> Direction {
|
||||||
|
loop {
|
||||||
|
match self.sm.rx().wait_pull().await {
|
||||||
|
0 => return Direction::CounterClockwise,
|
||||||
|
1 => return Direction::Clockwise,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Direction {
|
||||||
|
Clockwise,
|
||||||
|
CounterClockwise,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
let p = embassy_rp::init(Default::default());
|
||||||
|
let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
|
||||||
|
|
||||||
|
let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5);
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
loop {
|
||||||
|
info!("Count: {}", count);
|
||||||
|
count += match encoder.read().await {
|
||||||
|
Direction::Clockwise => 1,
|
||||||
|
Direction::CounterClockwise => -1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
26
examples/rp/src/bin/pwm_input.rs
Normal file
26
examples/rp/src/bin/pwm_input.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//! This example shows how to use the PWM module to measure the frequency of an input signal.
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use defmt::*;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_rp::pwm::{Config, InputMode, Pwm};
|
||||||
|
use embassy_time::{Duration, Ticker};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
let p = embassy_rp::init(Default::default());
|
||||||
|
|
||||||
|
let cfg: Config = Default::default();
|
||||||
|
let pwm = Pwm::new_input(p.PWM_CH2, p.PIN_5, InputMode::RisingEdge, cfg);
|
||||||
|
|
||||||
|
let mut ticker = Ticker::every(Duration::from_secs(1));
|
||||||
|
loop {
|
||||||
|
info!("Input frequency: {} Hz", pwm.counter());
|
||||||
|
pwm.set_counter(0);
|
||||||
|
ticker.next().await;
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,7 @@ use {defmt_rtt as _, panic_probe as _};
|
|||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.rcc.enable_hsi48 = true;
|
config.rcc.hsi48 = true;
|
||||||
let p = embassy_stm32::init(config);
|
let p = embassy_stm32::init(config);
|
||||||
|
|
||||||
let button = Input::new(p.PB2, Pull::Up);
|
let button = Input::new(p.PB2, Pull::Up);
|
||||||
|
@ -24,7 +24,7 @@ const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriatel
|
|||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let mut config = embassy_stm32::Config::default();
|
let mut config = embassy_stm32::Config::default();
|
||||||
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI;
|
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI;
|
||||||
config.rcc.enable_hsi48 = true;
|
config.rcc.hsi48 = true;
|
||||||
let p = embassy_stm32::init(config);
|
let p = embassy_stm32::init(config);
|
||||||
|
|
||||||
let mut spi_config = spi::Config::default();
|
let mut spi_config = spi::Config::default();
|
||||||
|
@ -34,7 +34,7 @@ const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set th
|
|||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let mut config = embassy_stm32::Config::default();
|
let mut config = embassy_stm32::Config::default();
|
||||||
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI;
|
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI;
|
||||||
config.rcc.enable_hsi48 = true;
|
config.rcc.hsi48 = true;
|
||||||
let p = embassy_stm32::init(config);
|
let p = embassy_stm32::init(config);
|
||||||
|
|
||||||
let mut spi_config = spi::Config::default();
|
let mut spi_config = spi::Config::default();
|
||||||
|
@ -24,7 +24,7 @@ const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriatel
|
|||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let mut config = embassy_stm32::Config::default();
|
let mut config = embassy_stm32::Config::default();
|
||||||
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI;
|
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI;
|
||||||
config.rcc.enable_hsi48 = true;
|
config.rcc.hsi48 = true;
|
||||||
let p = embassy_stm32::init(config);
|
let p = embassy_stm32::init(config);
|
||||||
|
|
||||||
let mut spi_config = spi::Config::default();
|
let mut spi_config = spi::Config::default();
|
||||||
|
@ -24,7 +24,7 @@ const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriatel
|
|||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let mut config = embassy_stm32::Config::default();
|
let mut config = embassy_stm32::Config::default();
|
||||||
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI;
|
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI;
|
||||||
config.rcc.enable_hsi48 = true;
|
config.rcc.hsi48 = true;
|
||||||
let p = embassy_stm32::init(config);
|
let p = embassy_stm32::init(config);
|
||||||
|
|
||||||
let mut spi_config = spi::Config::default();
|
let mut spi_config = spi::Config::default();
|
||||||
|
@ -33,7 +33,7 @@ stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"]
|
|||||||
eth = []
|
eth = []
|
||||||
rng = []
|
rng = []
|
||||||
sdmmc = []
|
sdmmc = []
|
||||||
stop = ["embassy-stm32/low-power"]
|
stop = ["embassy-stm32/low-power", "embassy-stm32/low-power-debug-with-sleep"]
|
||||||
chrono = ["embassy-stm32/chrono", "dep:chrono"]
|
chrono = ["embassy-stm32/chrono", "dep:chrono"]
|
||||||
can = []
|
can = []
|
||||||
ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"]
|
ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"]
|
||||||
|
@ -460,23 +460,25 @@ pub fn config() -> Config {
|
|||||||
#[cfg(feature = "stm32l073rz")]
|
#[cfg(feature = "stm32l073rz")]
|
||||||
{
|
{
|
||||||
use embassy_stm32::rcc::*;
|
use embassy_stm32::rcc::*;
|
||||||
config.rcc.mux = ClockSrc::PLL(
|
config.rcc.hsi = true;
|
||||||
// 32Mhz clock (16 * 4 / 2)
|
config.rcc.pll = Some(Pll {
|
||||||
PLLSource::HSI,
|
source: PLLSource::HSI,
|
||||||
PLLMul::MUL4,
|
mul: PLLMul::MUL4,
|
||||||
PLLDiv::DIV2,
|
div: PLLDiv::DIV2, // 32Mhz clock (16 * 4 / 2)
|
||||||
);
|
});
|
||||||
|
config.rcc.mux = ClockSrc::PLL1_P;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "stm32l152re"))]
|
#[cfg(any(feature = "stm32l152re"))]
|
||||||
{
|
{
|
||||||
use embassy_stm32::rcc::*;
|
use embassy_stm32::rcc::*;
|
||||||
config.rcc.mux = ClockSrc::PLL(
|
config.rcc.hsi = true;
|
||||||
// 32Mhz clock (16 * 4 / 2)
|
config.rcc.pll = Some(Pll {
|
||||||
PLLSource::HSI,
|
source: PLLSource::HSI,
|
||||||
PLLMul::MUL4,
|
mul: PLLMul::MUL4,
|
||||||
PLLDiv::DIV2,
|
div: PLLDiv::DIV2, // 32Mhz clock (16 * 4 / 2)
|
||||||
);
|
});
|
||||||
|
config.rcc.mux = ClockSrc::PLL1_P;
|
||||||
}
|
}
|
||||||
|
|
||||||
config
|
config
|
||||||
|
Reference in New Issue
Block a user