Thomas Kolb
df3211333e
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.
137 lines
3 KiB
C
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);
|
|
}
|
|
|