LNSC-2420-Firmware/src/deepsleep.c
Thomas Kolb df3211333e Properly shut down peripherals before entering deep sleep
This is most important for the LEDs and the RS485 interface. If any LED
is on when deep sleep is entered, it will stay on the whole sleep time,
drawing about 10 mA on the 3,3V rail.

The RS485 transceiver must be switched to RX mode, as it consumes about
30 mA from the 5V rail in TX mode.
2021-06-12 16:02:21 +02:00

137 lines
3 KiB
C

#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/pwr.h>
#include <libopencm3/stm32/rtc.h>
#include <libopencmsis/core_cm3.h>
#include <libopencm3/stm32/exti.h>
#include "clock.h"
#include "deepsleep.h"
#include "libopencm3/stm32/f0/rcc.h"
void init_rtc(void)
{
// RTC clock setup
// see libopencm3-examples::examples/stm32/l1/stm32l-discovery/button-irq-printf-lowpower/main.c
/* turn on power block to enable unlocking */
rcc_periph_clock_enable(RCC_PWR);
pwr_disable_backup_domain_write_protect();
/* reset rtc */
RCC_BDCR |= RCC_BDCR_BDRST;
RCC_BDCR &= ~RCC_BDCR_BDRST;
// use LSI for RTC
rcc_osc_on(RCC_LSI);
rcc_wait_for_osc_ready(RCC_LSI);
/* Select the LSI as rtc clock */
RCC_BDCR |= RCC_BDCR_RTCSEL_LSI;
/* ?! Stdperiph examples don't turn this on until _afterwards_ which
* simply doesn't work. It must be on at least to be able to
* configure it */
rcc_enable_rtc_clock();
pwr_enable_backup_domain_write_protect();
nvic_enable_irq(NVIC_RTC_IRQ);
exti_set_trigger(EXTI17, EXTI_TRIGGER_RISING);
exti_enable_request(EXTI17);
}
static void unlock_rtc_access(void)
{
pwr_disable_backup_domain_write_protect();
rtc_unlock();
}
static void lock_rtc_access(void)
{
rtc_lock();
pwr_enable_backup_domain_write_protect();
}
void deepsleep(uint32_t duration_secs)
{
uint32_t tmp = 0;
// unlock RTC registers
unlock_rtc_access();
// enter initialization mode
rtc_set_init_flag();
rtc_wait_for_init_ready();
RTC_TR = 0; // 00:00:00
RTC_DR = // friday, 01.01.16
(1 << RTC_DR_YT_SHIFT) |
(6 << RTC_DR_YU_SHIFT) |
(5 << RTC_DR_WDU_SHIFT) |
(0 << RTC_DR_MT_SHIFT) |
(1 << RTC_DR_MU_SHIFT) |
(0 << RTC_DR_DT_SHIFT) |
(1 << RTC_DR_DU_SHIFT);
// disable Alarm A
RTC_CR &= ~RTC_CR_ALRAE;
// wait until register is writeable
while((RTC_ISR & RTC_ISR_ALRAWF) != RTC_ISR_ALRAWF) {
// do nothing
}
tmp |= (duration_secs % 10) << RTC_ALRMXR_SU_SHIFT;
duration_secs /= 10;
tmp |= (duration_secs % 6) << RTC_ALRMXR_ST_SHIFT;
duration_secs /= 6;
tmp |= (duration_secs % 10) << RTC_ALRMXR_MNU_SHIFT;
duration_secs /= 10;
tmp |= (duration_secs % 6) << RTC_ALRMXR_MNT_SHIFT;
duration_secs /= 6;
tmp |= (duration_secs % 10) << RTC_ALRMXR_HU_SHIFT;
duration_secs /= 10;
tmp |= (duration_secs % 2) << RTC_ALRMXR_HT_SHIFT;
// FIXME: >1d is not supported
tmp |= RTC_ALRMXR_MSK4; // ignore day/date
// set alarm register
RTC_ALRMAR = tmp;
// clear Alarm A flag
RTC_ISR &= ~RTC_ISR_ALRAF;
// enable RTC alarm interrupt for wakeup
RTC_CR |= RTC_CR_ALRAE | RTC_CR_ALRAIE;
// leave initialization mode
rtc_clear_init_flag();
// lock registers again (using invalid key)
lock_rtc_access();
// enter deep sleep mode
pwr_set_stop_mode();
pwr_voltage_regulator_low_power_in_stop();
SCB_SCR |= SCB_SCR_SLEEPDEEP;
__WFI();
SCB_SCR &= ~SCB_SCR_SLEEPDEEP; // no deepsleep except in this function
//rcc_periph_clock_disable(RCC_PWR); // no longer needed
//rcc_osc_off(RCC_LSI);
init_clock(); // ensure that all clocks are running again
}
void rtc_isr(void)
{
exti_reset_request(EXTI17);
}