diff options
| -rw-r--r-- | embassy-rp/src/clocks.rs | 130 | ||||
| -rw-r--r-- | embassy-stm32/Cargo.toml | 4 | ||||
| -rw-r--r-- | examples/stm32f334/Cargo.toml | 2 | ||||
| -rw-r--r-- | tests/rp/src/bin/adc.rs | 4 |
4 files changed, 136 insertions, 4 deletions
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index a33980230..7b25ecffb 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | use core::arch::asm; | ||
| 1 | use core::marker::PhantomData; | 2 | use core::marker::PhantomData; |
| 2 | use core::sync::atomic::{AtomicU16, AtomicU32, Ordering}; | 3 | use core::sync::atomic::{AtomicU16, AtomicU32, Ordering}; |
| 3 | 4 | ||
| @@ -6,6 +7,7 @@ use pac::clocks::vals::*; | |||
| 6 | 7 | ||
| 7 | use crate::gpio::sealed::Pin; | 8 | use crate::gpio::sealed::Pin; |
| 8 | use crate::gpio::AnyPin; | 9 | use crate::gpio::AnyPin; |
| 10 | use crate::pac::common::{Reg, RW}; | ||
| 9 | use crate::{pac, reset, Peripheral}; | 11 | use crate::{pac, reset, Peripheral}; |
| 10 | 12 | ||
| 11 | // NOTE: all gpin handling is commented out for future reference. | 13 | // NOTE: all gpin handling is commented out for future reference. |
| @@ -873,3 +875,131 @@ impl rand_core::RngCore for RoscRng { | |||
| 873 | dest.fill_with(Self::next_u8) | 875 | dest.fill_with(Self::next_u8) |
| 874 | } | 876 | } |
| 875 | } | 877 | } |
| 878 | /// Enter the `DORMANT` sleep state. This will stop *all* internal clocks | ||
| 879 | /// and can only be exited through resets, dormant-wake GPIO interrupts, | ||
| 880 | /// and RTC interrupts. If RTC is clocked from an internal clock source | ||
| 881 | /// it will be stopped and not function as a wakeup source. | ||
| 882 | #[cfg(target_arch = "arm")] | ||
| 883 | pub fn dormant_sleep() { | ||
| 884 | struct Set<T: Copy, F: Fn()>(Reg<T, RW>, T, F); | ||
| 885 | |||
| 886 | impl<T: Copy, F: Fn()> Drop for Set<T, F> { | ||
| 887 | fn drop(&mut self) { | ||
| 888 | self.0.write_value(self.1); | ||
| 889 | self.2(); | ||
| 890 | } | ||
| 891 | } | ||
| 892 | |||
| 893 | fn set_with_post_restore<T: Copy, After: Fn(), F: FnOnce(&mut T) -> After>( | ||
| 894 | reg: Reg<T, RW>, | ||
| 895 | f: F, | ||
| 896 | ) -> Set<T, impl Fn()> { | ||
| 897 | reg.modify(|w| { | ||
| 898 | let old = *w; | ||
| 899 | let after = f(w); | ||
| 900 | Set(reg, old, after) | ||
| 901 | }) | ||
| 902 | } | ||
| 903 | |||
| 904 | fn set<T: Copy, F: FnOnce(&mut T)>(reg: Reg<T, RW>, f: F) -> Set<T, impl Fn()> { | ||
| 905 | set_with_post_restore(reg, |r| { | ||
| 906 | f(r); | ||
| 907 | || () | ||
| 908 | }) | ||
| 909 | } | ||
| 910 | |||
| 911 | // disable all clocks that are not vital in preparation for disabling clock sources. | ||
| 912 | // we'll keep gpout and rtc clocks untouched, gpout because we don't care about them | ||
| 913 | // and rtc because it's a possible wakeup source. if clk_rtc is not configured for | ||
| 914 | // gpin we'll never wake from rtc, but that's what the user asked for then. | ||
| 915 | let _stop_adc = set(pac::CLOCKS.clk_adc_ctrl(), |w| w.set_enable(false)); | ||
| 916 | let _stop_usb = set(pac::CLOCKS.clk_usb_ctrl(), |w| w.set_enable(false)); | ||
| 917 | let _stop_peri = set(pac::CLOCKS.clk_peri_ctrl(), |w| w.set_enable(false)); | ||
| 918 | // set up rosc. we could ask the use to tell us which clock source to wake from like | ||
| 919 | // the C SDK does, but that seems rather unfriendly. we *may* disturb rtc by changing | ||
| 920 | // rosc configuration if it's currently the rtc clock source, so we'll configure rosc | ||
| 921 | // to the slowest frequency to minimize that impact. | ||
| 922 | let _configure_rosc = ( | ||
| 923 | set(pac::ROSC.ctrl(), |w| { | ||
| 924 | w.set_enable(pac::rosc::vals::Enable::ENABLE); | ||
| 925 | w.set_freq_range(pac::rosc::vals::FreqRange::LOW); | ||
| 926 | }), | ||
| 927 | // div=32 | ||
| 928 | set(pac::ROSC.div(), |w| w.set_div(pac::rosc::vals::Div(0xaa0))), | ||
| 929 | ); | ||
| 930 | while !pac::ROSC.status().read().stable() {} | ||
| 931 | // switch over to rosc as the system clock source. this will change clock sources for | ||
| 932 | // watchdog and timer clocks, but timers won't be a concern and the watchdog won't | ||
| 933 | // speed up by enough to worry about (unless it's clocked from gpin, which we don't | ||
| 934 | // support anyway). | ||
| 935 | let _switch_clk_ref = set(pac::CLOCKS.clk_ref_ctrl(), |w| { | ||
| 936 | w.set_src(pac::clocks::vals::ClkRefCtrlSrc::ROSC_CLKSRC_PH); | ||
| 937 | }); | ||
| 938 | let _switch_clk_sys = set(pac::CLOCKS.clk_sys_ctrl(), |w| { | ||
| 939 | w.set_src(pac::clocks::vals::ClkSysCtrlSrc::CLK_REF); | ||
| 940 | }); | ||
| 941 | // oscillator dormancy does not power down plls, we have to do that ourselves. we'll | ||
| 942 | // restore them to their prior glory when woken though since the system may be clocked | ||
| 943 | // from either (and usb/adc will probably need the USB PLL anyway) | ||
| 944 | let _stop_pll_sys = set_with_post_restore(pac::PLL_SYS.pwr(), |w| { | ||
| 945 | let wake = !w.pd() && !w.vcopd(); | ||
| 946 | w.set_pd(true); | ||
| 947 | w.set_vcopd(true); | ||
| 948 | move || while wake && !pac::PLL_SYS.cs().read().lock() {} | ||
| 949 | }); | ||
| 950 | let _stop_pll_usb = set_with_post_restore(pac::PLL_USB.pwr(), |w| { | ||
| 951 | let wake = !w.pd() && !w.vcopd(); | ||
| 952 | w.set_pd(true); | ||
| 953 | w.set_vcopd(true); | ||
| 954 | move || while wake && !pac::PLL_USB.cs().read().lock() {} | ||
| 955 | }); | ||
| 956 | // dormancy only stops the oscillator we're telling to go dormant, the other remains | ||
| 957 | // running. nothing can use xosc at this point any more. not doing this costs an 200µA. | ||
| 958 | let _stop_xosc = set_with_post_restore(pac::XOSC.ctrl(), |w| { | ||
| 959 | let wake = w.enable() == pac::xosc::vals::Enable::ENABLE; | ||
| 960 | if wake { | ||
| 961 | w.set_enable(pac::xosc::vals::Enable::DISABLE); | ||
| 962 | } | ||
| 963 | move || while wake && !pac::XOSC.status().read().stable() {} | ||
| 964 | }); | ||
| 965 | let _power_down_xip_cache = set(pac::XIP_CTRL.ctrl(), |w| w.set_power_down(true)); | ||
| 966 | |||
| 967 | // only power down memory if we're running from XIP (or ROM? how?). | ||
| 968 | // powering down memory otherwise would require a lot of exacting checks that | ||
| 969 | // are better done by the user in a local copy of this function. | ||
| 970 | // powering down memories saves ~100µA, so it's well worth doing. | ||
| 971 | unsafe { | ||
| 972 | let is_in_flash = { | ||
| 973 | // we can't rely on the address of this function as rust sees it since linker | ||
| 974 | // magic or even boot2 may place it into ram. | ||
| 975 | let pc: usize; | ||
| 976 | asm!( | ||
| 977 | "mov {pc}, pc", | ||
| 978 | pc = out (reg) pc | ||
| 979 | ); | ||
| 980 | pc < 0x20000000 | ||
| 981 | }; | ||
| 982 | if is_in_flash { | ||
| 983 | // we will be powering down memories, so we must be *absolutely* | ||
| 984 | // certain that we're running entirely from XIP and registers until | ||
| 985 | // memories are powered back up again. accessing memory that's powered | ||
| 986 | // down may corrupt memory contents (see section 2.11.4 of the manual). | ||
| 987 | // additionally a 20ns wait time is needed after powering up memories | ||
| 988 | // again. rosc is likely to run at only a few MHz at most, so the | ||
| 989 | // inter-instruction delay alone will be enough to satisfy this bound. | ||
| 990 | asm!( | ||
| 991 | "ldr {old_mem}, [{mempowerdown}]", | ||
| 992 | "str {power_down_mems}, [{mempowerdown}]", | ||
| 993 | "str {coma}, [{dormant}]", | ||
| 994 | "str {old_mem}, [{mempowerdown}]", | ||
| 995 | old_mem = out (reg) _, | ||
| 996 | mempowerdown = in (reg) pac::SYSCFG.mempowerdown().as_ptr(), | ||
| 997 | power_down_mems = in (reg) 0b11111111, | ||
| 998 | dormant = in (reg) pac::ROSC.dormant().as_ptr(), | ||
| 999 | coma = in (reg) 0x636f6d61, | ||
| 1000 | ); | ||
| 1001 | } else { | ||
| 1002 | pac::ROSC.dormant().write_value(0x636f6d61); | ||
| 1003 | } | ||
| 1004 | } | ||
| 1005 | } | ||
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 8c7dd38c2..723c50296 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -57,7 +57,7 @@ sdio-host = "0.5.0" | |||
| 57 | embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } | 57 | embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } |
| 58 | critical-section = "1.1" | 58 | critical-section = "1.1" |
| 59 | atomic-polyfill = "1.0.1" | 59 | atomic-polyfill = "1.0.1" |
| 60 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-1f8ab493e029fc601edebc6bac105a63cc9858fe" } | 60 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-82a24863823a3daf0ca664c7fdf008379d0a0d42" } |
| 61 | vcell = "0.1.3" | 61 | vcell = "0.1.3" |
| 62 | bxcan = "0.7.0" | 62 | bxcan = "0.7.0" |
| 63 | nb = "1.0.0" | 63 | nb = "1.0.0" |
| @@ -75,7 +75,7 @@ critical-section = { version = "1.1", features = ["std"] } | |||
| 75 | [build-dependencies] | 75 | [build-dependencies] |
| 76 | proc-macro2 = "1.0.36" | 76 | proc-macro2 = "1.0.36" |
| 77 | quote = "1.0.15" | 77 | quote = "1.0.15" |
| 78 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-1f8ab493e029fc601edebc6bac105a63cc9858fe", default-features = false, features = ["metadata"]} | 78 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-82a24863823a3daf0ca664c7fdf008379d0a0d42", default-features = false, features = ["metadata"]} |
| 79 | 79 | ||
| 80 | [features] | 80 | [features] |
| 81 | default = ["rt"] | 81 | default = ["rt"] |
diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml index 6410891a1..d8f6b8fe8 100644 --- a/examples/stm32f334/Cargo.toml +++ b/examples/stm32f334/Cargo.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-stm32f3-examples" | 3 | name = "embassy-stm32f334-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
diff --git a/tests/rp/src/bin/adc.rs b/tests/rp/src/bin/adc.rs index d6d58f0c0..b29a3a7cb 100644 --- a/tests/rp/src/bin/adc.rs +++ b/tests/rp/src/bin/adc.rs | |||
| @@ -8,7 +8,7 @@ use defmt::*; | |||
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler, Sample}; | 9 | use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler, Sample}; |
| 10 | use embassy_rp::bind_interrupts; | 10 | use embassy_rp::bind_interrupts; |
| 11 | use embassy_rp::gpio::Pull; | 11 | use embassy_rp::gpio::{Level, Output, Pull}; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 13 | ||
| 14 | bind_interrupts!(struct Irqs { | 14 | bind_interrupts!(struct Irqs { |
| @@ -18,6 +18,8 @@ bind_interrupts!(struct Irqs { | |||
| 18 | #[embassy_executor::main] | 18 | #[embassy_executor::main] |
| 19 | async fn main(_spawner: Spawner) { | 19 | async fn main(_spawner: Spawner) { |
| 20 | let mut p = embassy_rp::init(Default::default()); | 20 | let mut p = embassy_rp::init(Default::default()); |
| 21 | let _power_reg_pwm_mode = Output::new(p.PIN_23, Level::High); | ||
| 22 | let _wifi_off = Output::new(p.PIN_25, Level::High); | ||
| 21 | let mut adc = Adc::new(p.ADC, Irqs, Config::default()); | 23 | let mut adc = Adc::new(p.ADC, Irqs, Config::default()); |
| 22 | 24 | ||
| 23 | { | 25 | { |
