kopia lustrzana https://github.com/SP8EBC/ParaTNC
1238 wiersze
30 KiB
C
1238 wiersze
30 KiB
C
/*
|
|
* pwr_save.c
|
|
*
|
|
* Created on: Aug 22, 2021
|
|
* Author: mateusz
|
|
*/
|
|
|
|
#include "pwr_save.h"
|
|
#include "pwr_save_configuration.h"
|
|
|
|
#include "stm32l4xx.h"
|
|
#include "system_stm32l4xx.h"
|
|
#include <stdint.h>
|
|
|
|
#include "wx_pwr_switch.h"
|
|
#include "io.h"
|
|
#include "LedConfig.h"
|
|
#include "packet_tx_handler.h"
|
|
#include "wx_handler.h"
|
|
#include "main.h"
|
|
#include "backup_registers.h"
|
|
#include "status.h"
|
|
#include "afsk_pr.h"
|
|
#include "gsm/sim800c.h"
|
|
#include "aprsis.h"
|
|
#include "it_handlers.h"
|
|
#include "main.h"
|
|
|
|
#include "rte_main.h"
|
|
|
|
#include "drivers/analog_anemometer.h"
|
|
|
|
#define IN_STOP2_MODE (1 << 1)
|
|
#define IN_C0_STATE (1 << 2)
|
|
#define IN_C1_STATE (1 << 3)
|
|
#define IN_C2_STATE (1 << 4)
|
|
#define IN_C3_STATE (1 << 5)
|
|
#define IN_M4_STATE (1 << 6)
|
|
#define IN_I5_STATE (1 << 7)
|
|
#define IN_L6_STATE (1 << 8)
|
|
#define IN_L7_STATE (1 << 9)
|
|
|
|
#define MINIMUM_SENSEFUL_VBATT_VOLTAGE 678u
|
|
|
|
/**
|
|
* How long a controller should be woken up in aggressive powersaving mode
|
|
* before it will send a frame to APRS-IS and go sleep once again. This should
|
|
* be long enought to connect to APRS server and go sleep once again
|
|
*/
|
|
#define WAKEUP_PERIOD_BEFORE_WX_FRAME_IN_MINUTES 2
|
|
|
|
#if defined(STM32L471xx)
|
|
|
|
int8_t pwr_save_seconds_to_wx = 0;
|
|
int16_t pwr_save_sleep_time_in_seconds = -1;
|
|
|
|
/**
|
|
* Number of 30 seconds cycles of SLEEP2 in L6 and L7 powersave mode
|
|
*/
|
|
int8_t pwr_save_number_of_sleep_cycles = -1;
|
|
|
|
/**
|
|
* Variable stores cutoff state and to save RAM it also keeps a low battery voltage flag
|
|
*/
|
|
int8_t pwr_save_currently_cutoff = 0;
|
|
|
|
/**
|
|
* This stores a previous value of 'pwr_save_currently_cutoff' which is required to
|
|
* trigger a status message when controller goes into low battery voltage or cutoff state
|
|
*/
|
|
int8_t pwr_save_previously_cutoff = 0;
|
|
|
|
/**
|
|
* This is cutoff voltage at which the power saving subsystem will keep ParaMETEO constantly
|
|
* in L7 mode and wakeup once every 20 minutes to check B+ once again
|
|
*/
|
|
const uint16_t pwr_save_cutoff_voltage = PWR_SAVE_CUTOFF_VOLTAGE_DEF;
|
|
|
|
/**
|
|
* This is the restore voltage a battery must be charged to for ParaMETEO to restore it's normal operation
|
|
*/
|
|
const uint16_t pwr_save_startup_restore_voltage = PWR_SAVE_STARTUP_RESTORE_VOLTAGE_DEF;
|
|
|
|
/**
|
|
* Below this voltage (and above pwr_save_cutoff_voltage) software will switch powersaving
|
|
* mode to PWSAVE_AGGRESV
|
|
*/
|
|
const uint16_t pwr_save_aggressive_powersave_voltage = PWR_SAVE_AGGRESIVE_POWERSAVE_VOLTAGE;
|
|
|
|
static void pwr_save_unclock_rtc_backup_regs(void) {
|
|
// enable access to backup domain
|
|
PWR->CR1 |= PWR_CR1_DBP;
|
|
}
|
|
|
|
static void pwr_save_lock_rtc_backup_regs(void) {
|
|
PWR->CR1 &= (0xFFFFFFFF ^ PWR_CR1_DBP);
|
|
}
|
|
|
|
static void pwr_save_clear_powersave_idication_bits() {
|
|
// unlock access to backup registers
|
|
pwr_save_unclock_rtc_backup_regs();
|
|
|
|
// clear all previous powersave indication bits
|
|
backup_reg_reset_all_powersave_states();
|
|
|
|
// lock access to backup
|
|
pwr_save_lock_rtc_backup_regs();
|
|
}
|
|
|
|
/**
|
|
* Entering STOP2 power save mode. In this mode all clocks except LSI and LSE are disabled. StaticRAM content
|
|
* is preserved, optionally GPIO and few other peripherals can be kept power up depending on configuration
|
|
*/
|
|
static void pwr_save_enter_stop2(void) {
|
|
|
|
// set 31st monitor bit
|
|
backup_reg_set_monitor(31);
|
|
|
|
// reload internal watchdog
|
|
main_reload_internal_wdg();
|
|
|
|
// clear main battery voltage to be sure that it'd be updated???
|
|
rte_main_battery_voltage = 0;
|
|
|
|
analog_anemometer_deinit();
|
|
|
|
// clear previous low power mode selection
|
|
PWR->CR1 &= (0xFFFFFFFF ^ PWR_CR1_LPMS_Msk);
|
|
|
|
// select STOP2
|
|
PWR->CR1 |= PWR_CR1_LPMS_STOP2;
|
|
|
|
// enable write access to RTC registers by writing two magic words
|
|
RTC->WPR = 0xCA;
|
|
RTC->WPR = 0x53;
|
|
|
|
// unlock an access to backup domain
|
|
pwr_save_unclock_rtc_backup_regs();
|
|
|
|
// save an information that STOP2 mode has been applied
|
|
RTC->BKP0R |= IN_STOP2_MODE;
|
|
|
|
// save a timestamp when micro has been switched to STOP2 mode
|
|
backup_reg_set_last_sleep_timestamp();
|
|
|
|
pwr_save_lock_rtc_backup_regs();
|
|
|
|
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
|
|
|
|
DBGMCU->CR &= (0xFFFFFFFF ^ (DBGMCU_CR_DBG_SLEEP_Msk | DBGMCU_CR_DBG_STOP_Msk | DBGMCU_CR_DBG_STANDBY_Msk));
|
|
|
|
// disabling all IRQs
|
|
//__disable_irq();
|
|
|
|
asm("sev");
|
|
asm("wfi");
|
|
|
|
}
|
|
|
|
/**
|
|
* Used after each of 30 seconds long STOP2 sleep, to check
|
|
* how many sleeps the micro must be put in, to complete
|
|
* L6/L7 powersave mode
|
|
*/
|
|
static void pwr_save_check_stop2_cycles(void) {
|
|
|
|
while(1) {
|
|
// decrement stop2 cycles for current L7 or L6 powersave mode
|
|
pwr_save_number_of_sleep_cycles--;
|
|
|
|
// if there is time left to exit from depp sleep
|
|
if (pwr_save_number_of_sleep_cycles > 0) {
|
|
backup_reg_set_monitor(15);
|
|
|
|
// go back to sleep
|
|
// configure how long micro should sleep
|
|
system_clock_configure_auto_wakeup_l4(PWR_SAVE_STOP2_CYCLE_LENGHT_SEC);
|
|
|
|
pwr_save_enter_stop2();
|
|
}
|
|
else {
|
|
backup_reg_set_monitor(14);
|
|
|
|
// we are done sleeping so exit from this loop
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function has to be called after last 30 second long cycle of STOP2 sleep,
|
|
* to bounce all frames transmission counters.
|
|
*/
|
|
static void pwr_save_exit_after_last_stop2_cycle(void) {
|
|
|
|
uint32_t counter = 0;
|
|
|
|
// set 30th minitor bit
|
|
backup_reg_set_monitor(30);
|
|
|
|
// unlock access to backup registers
|
|
pwr_save_unclock_rtc_backup_regs();
|
|
|
|
// save a timestamp of this wakeup event
|
|
//REGISTER_LAST_WKUP = RTC->TR;
|
|
backup_reg_set_last_wakeup_timestamp();
|
|
|
|
// increase wakeup counter
|
|
counter = backup_reg_get_wakeup_counter();
|
|
|
|
counter++;
|
|
|
|
// store current wakeup counter in RTE
|
|
rte_main_wakeup_count = counter;
|
|
|
|
// check counter overflow conditions
|
|
if (counter > 0xFFFF) {
|
|
counter = 0;
|
|
}
|
|
|
|
backup_reg_set_wakeup_counter(counter);
|
|
|
|
pwr_save_lock_rtc_backup_regs();
|
|
|
|
// packet tx timers values
|
|
packet_tx_counter_values_t timers;
|
|
|
|
// check power saving mode set before switching uC to SLEEP2
|
|
uint16_t powersave_mode = backup_reg_get_powersave_state();//(uint16_t)(REGISTER & ALL_STATES_BITMASK);
|
|
|
|
// check if sleep time is valid
|
|
if (pwr_save_sleep_time_in_seconds <= 0) {
|
|
// if for some reason the value is not valid change is to something meaningful
|
|
pwr_save_sleep_time_in_seconds = 60;
|
|
}
|
|
|
|
main_reset_pooling_timers();
|
|
|
|
switch(powersave_mode) {
|
|
case IN_L6_STATE:
|
|
case IN_L7_STATE:
|
|
|
|
// get all timers values
|
|
packet_tx_get_current_counters(&timers);
|
|
|
|
// rewind all timers in packet tx handler as they were no updated when micro was sleeping
|
|
// sleep shall be always set as wx packet interval minus one minute
|
|
timers.wx_counter += (pwr_save_sleep_time_in_seconds / 60);
|
|
timers.gsm_wx_counter += (pwr_save_sleep_time_in_seconds / 60);
|
|
timers.beacon_counter += (pwr_save_sleep_time_in_seconds / 60);
|
|
timers.kiss_counter += (pwr_save_sleep_time_in_seconds / 60);
|
|
timers.telemetry_counter += (pwr_save_sleep_time_in_seconds / 60);
|
|
timers.telemetry_desc_counter += (pwr_save_sleep_time_in_seconds / 60);
|
|
|
|
if ((pwr_save_currently_cutoff & CURRENTLY_CUTOFF) == 0) {
|
|
// set counters back
|
|
packet_tx_set_current_counters(&timers);
|
|
}
|
|
else {
|
|
packet_tx_set_current_counters(0);
|
|
}
|
|
|
|
break;
|
|
|
|
// something is screwed horribly as in all other modes a micro shall not be placed in STOP2 mode
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// reinitialize LEDs when controller goes out from sleep
|
|
it_handlers_inhibit_radiomodem_dcd_led = 0;
|
|
led_init();
|
|
|
|
backup_reg_set_monitor(29);
|
|
}
|
|
|
|
/**
|
|
* This function is called in two places within a pooler.
|
|
* 1st: just after the micro wakes up from STOP2 deep sleep caused by low battery
|
|
* voltage and returns from an interrupt
|
|
* from RTC interrupt handler.
|
|
* 2nd: just after the micro wakes up from STOP2 caues by aggressive powersave
|
|
* configuration.
|
|
*/
|
|
static void pwr_save_after_stop2_rtc_wakeup_it(void) {
|
|
// check if we are just after waking up from STOP2 mode
|
|
if (rte_main_woken_up == RTE_MAIN_WOKEN_UP_RTC_INTERRUPT) {
|
|
|
|
// if yes set curent state
|
|
rte_main_woken_up = RTE_MAIN_WOKEN_UP_AFTER_RTC_IT;
|
|
|
|
// check if this is an intermediate wakeup from STOP2
|
|
pwr_save_check_stop2_cycles();
|
|
|
|
system_clock_configure_l4();
|
|
|
|
pwr_save_exit_after_last_stop2_cycle();
|
|
|
|
rte_main_woken_up = RTE_MAIN_WOKEN_UP_EXITED;
|
|
}
|
|
|
|
}
|
|
|
|
int pwr_save_switch_mode_to_c0(void) {
|
|
|
|
if (backup_reg_is_in_powersave_state(IN_C0_STATE) != 0) {
|
|
return 0;
|
|
}
|
|
//backup_reg_is_in_powersave_state
|
|
|
|
// turn ON +5V_S
|
|
io___cntrl_vbat_s_enable();
|
|
|
|
io___cntrl_vbat_m_enable();
|
|
|
|
// turn ON +5V_R and VBATT_SW_R
|
|
io___cntrl_vbat_r_enable();
|
|
|
|
// turn ON +4V_G
|
|
io___cntrl_vbat_g_enable();
|
|
|
|
// turn ON +5V_C (SD card, PT100 interface and Op Amplifier)
|
|
io___cntrl_vbat_c_enable();
|
|
|
|
// deinhibit GSM modem
|
|
gsm_sim800_inhibit(0);
|
|
|
|
// unlock access to backup registers
|
|
pwr_save_unclock_rtc_backup_regs();
|
|
|
|
// clear all previous powersave indication bits
|
|
backup_reg_reset_all_powersave_states();
|
|
|
|
// set for C0 mode
|
|
backup_reg_set_powersave_state(IN_C0_STATE);
|
|
|
|
// lock access to backup
|
|
pwr_save_lock_rtc_backup_regs();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
// in HW-RevB this will disable external VHF radio!!
|
|
int pwr_save_switch_mode_to_c1(void) {
|
|
|
|
if (backup_reg_is_in_powersave_state(IN_C0_STATE) != 0) {
|
|
return 0;
|
|
}
|
|
|
|
// disconnect APRS-IS connection if it is established
|
|
aprsis_disconnect();
|
|
|
|
NVIC_DisableIRQ( USART3_IRQn );
|
|
|
|
// close and deconfigure port used for communication with GPRS module
|
|
srl_close(main_gsm_srl_ctx_ptr);
|
|
|
|
// turn ON +5V_S (and internal VHF radio module in HW-RevB)
|
|
io___cntrl_vbat_s_enable();
|
|
|
|
io___cntrl_vbat_m_enable();
|
|
|
|
// turn ON +5V_R and VBATT_SW_R
|
|
io___cntrl_vbat_r_enable();
|
|
|
|
// turn OFF +4V_G
|
|
io___cntrl_vbat_g_disable();
|
|
|
|
// turn ON +5V_C (SD card, PT100 interface and Op Amplifier)
|
|
io___cntrl_vbat_c_enable();
|
|
|
|
// inhibit GSM modem
|
|
gsm_sim800_inhibit(1);
|
|
|
|
// unlock access to backup registers
|
|
pwr_save_unclock_rtc_backup_regs();
|
|
|
|
// clear all previous powersave indication bits
|
|
backup_reg_reset_all_powersave_states();
|
|
|
|
// set for C0 mode
|
|
backup_reg_set_powersave_state(IN_C1_STATE);
|
|
|
|
// lock access to backup
|
|
pwr_save_lock_rtc_backup_regs();
|
|
|
|
return 1;
|
|
}
|
|
|
|
// this mode is not avaliable in HW Revision B as internal radio
|
|
// is powered from +5V_S and external one is switched on with the same
|
|
// line which controls +4V_G
|
|
void pwr_save_switch_mode_to_c2(void) {
|
|
|
|
if (backup_reg_is_in_powersave_state(IN_C0_STATE) != 0) {
|
|
return;
|
|
}
|
|
|
|
// disconnect APRS-IS connection if it is established
|
|
aprsis_disconnect();
|
|
|
|
NVIC_DisableIRQ( USART3_IRQn );
|
|
|
|
// close and deconfigure port used for communication with GPRS module
|
|
srl_close(main_gsm_srl_ctx_ptr);
|
|
|
|
// turn OFF +5V_S (and internal VHF radio module in HW-RevB)
|
|
io___cntrl_vbat_s_disable();
|
|
|
|
io___cntrl_vbat_m_disable();
|
|
|
|
// turn ON +5V_R and VBATT_SW_R
|
|
io___cntrl_vbat_r_enable();
|
|
|
|
// turn OFF +4V_G
|
|
io___cntrl_vbat_g_disable();
|
|
|
|
// turn ON +5V_C (SD card, PT100 interface and Op Amplifier)
|
|
io___cntrl_vbat_c_enable();
|
|
|
|
// inhibit GSM modem
|
|
gsm_sim800_inhibit(1);
|
|
|
|
// unlock access to backup registers
|
|
pwr_save_unclock_rtc_backup_regs();
|
|
|
|
// clear all previous powersave indication bits
|
|
backup_reg_reset_all_powersave_states();
|
|
|
|
// set for C2 mode
|
|
backup_reg_set_powersave_state(IN_C2_STATE);
|
|
|
|
// lock access to backup
|
|
pwr_save_lock_rtc_backup_regs();
|
|
|
|
}
|
|
|
|
void pwr_save_switch_mode_to_c3(void) {
|
|
|
|
if (backup_reg_is_in_powersave_state(IN_C3_STATE) != 0) {
|
|
return;
|
|
}
|
|
|
|
// turn OFF +5V_S (and internal VHF radio module in HW-RevB)
|
|
io___cntrl_vbat_s_disable();
|
|
|
|
io___cntrl_vbat_m_disable();
|
|
|
|
// turn ON +5V_R and VBATT_SW_R
|
|
io___cntrl_vbat_r_enable();
|
|
|
|
// turn ON +4V_G
|
|
io___cntrl_vbat_g_enable();
|
|
|
|
// turn ON +5V_C (SD card, PT100 interface and Op Amplifier)
|
|
io___cntrl_vbat_c_enable();
|
|
|
|
// deinhibit GSM modem
|
|
gsm_sim800_inhibit(0);
|
|
|
|
// unlock access to backup registers
|
|
pwr_save_unclock_rtc_backup_regs();
|
|
|
|
// clear all previous powersave indication bits
|
|
backup_reg_reset_all_powersave_states();
|
|
|
|
// set for C3 mode
|
|
backup_reg_set_powersave_state(IN_C3_STATE);
|
|
|
|
// lock access to backup
|
|
pwr_save_lock_rtc_backup_regs();
|
|
|
|
}
|
|
|
|
// in HW-RevB this will keep internal VHF radio module working!
|
|
int pwr_save_switch_mode_to_m4(void) {
|
|
|
|
if (backup_reg_is_in_powersave_state(IN_M4_STATE) != 0) {
|
|
return 0;
|
|
}
|
|
|
|
// disconnect APRS-IS connection if it is established
|
|
aprsis_disconnect();
|
|
|
|
NVIC_DisableIRQ( USART3_IRQn );
|
|
|
|
// close and deconfigure port used for communication with GPRS module
|
|
srl_close(main_gsm_srl_ctx_ptr);
|
|
|
|
// turn ON +5V_S (and internal VHF radio module in HW-RevB)
|
|
io___cntrl_vbat_s_enable();
|
|
|
|
io___cntrl_vbat_m_enable();
|
|
|
|
// turn OFF +5V_R and VBATT_SW_R
|
|
io___cntrl_vbat_r_disable();
|
|
|
|
// turn OFF +4V_G
|
|
io___cntrl_vbat_g_disable();
|
|
|
|
// turn ON +5V_C (SD card, PT100 interface and Op Amplifier)
|
|
io___cntrl_vbat_c_enable();
|
|
|
|
// inhibit GSM modem
|
|
gsm_sim800_inhibit(1);
|
|
|
|
// unlock access to backup registers
|
|
pwr_save_unclock_rtc_backup_regs();
|
|
|
|
// clear all previous powersave indication bits
|
|
backup_reg_reset_all_powersave_states();
|
|
|
|
// set for C3 mode
|
|
backup_reg_set_powersave_state(IN_M4_STATE);
|
|
|
|
// lock access to backup
|
|
pwr_save_lock_rtc_backup_regs();
|
|
|
|
return 1;
|
|
}
|
|
|
|
int pwr_save_switch_mode_to_m4a(void) {
|
|
|
|
if (backup_reg_is_in_powersave_state(IN_M4_STATE) != 0) {
|
|
return 0;
|
|
}
|
|
|
|
// turn ON +5V_S (and internal VHF radio module in HW-RevB)
|
|
io___cntrl_vbat_s_enable();
|
|
|
|
io___cntrl_vbat_m_enable();
|
|
|
|
// turn OFF +5V_R and VBATT_SW_R
|
|
io___cntrl_vbat_r_disable();
|
|
|
|
// turn OFF +4V_G
|
|
io___cntrl_vbat_g_enable();
|
|
|
|
// turn ON +5V_C (SD card, PT100 interface and Op Amplifier)
|
|
io___cntrl_vbat_c_enable();
|
|
|
|
// deinhibit GSM modem
|
|
gsm_sim800_inhibit(0);
|
|
|
|
// unlock access to backup registers
|
|
pwr_save_unclock_rtc_backup_regs();
|
|
|
|
// clear all previous powersave indication bits
|
|
backup_reg_reset_all_powersave_states();
|
|
|
|
// set for C3 mode
|
|
backup_reg_set_powersave_state(IN_M4_STATE);
|
|
|
|
// lock access to backup
|
|
pwr_save_lock_rtc_backup_regs();
|
|
|
|
return 1;
|
|
}
|
|
|
|
void pwr_save_switch_mode_to_i5(void) {
|
|
|
|
if (backup_reg_is_in_powersave_state(IN_I5_STATE) != 0) {
|
|
return;
|
|
}
|
|
|
|
// disconnect APRS-IS connection if it is established
|
|
aprsis_disconnect();
|
|
|
|
NVIC_DisableIRQ( USART3_IRQn );
|
|
|
|
// close and deconfigure port used for communication with GPRS module
|
|
srl_close(main_gsm_srl_ctx_ptr);
|
|
|
|
// turn OFF +5V_S (and internal VHF radio module in HW-RevB)
|
|
io___cntrl_vbat_s_disable();
|
|
|
|
io___cntrl_vbat_m_disable();
|
|
|
|
// turn OFF +5V_R and VBATT_SW_R
|
|
io___cntrl_vbat_r_disable();
|
|
|
|
// turn OFF +4V_G
|
|
io___cntrl_vbat_g_disable();
|
|
|
|
// turn OFF +5V_C (SD card, PT100 interface and Op Amplifier)
|
|
io___cntrl_vbat_c_disable();
|
|
|
|
// inhibit GSM modem
|
|
gsm_sim800_inhibit(1);
|
|
|
|
// unlock access to backup registers
|
|
pwr_save_unclock_rtc_backup_regs();
|
|
|
|
// clear all previous powersave indication bits
|
|
backup_reg_reset_all_powersave_states();
|
|
|
|
// set for C3 mode
|
|
backup_reg_set_powersave_state(IN_I5_STATE);
|
|
|
|
// lock access to backup
|
|
pwr_save_lock_rtc_backup_regs();
|
|
|
|
}
|
|
|
|
// this will keep external VHF radio working in HW-RevB
|
|
void pwr_save_switch_mode_to_l6(uint16_t sleep_time) {
|
|
|
|
uint16_t counter = 0;
|
|
|
|
if (sleep_time > 3000u) {
|
|
// this is an error situation
|
|
sleep_time = 3000u;
|
|
}
|
|
|
|
if (system_is_rtc_ok() == 0) {
|
|
pwr_save_switch_mode_to_i5();
|
|
|
|
return;
|
|
}
|
|
|
|
if (backup_reg_is_in_powersave_state(IN_L6_STATE) != 0) {
|
|
return;
|
|
}
|
|
|
|
// calculate amount of STOP2 cycles
|
|
pwr_save_number_of_sleep_cycles = (int8_t)(sleep_time / PWR_SAVE_STOP2_CYCLE_LENGHT_SEC) & 0x7Fu;
|
|
|
|
backup_reg_set_monitor(28);
|
|
|
|
// turn off leds to save power
|
|
it_handlers_inhibit_radiomodem_dcd_led = 1;
|
|
led_control_led1_upper(false);
|
|
led_control_led2_bottom(false);
|
|
led_deinit();
|
|
|
|
// disconnect APRS-IS connection if it is established
|
|
aprsis_disconnect();
|
|
|
|
NVIC_DisableIRQ( USART3_IRQn );
|
|
|
|
// close and deconfigure port used for communication with GPRS module
|
|
srl_close(main_gsm_srl_ctx_ptr);
|
|
|
|
// disable ADC used for vbat measurement
|
|
io_vbat_meas_disable();
|
|
|
|
// stop DAC and ADC used for APRS
|
|
ADCStop();
|
|
DACStop();
|
|
|
|
// turn OFF +5V_S (and internal VHF radio module in HW-RevB)
|
|
io___cntrl_vbat_s_disable();
|
|
|
|
io___cntrl_vbat_m_disable();
|
|
|
|
// turn OFF +5V_R and VBATT_SW_R
|
|
io___cntrl_vbat_r_disable();
|
|
|
|
// turn ON +4V_G
|
|
io___cntrl_vbat_g_enable();
|
|
|
|
// turn OFF +5V_C (SD card, PT100 interface and Op Amplifier)
|
|
io___cntrl_vbat_c_disable();
|
|
|
|
// de inhibit GSM modem
|
|
gsm_sim800_inhibit(0);
|
|
|
|
// unlock access to backup registers
|
|
pwr_save_unclock_rtc_backup_regs();
|
|
|
|
// clear all previous powersave indication bits
|
|
backup_reg_reset_all_powersave_states();
|
|
|
|
// set for C3 mode
|
|
backup_reg_set_powersave_state(IN_L6_STATE);
|
|
|
|
backup_reg_set_last_sleep_timestamp();
|
|
|
|
// increment the STOP2 sleep counters
|
|
counter = backup_reg_get_sleep_counter();//(uint16_t)(REGISTER_COUNTERS & 0xFFFF);
|
|
|
|
counter++;
|
|
|
|
rte_main_going_sleep_count = counter;
|
|
|
|
backup_reg_set_sleep_counter(counter);
|
|
|
|
// lock access to backup
|
|
pwr_save_lock_rtc_backup_regs();
|
|
|
|
system_clock_configure_auto_wakeup_l4(PWR_SAVE_STOP2_CYCLE_LENGHT_SEC);
|
|
|
|
// save how long the micro will sleep - required for handling wakeup event
|
|
pwr_save_sleep_time_in_seconds = sleep_time;
|
|
|
|
pwr_save_enter_stop2();
|
|
|
|
backup_reg_set_monitor(27);
|
|
|
|
|
|
}
|
|
|
|
void pwr_save_switch_mode_to_l7(uint16_t sleep_time) {
|
|
|
|
uint16_t counter = 0;
|
|
|
|
if (sleep_time > 3000u) {
|
|
// this is an error situation
|
|
sleep_time = 3000u;
|
|
}
|
|
|
|
///////////
|
|
if (system_is_rtc_ok() == 0) {
|
|
pwr_save_switch_mode_to_i5();
|
|
|
|
return;
|
|
}
|
|
|
|
if (backup_reg_is_in_powersave_state(IN_L7_STATE) != 0) {
|
|
return;
|
|
}
|
|
|
|
// calculate amount of STOP2 cycles
|
|
pwr_save_number_of_sleep_cycles = (int8_t)(sleep_time / PWR_SAVE_STOP2_CYCLE_LENGHT_SEC) & 0x7Fu;
|
|
|
|
backup_reg_set_monitor(26);
|
|
|
|
// turn off leds to save power
|
|
it_handlers_inhibit_radiomodem_dcd_led = 1;
|
|
led_control_led1_upper(false);
|
|
led_control_led2_bottom(false);
|
|
led_deinit();
|
|
|
|
// disconnect APRS-IS connection if it is established
|
|
aprsis_disconnect();
|
|
|
|
// close and deconfigure port used for communication with GPRS module
|
|
srl_close(main_gsm_srl_ctx_ptr);
|
|
|
|
// disable ADC used for vbat measurement
|
|
io_vbat_meas_disable();
|
|
|
|
// stop DAC and ADC used for APRS
|
|
ADCStop();
|
|
DACStop();
|
|
|
|
// turn OFF +5V_S (and internal VHF radio module in HW-RevB)
|
|
io___cntrl_vbat_s_disable();
|
|
|
|
io___cntrl_vbat_m_disable();
|
|
|
|
// turn OFF +5V_R and VBATT_SW_R
|
|
io___cntrl_vbat_r_disable();
|
|
|
|
// turn OFF +4V_G
|
|
io___cntrl_vbat_g_disable();
|
|
|
|
// turn OFF +5V_C (SD card, PT100 interface and Op Amplifier)
|
|
io___cntrl_vbat_c_disable();
|
|
|
|
// inhibit GSM modem
|
|
gsm_sim800_inhibit(1);
|
|
|
|
// clear all previous powersave indication bits
|
|
backup_reg_reset_all_powersave_states();
|
|
|
|
// set for C3 mode
|
|
backup_reg_set_powersave_state(IN_L7_STATE);
|
|
|
|
//REGISTER_LAST_SLTIM = sleep_time;
|
|
backup_reg_set_last_sleep_timestamp();
|
|
|
|
// increment the STOP2 sleep counters
|
|
counter = backup_reg_get_sleep_counter();//(uint16_t)(REGISTER_COUNTERS & 0xFFFF);
|
|
|
|
counter++;
|
|
|
|
rte_main_going_sleep_count = counter;
|
|
|
|
backup_reg_set_sleep_counter(counter);
|
|
|
|
// configure how long micro should sleep
|
|
system_clock_configure_auto_wakeup_l4(PWR_SAVE_STOP2_CYCLE_LENGHT_SEC);
|
|
|
|
// save how long the micro will sleep - required for handling wakeup event
|
|
pwr_save_sleep_time_in_seconds = sleep_time;
|
|
|
|
pwr_save_enter_stop2();
|
|
|
|
backup_reg_set_monitor(25);
|
|
}
|
|
|
|
/**
|
|
* This function initializes everything related to power saving features
|
|
* including programming Flash memory option bytes
|
|
*/
|
|
void pwr_save_init(config_data_powersave_mode_t mode) {
|
|
|
|
// make a pointer to option byte
|
|
uint32_t* option_byte = (uint32_t*)0x1FFF7800;
|
|
|
|
// content of option byte read from the flash memory
|
|
uint32_t option_byte_content = *option_byte;
|
|
|
|
// definition of bitmask
|
|
#define IWDG_STBY_STOP (0x3 << 17)
|
|
|
|
// check if IWDG_STDBY and IWDG_STOP is set in ''User and read protection option bytes''
|
|
// at 0x1FFF7800
|
|
if ((option_byte_content & IWDG_STBY_STOP) != IWDG_STBY_STOP) {
|
|
|
|
// unlock write/erase operations on flash memory
|
|
FLASH->KEYR = 0x45670123;
|
|
FLASH->KEYR = 0xCDEF89AB;
|
|
|
|
// wait for any possible flash operation to finish (rather impossible here, but ST manual recommend doing this)
|
|
while((FLASH->SR & FLASH_SR_BSY) != 0);
|
|
|
|
// unlock operations on option bytes
|
|
FLASH->OPTKEYR = 0x08192A3B;
|
|
FLASH->OPTKEYR = 0x4C5D6E7F;
|
|
|
|
// set the flash option register (in RAM!!)
|
|
FLASH->OPTR |= FLASH_OPTR_IWDG_STDBY;
|
|
FLASH->OPTR |= FLASH_OPTR_IWDG_STOP;
|
|
|
|
// trigger an update of flash option bytes with values from RAM (from FLASH->OPTR)
|
|
FLASH->CR |= FLASH_CR_OPTSTRT;
|
|
|
|
// wait for option bytes to be updated
|
|
while((FLASH->SR & FLASH_SR_BSY) != 0);
|
|
|
|
// lock flash memory
|
|
FLASH-> CR |= FLASH_CR_LOCK;
|
|
|
|
// forcre reloading option bytes
|
|
FLASH->CR |= FLASH_CR_OBL_LAUNCH;
|
|
|
|
}
|
|
|
|
// reset a status register
|
|
backup_reg_reset_all_powersave_states();
|
|
backup_reg_reset_inhibit_periodic_pwr_switch();
|
|
|
|
// switch power switch handler inhibition if it is needed
|
|
switch (mode) {
|
|
case PWSAVE_NONE:
|
|
break;
|
|
case PWSAVE_NORMAL:
|
|
case PWSAVE_AGGRESV:
|
|
backup_reg_inhibit_periodic_pwr_switch();
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
config_data_powersave_mode_t pwr_save_pooling_handler( const config_data_mode_t * config,
|
|
const config_data_basic_t * timers,
|
|
int16_t minutes_to_wx,
|
|
uint16_t vbatt_average,
|
|
uint16_t vbatt_current) {
|
|
// this function should be called from 10 seconds pooler
|
|
|
|
int8_t reinit_sensors = 0;
|
|
|
|
int8_t reinit_gprs = 0;
|
|
|
|
packet_tx_counter_values_t counters;
|
|
|
|
// by default use powersave mode from controller configuration
|
|
config_data_powersave_mode_t psave_mode = config->powersave;
|
|
|
|
backup_reg_set_monitor(24);
|
|
|
|
// save previous state
|
|
pwr_save_previously_cutoff = pwr_save_currently_cutoff;
|
|
|
|
// check if battery voltage measurement is done and senseful
|
|
if (vbatt_average < MINIMUM_SENSEFUL_VBATT_VOLTAGE) {
|
|
// inhibit both cutoff and aggresive powersave if vbatt measurement is either not
|
|
// done at all or scaling factor are really screwed
|
|
vbatt_average = 0xFFFFu;
|
|
}
|
|
|
|
#ifdef INHIBIT_CUTOFF
|
|
vbatt_average = 0xFFFFu; // TODO:: THis shall not be uncommented on production!!!
|
|
#endif
|
|
|
|
if (vbatt_average > PWR_SAVE_STARTUP_RESTORE_VOLTAGE_DEF) {
|
|
pwr_save_currently_cutoff = 0;
|
|
|
|
backup_reg_set_monitor(23);
|
|
}
|
|
else {
|
|
if (vbatt_current <= PWR_SAVE_CUTOFF_VOLTAGE_DEF && vbatt_average <= (PWR_SAVE_CUTOFF_VOLTAGE_DEF + PWR_SAVE_CUTOFF_AVG_VOLTAGE_MARGIN)) {
|
|
backup_reg_set_monitor(22);
|
|
|
|
// if the battery voltage is below cutoff level and the ParaMETEO controller is currently not cut off
|
|
pwr_save_currently_cutoff |= CURRENTLY_CUTOFF;
|
|
}
|
|
// check if battery voltage is below low voltage level
|
|
else if (vbatt_average <= PWR_SAVE_AGGRESIVE_POWERSAVE_VOLTAGE) {
|
|
backup_reg_set_monitor(21);
|
|
|
|
// if battery voltage is low swtich to aggressive powersave mode
|
|
pwr_save_currently_cutoff |= CURRENTLY_VBATT_LOW;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
backup_reg_set_monitor(20);
|
|
|
|
// check if cutoff status has changed
|
|
if (pwr_save_currently_cutoff != pwr_save_previously_cutoff) {
|
|
status_send_powersave_cutoff(vbatt_average, pwr_save_previously_cutoff, pwr_save_currently_cutoff);
|
|
}
|
|
|
|
|
|
if ((pwr_save_currently_cutoff & CURRENTLY_CUTOFF) != 0) {
|
|
backup_reg_set_monitor(19);
|
|
|
|
// clear all previous powersave indication bits as we want to go sleep being already in L7 state
|
|
pwr_save_clear_powersave_idication_bits();
|
|
|
|
// go sleep immediately and periodically check if battery has been charged above restore level
|
|
pwr_save_switch_mode_to_l7(60 * PWR_SAVE_CUTOFF_SLEEP_TIME_IN_MINUTES);
|
|
|
|
// RTC interrupt is between this call and previous one (switching to l7)
|
|
pwr_save_after_stop2_rtc_wakeup_it();
|
|
|
|
return psave_mode;
|
|
}
|
|
|
|
if ((pwr_save_currently_cutoff & CURRENTLY_VBATT_LOW) != 0) {
|
|
backup_reg_set_monitor(18);
|
|
|
|
psave_mode = PWSAVE_AGGRESV;
|
|
}
|
|
|
|
|
|
// get current counter values
|
|
packet_tx_get_current_counters(&counters);
|
|
|
|
// decrement seconds in last minute
|
|
if (pwr_save_seconds_to_wx != -1) {
|
|
pwr_save_seconds_to_wx -= 10;
|
|
}
|
|
|
|
// if there is more than one minute to next frame
|
|
if (minutes_to_wx > 1) {
|
|
// reset counter as we dont
|
|
pwr_save_seconds_to_wx = -1;
|
|
}
|
|
else if (minutes_to_wx == 1 && pwr_save_seconds_to_wx == -1) {
|
|
// if this is the last second to wx frame
|
|
pwr_save_seconds_to_wx = 60;
|
|
}
|
|
|
|
// handle depends on current powersave configuration
|
|
switch (psave_mode) {
|
|
/**
|
|
* PWSAVE_NONE = 0,
|
|
PWSAVE_NORMAL = 1,
|
|
PWSAVE_AGGRESV = 3
|
|
*/
|
|
case PWSAVE_NONE : {
|
|
|
|
// if weather station is enabled
|
|
if (config->wx == 1) {
|
|
|
|
// if GSM modem is enabled in configuration
|
|
if (config->gsm == 1) {
|
|
|
|
// if digipeater is enabled
|
|
if (config->digi == 1) { // DIGI + WX + GSM
|
|
reinit_sensors = pwr_save_switch_mode_to_c0();
|
|
}
|
|
else { // WX + GSM
|
|
reinit_sensors = pwr_save_switch_mode_to_c0();
|
|
}
|
|
}
|
|
else {
|
|
// if digipeater is enabled
|
|
if (config->digi == 1) { // DIGI + WX
|
|
reinit_sensors = pwr_save_switch_mode_to_c1();
|
|
}
|
|
else { // WX
|
|
if (minutes_to_wx > 2) {
|
|
if (config->powersave_keep_gsm_always_enabled == 0){
|
|
reinit_sensors = pwr_save_switch_mode_to_m4();
|
|
}
|
|
else {
|
|
reinit_sensors = pwr_save_switch_mode_to_m4a();
|
|
}
|
|
}
|
|
else {
|
|
reinit_sensors = pwr_save_switch_mode_to_c0();
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
else { // DIGI
|
|
// if weather station is not enabled just stay in C2 mode
|
|
// as this is default state for DIGI operation. Of course
|
|
// DIGI might not be enabled (which has no sense) but for
|
|
// sake of simplicity just agree that it is.
|
|
pwr_save_switch_mode_to_c2();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case PWSAVE_NORMAL : {
|
|
|
|
// if weather station is enabled
|
|
if (config->wx == 1) {
|
|
|
|
// if GSM modem is enabled in configuration
|
|
if (config->gsm == 1) {
|
|
|
|
// if digipeater is enabled
|
|
if (config->digi == 1) { // DIGI + WX + GSM
|
|
// if weather packets are send 5 minutes or less often
|
|
if (timers->wx_transmit_period >= 5) {
|
|
if (minutes_to_wx > 1) {
|
|
pwr_save_switch_mode_to_c2();
|
|
|
|
//reinit_gprs = 1;
|
|
}
|
|
else {
|
|
reinit_sensors = pwr_save_switch_mode_to_c0();
|
|
}
|
|
}
|
|
else {
|
|
if (minutes_to_wx > 1) {
|
|
pwr_save_switch_mode_to_c3();
|
|
}
|
|
else {
|
|
reinit_sensors = pwr_save_switch_mode_to_c0();
|
|
}
|
|
}
|
|
}
|
|
else { // WX + GSM
|
|
if (minutes_to_wx > 1) {
|
|
if (config->powersave_keep_gsm_always_enabled == 0){
|
|
reinit_sensors = pwr_save_switch_mode_to_m4();
|
|
|
|
//reinit_gprs = 1;
|
|
}
|
|
else {
|
|
reinit_sensors = pwr_save_switch_mode_to_m4a();
|
|
}
|
|
}
|
|
else {
|
|
reinit_sensors = pwr_save_switch_mode_to_c0();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// if digipeater is enabled
|
|
if (config->digi == 1) { // DIGI + WX
|
|
if (minutes_to_wx > 1) {
|
|
pwr_save_switch_mode_to_c2();
|
|
}
|
|
else {
|
|
reinit_sensors = pwr_save_switch_mode_to_c1();
|
|
}
|
|
}
|
|
else { // WX
|
|
if (minutes_to_wx > WAKEUP_PERIOD_BEFORE_WX_FRAME_IN_MINUTES) {
|
|
backup_reg_set_monitor(17);
|
|
|
|
// if there is more than two minutes to send wx packet
|
|
pwr_save_switch_mode_to_l7((timers->wx_transmit_period * 60) - (WAKEUP_PERIOD_BEFORE_WX_FRAME_IN_MINUTES * 60));
|
|
|
|
// GSM module is kept turned on, but the connection must be reastablished
|
|
reinit_gprs = 1;
|
|
}
|
|
else {
|
|
// TODO: Workaround here for HW-RevB!!!
|
|
//reinit_sensors= pwr_save_switch_mode_to_c1();
|
|
reinit_sensors = pwr_save_switch_mode_to_c0();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else { // DIGI
|
|
pwr_save_switch_mode_to_c2();
|
|
}
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
case PWSAVE_AGGRESV : {
|
|
|
|
// if weather station is enabled
|
|
if (config->wx == 1) {
|
|
|
|
// if GSM modem is enabled in configuration
|
|
if (config->gsm == 1) {
|
|
|
|
// if digipeater is enabled
|
|
if (config->digi == 1) { // DIGI + WX + GSM
|
|
if (minutes_to_wx > 1) {
|
|
pwr_save_switch_mode_to_c2();
|
|
|
|
//reinit_gprs = 1;
|
|
}
|
|
else {
|
|
reinit_sensors = pwr_save_switch_mode_to_c0();
|
|
}
|
|
|
|
}
|
|
else { // WX + GSM (only)
|
|
if (minutes_to_wx > WAKEUP_PERIOD_BEFORE_WX_FRAME_IN_MINUTES) {
|
|
backup_reg_set_monitor(17);
|
|
|
|
// if there is more than WAKEUP_PERIOD_BEFORE_WX_FRAME_IN_MINUTES minutes to wx packet
|
|
pwr_save_switch_mode_to_l7((timers->wx_transmit_period * 60) - (WAKEUP_PERIOD_BEFORE_WX_FRAME_IN_MINUTES * 60)); // TODO: !!!
|
|
|
|
reinit_gprs = 1;
|
|
|
|
}
|
|
else {
|
|
// if there is 30 seconds or less to next wx packet
|
|
reinit_sensors = pwr_save_switch_mode_to_c0();
|
|
}
|
|
}
|
|
}
|
|
else { // gsm is not enabled
|
|
// if digipeater is enabled
|
|
if (config->digi == 1) { // DIGI + WX
|
|
if (minutes_to_wx > 1) {
|
|
pwr_save_switch_mode_to_c2();
|
|
}
|
|
else {
|
|
reinit_sensors = pwr_save_switch_mode_to_c1();
|
|
}
|
|
}
|
|
else { // WX
|
|
if (minutes_to_wx > WAKEUP_PERIOD_BEFORE_WX_FRAME_IN_MINUTES) {
|
|
backup_reg_set_monitor(17);
|
|
|
|
// if there is more than WAKEUP_PERIOD_BEFORE_WX_FRAME_IN_MINUTES minutes to send wx packet
|
|
pwr_save_switch_mode_to_l7((timers->wx_transmit_period * 60) - (WAKEUP_PERIOD_BEFORE_WX_FRAME_IN_MINUTES * 60));
|
|
|
|
reinit_gprs = 1;
|
|
}
|
|
else {
|
|
if (pwr_save_seconds_to_wx <= 30) {
|
|
// TODO: Workaround here for HW-RevB!!!
|
|
reinit_sensors= pwr_save_switch_mode_to_c1();
|
|
//pwr_save_switch_mode_to_c0();
|
|
|
|
// do not reinitialize everything as reinitialization had been done when switching to m4 mode
|
|
reinit_sensors = 0;
|
|
}
|
|
else {
|
|
if (config->powersave_keep_gsm_always_enabled == 0){
|
|
reinit_sensors = pwr_save_switch_mode_to_m4();
|
|
}
|
|
else {
|
|
reinit_sensors = pwr_save_switch_mode_to_m4a();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else { // DIGI
|
|
pwr_save_switch_mode_to_c2();
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
backup_reg_set_monitor(16);
|
|
|
|
pwr_save_after_stop2_rtc_wakeup_it();
|
|
|
|
backup_reg_set_monitor(13);
|
|
|
|
if (reinit_gprs != 0) {
|
|
// reset GSM modem, internally this also check if GSM modem is inhibited or not
|
|
rte_main_reset_gsm_modem = 1;
|
|
}
|
|
|
|
if (reinit_sensors != 0) {
|
|
// reinitialize all i2c sensors
|
|
wx_force_i2c_sensor_reset = 1;
|
|
|
|
// reinitialize everything realted to anemometer
|
|
analog_anemometer_init(main_config_data_mode->wx_anemometer_pulses_constant, 38, 100, 1);
|
|
}
|
|
|
|
return psave_mode;
|
|
}
|
|
|
|
int pwr_save_is_currently_cutoff(void) {
|
|
int out = 0;
|
|
|
|
if ((pwr_save_currently_cutoff & CURRENTLY_CUTOFF) != 0) {
|
|
out = 1;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
uint8_t pwr_save_get_inhibit_pwr_switch_periodic(void) {
|
|
|
|
if (backup_reg_is_periodic_pwr_switch_inhibited() != 0){
|
|
return 1;
|
|
}
|
|
else if ((pwr_save_currently_cutoff & CURRENTLY_CUTOFF) != 0) {
|
|
return 1;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
uint8_t pwr_save_get_inhibit_pwr_switch_periodic(void) {
|
|
return 0;
|
|
}
|
|
|
|
|
|
#endif
|
|
|