commit 0cf5239314e282da11ef78bd87d605a5d8eff6b3
parent 45e6ddae7200ce6c53abd1d685123d462182036a
Author: Lukas F. Hartmann <lukas@mntre.com>
Date: Mon, 6 Sep 2021 20:15:05 +0200
LPC: when not charging, regularly go to deep sleep for 1 second
Diffstat:
1 file changed, 103 insertions(+), 31 deletions(-)
diff --git a/reform2-lpc-fw/src/boards/reform2/board_reform2.c b/reform2-lpc-fw/src/boards/reform2/board_reform2.c
@@ -37,9 +37,11 @@
#define REFORM_MBREV_R3 13 // R2 with "NTC instead of RNG/SS" fix
// don't forget to set this to the correct rev for your motherboard!
-#define REFORM_MOTHERBOARD_REV REFORM_MBREV_R2
+#define REFORM_MOTHERBOARD_REV REFORM_MBREV_R3
//#define REF2_DEBUG 1
-#define FW_REV "MREF2LPC R2 20210419"
+#define FW_REV "MREF2LPC R2 20210906"
+
+#define POWERSAVE_SLEEP_SECONDS 1
#define INA260_ADDRESS 0x4e
#define LTC4162F_ADDRESS 0x68
@@ -134,7 +136,8 @@ enum state_t {
ST_OVERVOLTED,
ST_UNDERVOLTED,
ST_MISSING,
- ST_FULLY_CHARGED
+ ST_FULLY_CHARGED,
+ ST_POWERSAVE
};
// charging state machine
@@ -143,6 +146,7 @@ int cycles_in_state = 0;
int charge_current = 1;
uint32_t cur_second = 0;
uint32_t last_second = 0;
+int powersave_holdoff_cycles = 10;
// 1.8A x 3600 seconds/hour
#define MAX_CAPACITY (1.8)*3600.0
@@ -447,33 +451,15 @@ void brownout_setup(void) {
}
void watchdog_feed(void) {
+ __disable_irq();
LPC_WWDT->FEED = 0xAA;
LPC_WWDT->FEED = 0x55;
+ __enable_irq();
}
#define WWDT_WDMOD_WDEN ((uint32_t) (1 << 0))
#define WWDT_WDMOD_WDRESET ((uint32_t) (1 << 1))
-void watchdog_setup(void) {
- LPC_SYSCON->SYSAHBCLKCTRL |= (1<<15); // WWDT enable
-
- LPC_SYSCON->WDTOSCCTRL =
- (1<<5) | // FREQSEL 0.6MHz
- 31; // DIVSEL 64 (31+1)*2
-
- LPC_SYSCON->PDRUNCFG &= ~(1<<6); // WDTOSC_PD disable
-
- LPC_WWDT->CLKSEL = 1; // WDOSC
-
- LPC_WWDT->TC = 0xffff/5; // timeout counter, ~5 seconds
-
- LPC_WWDT->MOD = 0;
- LPC_WWDT->MOD |= WWDT_WDMOD_WDRESET; // enable WDRESET (watchdog resets system)
- LPC_WWDT->MOD |= WWDT_WDMOD_WDEN; // watchdog enable
-
- watchdog_feed();
-}
-
void boardInit(void)
{
SystemCoreClockUpdate();
@@ -668,6 +654,8 @@ void handle_commands() {
sprintf(uartBuffer,FW_REV"cell missing,%d,%d,%d\r",cycles_in_state,min_mah,acc_mah);
} else if (state == ST_FULLY_CHARGED) {
sprintf(uartBuffer,FW_REV"full charge,%d,%d,%d\r",cycles_in_state,min_mah,acc_mah);
+ } else if (state == ST_POWERSAVE) {
+ sprintf(uartBuffer,FW_REV"pwrsv,%d,%lu\r",cycles_in_state,LPC_WWDT->TV);
} else {
sprintf(uartBuffer,FW_REV"unknown:%d,%d,%d,%d\r",state,cycles_in_state,min_mah,acc_mah);
}
@@ -798,6 +786,67 @@ void report_to_spi(void)
ssp0Send((uint8_t*)report, strlen(report));
}
+void WDT_IRQHandler(void)
+{
+ // Disable WDT interrupt
+ NVIC_DisableIRQ(WDT_IRQn);
+ NVIC_ClearPendingIRQ(WDT_IRQn);
+}
+
+// WARNING: take care not to overflow TC (11786 * secs)
+void deep_sleep_seconds(int secs) {
+ // make WWDTINT wake the LPC up from sleep
+ // STARTERP1 WWDTINT bit 12
+ LPC_SYSCON->STARTERP1 |= (1 << 12);
+
+ NVIC_DisableIRQ(WDT_IRQn);
+
+ // Configure watchdog timer to wake us up
+ LPC_SYSCON->SYSAHBCLKCTRL |= (1<<15); // WWDT enable
+
+ // 9375Hz??
+ LPC_SYSCON->WDTOSCCTRL =
+ (1<<5) | // FREQSEL 0.6MHz
+ 31; // DIVSEL 64 (31+1)*2
+
+ // Power configuration register
+ LPC_SYSCON->PDRUNCFG &= ~(1<<6); // WDTOSC_PD disable (power down disable)
+ LPC_SYSCON->PDSLEEPCFG &= ~(1<<6); // WDTOSC_PD disable (power down disable)
+ LPC_SYSCON->PDAWAKECFG = LPC_SYSCON->PDRUNCFG; // when waking up, power up the default blocks
+
+ LPC_WWDT->CLKSEL = 1; // WDOSC
+
+ //LPC_WWDT->TC = 0xffff/5; // timeout counter, ~5 seconds
+ // FIXME isn't that 1.39s? (0xffff/5.0937/5.0)
+ // no, apparently it is 5.56s (x4)
+
+ LPC_WWDT->TC = 11786 * secs; // timeout counter, ~1 second
+
+ LPC_WWDT->MOD = 0;
+ //LPC_WWDT->MOD |= WWDT_WDMOD_WDRESET; // WDRESET (watchdog resets system)
+ LPC_WWDT->MOD |= WWDT_WDMOD_WDEN; // watchdog enable
+
+ // counter value that triggers interrupt
+ LPC_WWDT->WARNINT = 0;
+
+ // need to feed WD once to apply WDMOD values
+ watchdog_feed();
+
+ // NVIC exception 25 (WWDT) interrupt source:
+ // ISER SETENA bit 25 -> 0xE000E100
+ NVIC_ClearPendingIRQ(WDT_IRQn);
+ NVIC_EnableIRQ(WDT_IRQn);
+
+ // Go to deep sleep mode
+ LPC_PMU->PCON = 1<<11; // clear DPDFLAG
+ LPC_PMU->PCON = 1; // select deep power down mode
+ SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
+ __WFI();
+
+ NVIC_DisableIRQ(WDT_IRQn);
+ LPC_WWDT->MOD = 0;
+}
+
int main(void)
{
boardInit();
@@ -808,11 +857,6 @@ int main(void)
last_second = delayGetSecondsActive();
- // WIP, not yet tested
- //watchdog_setup();
- //sprintf(uartBuffer, "\r\nwatchdog_setup() completed.\r\n");
- //uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
-
while (1)
{
// algorithm idea:
@@ -872,6 +916,13 @@ int main(void)
cycles_in_state = 0;
}
}
+ else if (current < 0.05 && current >= 0) {
+ // if not charging and the system is off, we can sleep regularly to save power
+ if (cycles_in_state > 0 && powersave_holdoff_cycles <= 0) {
+ state = ST_POWERSAVE;
+ cycles_in_state = 0;
+ }
+ }
}
else if (state == ST_UNDERVOLTED) {
// TODO: issue alert -- switch off system if critical
@@ -884,8 +935,10 @@ int main(void)
turn_som_power_off();
}
- state = ST_CHARGE;
- cycles_in_state = 0;
+ if (current >= 0 && powersave_holdoff_cycles <= 0) {
+ state = ST_POWERSAVE;
+ cycles_in_state = 0;
+ }
}
}
else if (state == ST_OVERVOLTED) {
@@ -932,10 +985,25 @@ int main(void)
}
}
}
+ else if (state == ST_POWERSAVE) {
+ deep_sleep_seconds(POWERSAVE_SLEEP_SECONDS);
+ state = ST_CHARGE;
+ cycles_in_state = 0;
+ }
// handle keyboard commands
+ if (uartRxBufferDataPending()) {
+ // don't go to powersave if keyboard wants to communicate
+ powersave_holdoff_cycles = 5;
+ }
+
handle_commands();
- cur_second = delayGetSecondsActive();
+
+ if (state == ST_POWERSAVE) {
+ cur_second+=POWERSAVE_SLEEP_SECONDS;
+ } else {
+ cur_second = delayGetSecondsActive();
+ }
if (last_second != cur_second) {
if (cur_second-last_second<10) {
@@ -947,6 +1015,10 @@ int main(void)
// report_to_spi();
}
last_second = cur_second;
+
+ if (powersave_holdoff_cycles>=0) {
+ powersave_holdoff_cycles--;
+ }
}
}
}