diff options
| author | Michael Kefeder <[email protected]> | 2025-09-28 18:32:40 +0200 |
|---|---|---|
| committer | Michael Kefeder <[email protected]> | 2025-09-29 21:24:34 +0200 |
| commit | 8fae6f5a3f7055dc8250cd8926624010b14db6dc (patch) | |
| tree | 8efee5340798565cf5a0e13293e1766f5156c6d9 | |
| parent | b29c7295e406045ec137b78ca5f220bf7909006b (diff) | |
Reset SAADC in Drop impl for nrf52, workaround for anomaly 241.
| -rw-r--r-- | embassy-nrf/CHANGELOG.md | 1 | ||||
| -rw-r--r-- | embassy-nrf/src/saadc.rs | 13 | ||||
| -rw-r--r-- | examples/nrf52810/src/bin/saadc_lowpower.rs | 62 |
3 files changed, 76 insertions, 0 deletions
diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index b8d03a1f8..3ad3c8005 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md | |||
| @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 17 | - changed: impl Drop for Timer | 17 | - changed: impl Drop for Timer |
| 18 | - added: expose `regs` for timer driver | 18 | - added: expose `regs` for timer driver |
| 19 | - added: timer driver CC `clear_events` method | 19 | - added: timer driver CC `clear_events` method |
| 20 | - changed: Saadc reset in Drop impl, anomaly 241 - high power usage | ||
| 20 | 21 | ||
| 21 | ## 0.7.0 - 2025-08-26 | 22 | ## 0.7.0 - 2025-08-26 |
| 22 | 23 | ||
diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 92b6fb01f..d84246572 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs | |||
| @@ -457,6 +457,19 @@ impl<'d> Saadc<'d, 1> { | |||
| 457 | 457 | ||
| 458 | impl<'d, const N: usize> Drop for Saadc<'d, N> { | 458 | impl<'d, const N: usize> Drop for Saadc<'d, N> { |
| 459 | fn drop(&mut self) { | 459 | fn drop(&mut self) { |
| 460 | // Reset of SAADC. | ||
| 461 | // | ||
| 462 | // This is needed when more than one pin is sampled to avoid needless power consumption. | ||
| 463 | // More information can be found in [nrf52 Anomaly 241](https://docs.nordicsemi.com/bundle/errata_nRF52810_Rev1/page/ERR/nRF52810/Rev1/latest/anomaly_810_241.html). | ||
| 464 | // The workaround seems like it copies the configuration before reset and reapplies it after. | ||
| 465 | // This method consumes the instance forcing a reconfiguration at compile time, hence we only | ||
| 466 | // call what is the reset portion of the workaround. | ||
| 467 | #[cfg(feature = "_nrf52")] | ||
| 468 | { | ||
| 469 | unsafe { core::ptr::write_volatile(0x40007FFC as *mut u32, 0) } | ||
| 470 | unsafe { core::ptr::read_volatile(0x40007FFC as *const ()) } | ||
| 471 | unsafe { core::ptr::write_volatile(0x40007FFC as *mut u32, 1) } | ||
| 472 | } | ||
| 460 | let r = Self::regs(); | 473 | let r = Self::regs(); |
| 461 | r.enable().write(|w| w.set_enable(false)); | 474 | r.enable().write(|w| w.set_enable(false)); |
| 462 | for i in 0..N { | 475 | for i in 0..N { |
diff --git a/examples/nrf52810/src/bin/saadc_lowpower.rs b/examples/nrf52810/src/bin/saadc_lowpower.rs new file mode 100644 index 000000000..d7e7f09a4 --- /dev/null +++ b/examples/nrf52810/src/bin/saadc_lowpower.rs | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | //! Run SAADC on multiple pins only every 3rd time, to show anomaly 241 workaround. | ||
| 2 | //! | ||
| 3 | //! To correctly measure the MCU current on the NRF52DK follow the instructions | ||
| 4 | //! <https://docs.nordicsemi.com/bundle/ug_nrf52832_dk/page/UG/dk/prepare_board.html> | ||
| 5 | //! otherwise you will measure the whole board, including the segger j-link chip for example | ||
| 6 | |||
| 7 | #![no_std] | ||
| 8 | #![no_main] | ||
| 9 | |||
| 10 | use defmt::info; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; | ||
| 13 | use embassy_nrf::saadc::{Oversample, Saadc}; | ||
| 14 | use embassy_nrf::{bind_interrupts, saadc}; | ||
| 15 | use embassy_time::Timer; | ||
| 16 | use {defmt_rtt as _, panic_probe as _}; | ||
| 17 | |||
| 18 | bind_interrupts!(struct Irqs { | ||
| 19 | SAADC => saadc::InterruptHandler; | ||
| 20 | }); | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(_p: Spawner) { | ||
| 24 | let mut p = embassy_nrf::init(Default::default()); | ||
| 25 | |||
| 26 | // For PPK2 digital channel plot to track when SAADC is on/off. | ||
| 27 | let mut ppk2_d0 = Output::new(p.P0_27, Level::Low, OutputDrive::Standard); | ||
| 28 | let mut num_loops: usize = 0; | ||
| 29 | loop { | ||
| 30 | num_loops += 1; | ||
| 31 | if num_loops.is_multiple_of(3) { | ||
| 32 | ppk2_d0.set_high(); | ||
| 33 | let battery_pin = p.P0_02.reborrow(); | ||
| 34 | let sensor1_pin = p.P0_03.reborrow(); | ||
| 35 | let mut adc_config = saadc::Config::default(); | ||
| 36 | adc_config.oversample = Oversample::OVER4X; | ||
| 37 | let battery = saadc::ChannelConfig::single_ended(battery_pin); | ||
| 38 | let sensor1 = saadc::ChannelConfig::single_ended(sensor1_pin); | ||
| 39 | let mut saadc = Saadc::new(p.SAADC.reborrow(), Irqs, adc_config, [battery, sensor1]); | ||
| 40 | // Indicated: wait for ADC calibration. | ||
| 41 | saadc.calibrate().await; | ||
| 42 | let mut buf = [0; 2]; | ||
| 43 | info!("sampling..."); | ||
| 44 | saadc.sample(&mut buf).await; | ||
| 45 | info!("data: {:x}", buf); | ||
| 46 | |||
| 47 | // Sleep to show the high power usage on the plot, even though sampling is done. | ||
| 48 | Timer::after_millis(100).await; | ||
| 49 | ppk2_d0.set_low(); | ||
| 50 | // disable the following line to show the anomaly on the power profiler plot. | ||
| 51 | core::mem::drop(saadc); | ||
| 52 | // Sleep to show the power usage when drop did not happen. | ||
| 53 | Timer::after_millis(100).await; | ||
| 54 | // worst case drop happens here | ||
| 55 | } else { | ||
| 56 | info!("waiting"); | ||
| 57 | } | ||
| 58 | // Sleep for 1 second. The executor ensures the core sleeps with a WFE when it has nothing to do. | ||
| 59 | // During this sleep, the nRF chip should only use ~3uA | ||
| 60 | Timer::after_secs(1).await; | ||
| 61 | } | ||
| 62 | } | ||
