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.
This commit is contained in:
Thomas Kolb 2021-06-12 15:57:19 +02:00
parent 5ceb014d1b
commit df3211333e
7 changed files with 61 additions and 11 deletions

View file

@ -56,7 +56,7 @@
/* Generic configuration */ /* Generic configuration */
/* Time (in ms) to stay active in idle state before entering deep sleep. */ /* Time (in ms) to stay active in idle state before entering deep sleep. */
#define DEEPSLEEP_DELAY 5000 #define DEEPSLEEP_DELAY 1000
/* Deep sleep duration (in seconds). */ /* Deep sleep duration (in seconds). */
#define DEEPSLEEP_DURATION 10 #define DEEPSLEEP_DURATION 10

View file

@ -118,9 +118,9 @@ void deepsleep(uint32_t duration_secs)
lock_rtc_access(); lock_rtc_access();
// enter deep sleep mode // enter deep sleep mode
SCB_SCR |= SCB_SCR_SLEEPDEEP;
pwr_voltage_regulator_low_power_in_stop();
pwr_set_stop_mode(); pwr_set_stop_mode();
pwr_voltage_regulator_low_power_in_stop();
SCB_SCR |= SCB_SCR_SLEEPDEEP;
__WFI(); __WFI();
SCB_SCR &= ~SCB_SCR_SLEEPDEEP; // no deepsleep except in this function SCB_SCR &= ~SCB_SCR_SLEEPDEEP; // no deepsleep except in this function

View file

@ -105,3 +105,17 @@ void led_chplex_mask(uint8_t mask)
{ {
led_mask = mask; led_mask = mask;
} }
void led_chplex_deepsleep_prepare(void)
{
// set all GPIOs to input to ensure the LEDs are properly switched off
gpio_mode_setup(LED_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, LED_ALL_PINS);
}
void led_chplex_deepsleep_resume(void)
{
// nothing to do here
}

View file

@ -19,4 +19,7 @@ void led_chplex_toggle(uint8_t idx);
void led_chplex_mask(uint8_t mask); void led_chplex_mask(uint8_t mask);
void led_chplex_deepsleep_prepare(void);
void led_chplex_deepsleep_resume(void);
#endif // LED_CHPLEX_H #endif // LED_CHPLEX_H

View file

@ -156,6 +156,27 @@ static void report_status(struct MeasurementResult *meas_data)
} }
static void low_power_mode(uint32_t duration_sec)
{
// stop the systick counter for reliable deep sleep
systick_counter_disable();
// prepare the individual modules for sleep
rs485_deepsleep_prepare();
led_chplex_deepsleep_prepare();
// enter deep sleep for the given duration
deepsleep(duration_sec);
// resume the modules
led_chplex_deepsleep_resume();
rs485_deepsleep_resume();
systick_counter_enable();
rs485_enqueue("PWR:DEEPSLEEP:EXIT\n");
}
int main(void) int main(void)
{ {
//uint32_t cpuload = 0; //uint32_t cpuload = 0;
@ -224,14 +245,7 @@ int main(void)
} else { } else {
// charge control already idle // charge control already idle
if((timebase_ms - charge_control_idle_since) > DEEPSLEEP_DELAY) { if((timebase_ms - charge_control_idle_since) > DEEPSLEEP_DELAY) {
systick_counter_disable(); low_power_mode(DEEPSLEEP_DURATION);
// force the RS485 driver into receive mode. The RS485
// driver alone consumes 150 mW if enabled.
gpio_clear(RS485_PORT, RS485_DE_PIN);
deepsleep(DEEPSLEEP_DURATION);
gpio_set(RS485_PORT, RS485_DE_PIN);
systick_counter_enable();
rs485_enqueue("PWR:DEEPSLEEP:EXIT\n");
charge_control_was_idle = false; charge_control_was_idle = false;
} }
} }

View file

@ -120,3 +120,19 @@ void rs485_enqueue(const char *str)
rs485_transfer(); rs485_transfer();
} }
} }
void rs485_deepsleep_prepare(void)
{
// force the transceiver to RX mode
gpio_clear(RS485_PORT, RS485_DE_PIN);
}
void rs485_deepsleep_resume(void)
{
// if a transmission is still in progress, switch to TX mode again
if(usartActive) {
gpio_set(RS485_PORT, RS485_DE_PIN);
}
}

View file

@ -6,4 +6,7 @@ void rs485_periodic(void);
void rs485_enqueue(const char *str); void rs485_enqueue(const char *str);
void rs485_deepsleep_prepare(void);
void rs485_deepsleep_resume(void);
#endif // RS485_H #endif // RS485_H