gfroerli / firmware

Firmware for the water temperature sensor project
GNU General Public License v3.0
6 stars 1 forks source link

Low power optimization #72

Open dbrgn opened 4 years ago

dbrgn commented 4 years ago
dbrgn commented 3 years ago

The double scan was fixed in #83.

dbrgn commented 3 years ago

Here are the low power modes:

2021-01-05-235302_1089x1237_scrot 2021-01-05-235313_1093x478_scrot

In principle we could use standby with an RTC wakeup, which only consumes 0.29µA. Then we don't get RAM retention though.

Without RAM retention, we need a way to track wakeup counters. EEPROM guarantees at least 100'000 cycles, that means at a wakeup interval of 5 minutes we'd get roughly a year's worth of cycles (if we write once per wakeup).

The other option would be creating our own simple wear leveling system. The EEPROM page size is 1 word (4 bytes). For example, we could keep the counter at BASE_ADDR + OFFSET. The BASE_ADDR would be constant, while the offset is increased every n (e.g. 1<<15 = 32768) cycles. That way we'd use 1 word of memory every 113 days, until EEPROM runs out 🙂 (And we have 6 KiB EEPROM, so with 256 bytes we'd get 19 years of lifetime.)

@rnestler what do you think?

dbrgn commented 3 years ago

@rnestler ping 🙂

By the way, here are two example scripts to test low power mode:

examples/standby.rs

//! A firmware that does nothing but putting the device into standby mode.
//! Used to test standby current draw.
//!
//! To flash:
//!
//!     $ cargo embed flash --release --example standby
//!
//! Make sure to power cycle the device afterwards, to prevent the debug
//! peripherals from running.

#![no_main]
#![no_std]
#![cfg(target_arch = "arm")]

use cortex_m_rt::entry;

use panic_persist as _;
use stm32l0xx_hal::prelude::*;
use stm32l0xx_hal::{self as hal, pac};

#[entry]
fn main() -> ! {
    // Peripherals
    let mut cp = pac::CorePeripherals::take().unwrap();
    let dp = pac::Peripherals::take().unwrap();

    // Clock configuration. Use HSI at 16 MHz.
    let mut rcc = dp.RCC.freeze(hal::rcc::Config::hsi16());

    // Get access to PWR peripheral
    let mut pwr = hal::pwr::PWR::new(dp.PWR, &mut rcc);

    // Put device into standby
    let mut standby = pwr.standby_mode(&mut cp.SCB);
    standby.enter();
    loop { }
}

examples/wfi.rs

//! A firmware that does nothing but call WFI in a loop.
//! Used to test base current draw.
//!
//! To flash:
//!
//!     $ cargo embed flash --release --example wfi
//!
//! Make sure to power cycle the device afterwards, to prevent the debug
//! peripherals from running.

#![no_main]
#![no_std]
#![cfg(target_arch = "arm")]

use cortex_m_rt::entry;

use panic_persist as _;
use stm32l0xx_hal::prelude::*;
use stm32l0xx_hal::{self as hal, pac};

#[entry]
fn main() -> ! {
    let dp = pac::Peripherals::take().unwrap();

    // Clock configuration. Use HSI at 16 MHz.
    let _rcc = dp.RCC.freeze(hal::rcc::Config::hsi16());

    // Run WFI in a loop
    loop {
        cortex_m::asm::wfi();
    }
}
dbrgn commented 3 years ago

@rnestler RFC on this comment: https://github.com/gfroerli/firmware/issues/72#issuecomment-754962731

dbrgn commented 3 years ago

RTC wakeup configuration example: https://github.com/stm32-rs/stm32l0xx-hal/blob/master/examples/rtc_wakeup.rs

rnestler commented 3 years ago

In principle we could use standby with an RTC wakeup, which only consumes 0.29µA. Then we don't get RAM retention though.

Do we have a section of RAM which has retention even in standby?

dbrgn commented 3 years ago

Datasheet page 16:

After entering Standby mode, the RAM and register contents are lost except for registers in the Standby circuitry (wakeup logic, IWDG, RTC, LSI, LSE Crystal 32 KHz oscillator, RCC_CSR register).

So: No.

dbrgn commented 3 years ago

Since we only need to track the wakeup cycles, we could use the RTC. If we use midnight as starting point, we can round to the next full cycle to find out what data to send!

I'll try to come up with a PoC.