From df3211333ed2129c5a7f1cd041435789fe370395 Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Sat, 12 Jun 2021 15:57:19 +0200 Subject: [PATCH] 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. --- src/config.h | 2 +- src/deepsleep.c | 4 ++-- src/led_chplex.c | 14 ++++++++++++++ src/led_chplex.h | 3 +++ src/main.c | 30 ++++++++++++++++++++++-------- src/rs485.c | 16 ++++++++++++++++ src/rs485.h | 3 +++ 7 files changed, 61 insertions(+), 11 deletions(-) diff --git a/src/config.h b/src/config.h index bba8724..c3e341f 100644 --- a/src/config.h +++ b/src/config.h @@ -56,7 +56,7 @@ /* Generic configuration */ /* 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). */ #define DEEPSLEEP_DURATION 10 diff --git a/src/deepsleep.c b/src/deepsleep.c index f626eb2..a7f6425 100644 --- a/src/deepsleep.c +++ b/src/deepsleep.c @@ -118,9 +118,9 @@ void deepsleep(uint32_t duration_secs) lock_rtc_access(); // enter deep sleep mode - SCB_SCR |= SCB_SCR_SLEEPDEEP; - pwr_voltage_regulator_low_power_in_stop(); 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 diff --git a/src/led_chplex.c b/src/led_chplex.c index 0c1fc9d..86f138e 100644 --- a/src/led_chplex.c +++ b/src/led_chplex.c @@ -105,3 +105,17 @@ void led_chplex_mask(uint8_t 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 +} diff --git a/src/led_chplex.h b/src/led_chplex.h index 13961c3..0dd01ab 100644 --- a/src/led_chplex.h +++ b/src/led_chplex.h @@ -19,4 +19,7 @@ void led_chplex_toggle(uint8_t idx); void led_chplex_mask(uint8_t mask); +void led_chplex_deepsleep_prepare(void); +void led_chplex_deepsleep_resume(void); + #endif // LED_CHPLEX_H diff --git a/src/main.c b/src/main.c index e0bf945..4e8812d 100644 --- a/src/main.c +++ b/src/main.c @@ -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) { //uint32_t cpuload = 0; @@ -224,14 +245,7 @@ int main(void) } else { // charge control already idle if((timebase_ms - charge_control_idle_since) > DEEPSLEEP_DELAY) { - systick_counter_disable(); - // 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"); + low_power_mode(DEEPSLEEP_DURATION); charge_control_was_idle = false; } } diff --git a/src/rs485.c b/src/rs485.c index 1d34dfb..3c46457 100644 --- a/src/rs485.c +++ b/src/rs485.c @@ -120,3 +120,19 @@ void rs485_enqueue(const char *str) 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); + } +} diff --git a/src/rs485.h b/src/rs485.h index 9325daf..5a16e8f 100644 --- a/src/rs485.h +++ b/src/rs485.h @@ -6,4 +6,7 @@ void rs485_periodic(void); void rs485_enqueue(const char *str); +void rs485_deepsleep_prepare(void); +void rs485_deepsleep_resume(void); + #endif // RS485_H