From 529b396fec1633269144bf92e2e8d3e0d965a30b Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 27 Nov 2024 16:18:40 +0000 Subject: [PATCH] STM32: Ensure we kick the WDT if auto kicking is enabled and in deep sleep (avoids having to do it manually and wait 30ms for USB to wake up/shut down) --- ChangeLog | 1 + src/jshardware.h | 7 +- src/jshardware_common.c | 8 ++ targets/nrf5x/jshardware.c | 11 +-- targets/stm32/jshardware.c | 150 +++++++++++++++++++++---------------- 5 files changed, 102 insertions(+), 75 deletions(-) diff --git a/ChangeLog b/ChangeLog index 707634df9..b1af537f9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -62,6 +62,7 @@ Pixl.js: Remove Wiznet W5100 support from default build (there's now a espruino_#v##_pixljs_wiznet.zip without JIT enabled) to ensure we have enough flash to continue builds Enable nostartfiles optimisation for Pixl,MDBT42 and nRF52DK STM32F4: Add SDIO support + STM32: Ensure we kick the WDT if auto kicking is enabled and in deep sleep (avoids having to to it manually and wait 30ms for USB to wake up/shut down) 2v24 : Bangle.js2: Add 'Bangle.touchRd()', 'Bangle.touchWr()' Bangle.js2: After Bangle.showTestScreen, put Bangle.js into a hard off state (not soft off) diff --git a/src/jshardware.h b/src/jshardware.h index d20d519e7..186ad9a1b 100644 --- a/src/jshardware.h +++ b/src/jshardware.h @@ -371,13 +371,14 @@ void jshResetRTCTimer(); void jshClearUSBIdleTimeout(); #endif -#if defined(NRF51_SERIES) || defined(NRF52_SERIES) /// Called when we have had an event that means we should execute JS extern void jshHadEvent(); +/// set if we've had an event we need to deal with +extern volatile bool jshHadEventDuringSleep; + +#if defined(NRF51_SERIES) || defined(NRF52_SERIES) /// Enable/disable(if level==NAN) the LPCOMP comparator bool jshSetComparator(Pin pin, JsVarFloat level); -#else -#define jshHadEvent() /* We should ensure we exit idle mode */ #endif /// the temperature from the internal temperature sensor, in degrees C diff --git a/src/jshardware_common.c b/src/jshardware_common.c index aeb3bd302..2856dd576 100644 --- a/src/jshardware_common.c +++ b/src/jshardware_common.c @@ -15,6 +15,9 @@ #include "jsinteractive.h" #include "platform_config.h" +/// set if we've had an event we need to deal with +volatile bool jshHadEventDuringSleep = false; + void jshUSARTInitInfo(JshUSARTInfo *inf) { inf->baudRate = DEFAULT_BAUD_RATE; inf->pinRX = PIN_UNDEFINED; @@ -166,6 +169,11 @@ void jshKickSoftWatchDog() { } } +/// Called when we have had an event that means we should execute JS +void jshHadEvent() { + jshHadEventDuringSleep = true; +} + /* Returns the estimated power usage of the microcontroller */ __attribute__((weak)) void jsvGetProcessorPowerUsage(JsVar *devices) { // not implemented by default diff --git a/targets/nrf5x/jshardware.c b/targets/nrf5x/jshardware.c index e18955b1b..6f9137e84 100644 --- a/targets/nrf5x/jshardware.c +++ b/targets/nrf5x/jshardware.c @@ -309,7 +309,6 @@ static uint8_t pwmClocks[PWM_COUNTERS]; /// For flash - whether it is busy or not... volatile bool flashIsBusy = false; -volatile bool hadEvent = false; // set if we've had an event we need to deal with unsigned int ticksSinceStart = 0; #if GPIO_COUNT>1 @@ -626,12 +625,6 @@ const nrf_drv_twis_t *jshGetTWIS(IOEventFlags device) { } #endif - -/// Called when we have had an event that means we should execute JS -void jshHadEvent() { - hadEvent = true; -} - void TIMER1_IRQHandler(void) { nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_CLEAR); nrf_timer_event_clear(NRF_TIMER1, NRF_TIMER_EVENT_COMPARE0); @@ -2746,7 +2739,7 @@ bool jshSleep(JsSysTime timeUntilWake) { #endif } jsiSetSleep(JSI_SLEEP_ASLEEP); - while (!hadEvent) { + while (!jshHadEventDuringSleep) { #ifdef NRF52_SERIES /* * Clear FPU exceptions. @@ -2765,7 +2758,7 @@ bool jshSleep(JsSysTime timeUntilWake) { while (app_usbd_event_queue_process()); /* Nothing to do */ #endif } - hadEvent = false; + jshHadEventDuringSleep = false; jsiSetSleep(JSI_SLEEP_AWAKE); #ifdef BLUETOOTH // we don't care about the return codes... diff --git a/targets/stm32/jshardware.c b/targets/stm32/jshardware.c index f06ce8a34..0aa429137 100644 --- a/targets/stm32/jshardware.c +++ b/targets/stm32/jshardware.c @@ -81,6 +81,9 @@ JsSysTime jshGetRTCSystemTime(); static JsSysTime jshGetTimeForSecond(); +/// Max time we can sleep in JsSysTime units for the watchdog timer - we need this so we don't get rebooted it auto kicking is enabled +uint32_t watchdogSleepMax; + // The amount of systicks for one second depends on the clock speed #define SYSTICKS_FOR_ONE_SECOND (1+(CLOCK_SPEED_MHZ*1000000/SYSTICK_RANGE)) @@ -2692,6 +2695,8 @@ void jshClearUSBIdleTimeout() { /// Enter simple sleep mode (can be woken up by interrupts). Returns true on success bool jshSleep(JsSysTime timeUntilWake) { + bool isAutoWDT = jsiStatus & JSIS_WATCHDOG_AUTO; + #ifdef USE_RTC /* TODO: Check jsiGetConsoleDevice to make sure we don't have to wake on USART (we can't do this fast enough) @@ -2706,7 +2711,7 @@ bool jshSleep(JsSysTime timeUntilWake) { #else (timeUntilWake > (jshGetTimeForSecond()*16*2/jshRTCPrescaler)) && // if there's less time that this then we can't go to sleep because we can't be sure we'll wake in time #endif - !jstUtilTimerIsRunning() && // if the utility timer is running (eg. digitalPulse, Waveform output, etc) then that would stop + !jstUtilTimerIsRunning() && // if the utility timer is running (eg. digitalPulse, Waveform output, etc) then that would stop so we can't sleep !jshHasTransmitData() && // if we're transmitting, we don't want USART/etc to get slowed down #ifdef USB !USB_IsConnected() && @@ -2729,93 +2734,104 @@ bool jshSleep(JsSysTime timeUntilWake) { ADC_Cmd(ADC4, DISABLE); // ADC off #endif #ifdef USB - jshSetUSBPower(false); + jshSetUSBPower(false); // WARNING: takes 25ms + bool wokenByUSB = false; #endif // USB + do { // we loop here so we can half-wake to kick the WDT without incurring wait for USB + JsSysTime timeToSleep = timeUntilWake; + if (isAutoWDT && timeToSleep>watchdogSleepMax) + timeToSleep = watchdogSleepMax; + if (timeUntilWake==JSSYSTIME_MAX) timeUntilWake = 0; // if we're just waiting for as long as possible + else timeUntilWake -= timeToSleep; + if (isAutoWDT) jshKickWatchDog(); /* Add EXTI for Serial port */ //jshPinWatch(JSH_PORTA_OFFSET+10, true); /* add exti for USB */ #ifdef USB #ifdef STM32F1 - // USB has 15k pull-down resistors (and STM32 has 40k pull up) - Pin usbPin = JSH_PORTA_OFFSET+11; - jshPinSetState(usbPin, JSHPINSTATE_GPIO_IN_PULLUP); - Pin oldWatch = watchedPins[pinInfo[usbPin].pin]; - jshPinWatch(usbPin, true, JSPW_NONE); + // USB has 15k pull-down resistors (and STM32 has 40k pull up) + Pin usbPin = JSH_PORTA_OFFSET+11; + jshPinSetState(usbPin, JSHPINSTATE_GPIO_IN_PULLUP); + Pin oldWatch = watchedPins[pinInfo[usbPin].pin]; + jshPinWatch(usbPin, true, JSPW_NONE); #endif #ifdef USB_VSENSE_PIN - // USB_VSENSE_PIN is connected to USB 5v (and pulled down by a 100k resistor) - // ... so wake up if it goes high - Pin oldWatch = watchedPins[pinInfo[USB_VSENSE_PIN].pin]; - jshPinWatch(USB_VSENSE_PIN, true, JSPW_NONE); + // USB_VSENSE_PIN is connected to USB 5v (and pulled down by a 100k resistor) + // ... so wake up if it goes high + Pin oldWatch = watchedPins[pinInfo[USB_VSENSE_PIN].pin]; + jshPinWatch(USB_VSENSE_PIN, true, JSPW_NONE); #endif #endif // USB - if (timeUntilWake!=JSSYSTIME_MAX) { // set alarm - unsigned int ticks = (unsigned int)(timeUntilWake/jshGetTimeForSecond()); // ensure we round down and leave a little time + if (timeToSleep!=JSSYSTIME_MAX) { // set alarm + unsigned int ticks = (unsigned int)(timeToSleep/jshGetTimeForSecond()); // ensure we round down and leave a little time #ifdef STM32F1 - /* If we're going asleep for more than a few seconds, - * add one second to the sleep time so that when we - * wake up, we execute our timer immediately (even if it is a bit late) - * and don't waste power in shallow sleep. This is documented in setInterval */ - if (ticks>3) ticks++; // sleep longer than we need - - RTC_SetAlarm(RTC_GetCounter() + ticks); - RTC_ITConfig(RTC_IT_ALR, ENABLE); - //RTC_AlarmCmd(RTC_Alarm_A, ENABLE); - RTC_WaitForLastTask(); + /* If we're going asleep for more than a few seconds, + * add one second to the sleep time so that when we + * wake up, we execute our timer immediately (even if it is a bit late) + * and don't waste power in shallow sleep. This is documented in setInterval */ + if (ticks>3) ticks++; // sleep longer than we need + + RTC_SetAlarm(RTC_GetCounter() + ticks); + RTC_ITConfig(RTC_IT_ALR, ENABLE); + //RTC_AlarmCmd(RTC_Alarm_A, ENABLE); + RTC_WaitForLastTask(); #else // If available, just use the WakeUp counter - if (ticks < ((65536*16) / jshRTCPrescaler)) { - // if the delay is small enough, clock the WakeUp counter faster so we can sleep more accurately - RTC_WakeUpClockConfig(RTC_WakeUpClock_RTCCLK_Div16); - ticks = (unsigned int)((timeUntilWake*jshRTCPrescaler) / (jshGetTimeForSecond()*16)); - } else { // wakeup in seconds - RTC_WakeUpClockConfig(RTC_WakeUpClock_CK_SPRE_16bits); - if (ticks > 65535) ticks = 65535; - } - RTC_SetWakeUpCounter(ticks - 1); // 0 based - RTC_ITConfig(RTC_IT_WUT, ENABLE); - RTC_WakeUpCmd(ENABLE); - RTC_ClearFlag(RTC_FLAG_WUTF); + if (ticks < ((65536*16) / jshRTCPrescaler)) { + // if the delay is small enough, clock the WakeUp counter faster so we can sleep more accurately + RTC_WakeUpClockConfig(RTC_WakeUpClock_RTCCLK_Div16); + ticks = (unsigned int)((timeToSleep*jshRTCPrescaler) / (jshGetTimeForSecond()*16)); + } else { // wakeup in seconds + RTC_WakeUpClockConfig(RTC_WakeUpClock_CK_SPRE_16bits); + if (ticks > 65535) ticks = 65535; + } + RTC_SetWakeUpCounter(ticks - 1); // 0 based + RTC_ITConfig(RTC_IT_WUT, ENABLE); + RTC_WakeUpCmd(ENABLE); + RTC_ClearFlag(RTC_FLAG_WUTF); #endif - } - // set flag in case there happens to be a SysTick - hasSystemSlept = true; - // ----------------------------------------------- + } + // set flag in case there happens to be a SysTick + hasSystemSlept = true; + // ----------------------------------------------- #ifdef STM32F4 - /* FLASH Deep Power Down Mode enabled */ - PWR_FlashPowerDownCmd(ENABLE); + /* FLASH Deep Power Down Mode enabled */ + PWR_FlashPowerDownCmd(ENABLE); #endif - /* Request to enter STOP mode with regulator in low power mode*/ - PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); - // ----------------------------------------------- - if (timeUntilWake!=JSSYSTIME_MAX) { // disable alarm + /* Request to enter STOP mode with regulator in low power mode*/ + PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); + // ----------------------------------------------- + if (timeToSleep!=JSSYSTIME_MAX) { // disable alarm #ifdef STM32F1 - RTC_ITConfig(RTC_IT_ALR, DISABLE); - //RTC_AlarmCmd(RTC_Alarm_A, DISABLE); + RTC_ITConfig(RTC_IT_ALR, DISABLE); + //RTC_AlarmCmd(RTC_Alarm_A, DISABLE); #else - RTC_ITConfig(RTC_IT_WUT, DISABLE); - RTC_WakeUpCmd(DISABLE); + RTC_ITConfig(RTC_IT_WUT, DISABLE); + RTC_WakeUpCmd(DISABLE); #endif - } + } #ifdef USB - bool wokenByUSB = false; + wokenByUSB = false; #ifdef STM32F1 - wokenByUSB = jshPinGetValue(usbPin)==0; - // remove watches on pins - jshPinWatch(usbPin, false, JSPW_NONE); - if (oldWatch!=PIN_UNDEFINED) jshPinWatch(oldWatch, true, JSPW_NONE); - jshPinSetState(usbPin, JSHPINSTATE_GPIO_IN); + wokenByUSB = jshPinGetValue(usbPin)==0; + // remove watches on pins + jshPinWatch(usbPin, false, JSPW_NONE); + if (oldWatch!=PIN_UNDEFINED) jshPinWatch(oldWatch, true, JSPW_NONE); + jshPinSetState(usbPin, JSHPINSTATE_GPIO_IN); #endif #ifdef USB_VSENSE_PIN - // remove watch and restore old watch if there was one - // setting that we've woken lets the board stay awake - // until a USB connection can be established - if (jshPinGetValue(USB_VSENSE_PIN)) wokenByUSB=true; - jshPinWatch(USB_VSENSE_PIN, false, JSPW_NONE); - if (oldWatch!=PIN_UNDEFINED) jshPinWatch(oldWatch, true, JSPW_NONE); + // remove watch and restore old watch if there was one + // setting that we've woken lets the board stay awake + // until a USB connection can be established + if (jshPinGetValue(USB_VSENSE_PIN)) wokenByUSB=true; + jshPinWatch(USB_VSENSE_PIN, false, JSPW_NONE); + if (oldWatch!=PIN_UNDEFINED) jshPinWatch(oldWatch, true, JSPW_NONE); #endif + } while (timeUntilWake>0 && !wokenByUSB && !jshHadEventDuringSleep); + jshHadEventDuringSleep = false; + if (isAutoWDT) jshKickWatchDog(); #endif // recover oscillator RCC_HSEConfig(RCC_HSE_ON); @@ -2827,7 +2843,7 @@ bool jshSleep(JsSysTime timeUntilWake) { } RTC_WaitForSynchro(); // make sure any RTC reads will be done #ifdef USB - jshSetUSBPower(true); + jshSetUSBPower(true); // WARNING: takes 3ms if (wokenByUSB) jshLastWokenByUSB = jshGetRTCSystemTime(); #endif @@ -2837,6 +2853,11 @@ bool jshSleep(JsSysTime timeUntilWake) { if (timeUntilWake > jshGetTimeFromMilliseconds(ESPR_MIN_WFI_TIME_MS)) { /* don't bother sleeping if the time period is so low we * might miss the timer */ + + // Dont' sleep too long if auto WDT enabled (we'll kick when we go around idle loop) + if (isAutoWDT && timeUntilWake > watchdogSleepMax) + timeUntilWake = watchdogSleepMax; + JsSysTime sysTickTime; #ifdef USE_RTC sysTickTime = expectedSysTickTime*5/4; @@ -3003,6 +3024,9 @@ void jshEnableWatchDog(JsVarFloat timeout) { /* Enable IWDG (the LSI oscillator will be enabled by hardware) */ IWDG_Enable(); + + // save timeout so when we sleep we don't sleep so long we get rebooted (use wdt time / 2) + watchdogSleepMax = (uint32_t)jshGetTimeFromMilliseconds(timeout*1000 / 2); } // Kick the watchdog