Skip to content

STM32WLE5JC serial stop working when deepsleep #15331

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
shiwzhi opened this issue Sep 15, 2022 · 18 comments
Closed

STM32WLE5JC serial stop working when deepsleep #15331

shiwzhi opened this issue Sep 15, 2022 · 18 comments

Comments

@shiwzhi
Copy link

shiwzhi commented Sep 15, 2022

Description of defect

Target(s) affected by this defect ?

STM32WLE5JC

Toolchain(s) (name and version) displaying this defect ?

ARMC6

What version of Mbed-os are you using (tag or sha) ?

807fd79

What version(s) of tools are you using. List all that apply (E.g. mbed-cli)

Mbed Studio 1.4.4.1 on Mac OS

How is this defect reproduced ?

#include "mbed.h"

#define TARGET_TX_PIN                                                     PA_2
#define TARGET_RX_PIN                                                     PA_3

UnbufferedSerial serial_port(TARGET_TX_PIN, TARGET_RX_PIN, 9600);

int main(void)
{
    
    while(1) {
        serial_port.write("hello\n", 6);
        ThisThread::sleep_for(5s);     //  Serial stop working no output after
    }
    
}
#include "mbed.h"

#define TARGET_TX_PIN                                                     PA_2
#define TARGET_RX_PIN                                                     PA_3

int main(void)
{
    
    while(1) {
        UnbufferedSerial serial_port(TARGET_TX_PIN, TARGET_RX_PIN, 9600);   //reinit serial seems working
        serial_port.write("hello\n", 6);
        ThisThread::sleep_for(5s); 
    }
    
}

I'm using a STM32WLE5JC board with stm32customtargets, I found serial stop working when build with release profile and put device in deepsleep, I tried with STM32F401CCU6 target seems working fine.

@mbedmain
Copy link

@shiwzhi thank you for raising this issue.Please take a look at the following comments:

Could you add some more detail to the description? A good description should be at least 25 words.

NOTE: If there are fields which are not applicable then please just add 'n/a' or 'None'. This indicates to us that at least all the fields have been considered.
Please update the issue header with the missing information.

@stefan-npsys
Copy link

stefan-npsys commented Sep 15, 2022

Just for info (could be related). I see the same on the SEED E5 (also STM32WLE5JC, GCC, MBED 6.14) on the console stdio.

The workaround for me is to disable IO before sleep and enable after sleep. Something like:

mbed_file_handle(STDIN_FILENO)->enable_input(false);
mbed_file_handle(STDOUT_FILENO)->enable_output(false);
  ThisThread::sleep_for(5s);
mbed_file_handle(STDIN_FILENO)->enable_input(true);
mbed_file_handle(STDOUT_FILENO)->enable_output(true);

EDIT: I'm having another deep-sleep problem using I2C2 (which is not on I2C3), which leads me to the believe that MBED pulls the MCU into STOP-mode 2 on deep-sleep (and not STOP-1 as expected with most things retained). STOP-mode 2 has only PARTIAL RETENTION!!
Full RAM is fine, but many of the periphal blocks are NOT retained. Incuding UART1/2, and I2C1/2.. this could be it. You can use the LPUART it seems. (Unsure if it is a MBED flaw, but it is strange and annoying to workaround)
Check: Slide 34 in https://st-onlinetraining.s3.amazonaws.com/STM32WL-System-Power-control-PWR/index.html - it has a nice overview of the power modes (even though it's regarding STM32WL5)

@hallard
Copy link
Contributor

hallard commented Sep 20, 2022

Guys, this may explains why console is working on boot then all printf in the loRaWAN stuff does sort nothing.
But in my case as far as I remember looks like it is happening only in release mode. Need to check back this one
Would be great to have this one solved, @jeromecoutant any idea?

@0xc0170
Copy link
Contributor

0xc0170 commented Sep 26, 2022

cc @ARMmbed/team-st-mcd

@jeromecoutant
Copy link
Collaborator

MBED pulls the MCU into STOP-mode 2 on deep-sleep (and not STOP-1 as expected with most things retained)

Yes, Stop2 is used:
https://github.com/ARMmbed/mbed-os/blob/master/targets/TARGET_STM/sleep.c#L200

@hallard
Copy link
Contributor

hallard commented Jan 2, 2023

Any idea on how to fix this one, for information same code with Murata target (STM32L0) does not have this issue, but on STM32WL wake up after sleep does not works

As simple as

int main()
{
    #if defined (MBED_DEBUG)
    const uint8_t device_mode = 2 ; // Debug
    #elif defined (NDEBUG)
    const uint8_t device_mode = 0 ; // Release
    #else
    const uint8_t device_mode = 1 ; // Develop
    #endif
    int n = 100;
    printf(CRLF CRLF);
    printf("Hello World!"CRLF);
    printf(CRLF);
    printf("Compilation mode %s" CRLF, device_mode==2 ? "DEBUG" : device_mode==0 ? "RELEASE" : "DEVELOP");
    while (n--) {
        printf("Loop %d" CRLF, n);
        ThisThread::sleep_for(500ms);
    }
}

DEBUG

Hello World!

Compilation mode DEBUG
Loop 99
Loop 98
Loop 97
Loop 96
Loop 95
Loop 94
Loop 93
Loop 92

All other stuck with Serial not working anymore after sleep

RELEASE

Hello World!

Compilation mode RELEASE
Loop 99

DEVELOP

Hello World!

Compilation mode DEVELOP
Loop 99

@shiwzhi
Copy link
Author

shiwzhi commented Jan 6, 2023

Issue solved after set baudrate to 9600 and use lpuart pin
PA_2_ALT0、PA_3_ALT0 instead

"GENERIC_STM32WLE5JC": {
            "stm32wl-lora-driver.crystal_select": 0,
            "target.stdio_uart_tx": "PA_2_ALT0",
            "target.stdio_uart_rx": "PA_3_ALT0"
 }

@shiwzhi shiwzhi closed this as completed Jan 6, 2023
@JojoS62
Copy link
Contributor

JojoS62 commented Jan 6, 2023

a difference is that debug build does not enter deep sleep mode. You can try also to lock deep sleep:
https://os.mbed.com/docs/mbed-os/v6.15/apis/power-apis.html

@hallard
Copy link
Contributor

hallard commented Jan 6, 2023

Issue solved after set baudrate to 9600 and use lpuart pin PA_2_ALT0、PA_3_ALT0 instead

"GENERIC_STM32WLE5JC": {
            "stm32wl-lora-driver.crystal_select": 0,
            "target.stdio_uart_tx": "PA_2_ALT0",
            "target.stdio_uart_rx": "PA_3_ALT0"
 }

Indeed does works (on this sample I need to check with my LoRaWAN test), amazing but from my point of view it's just a workaround not a real fix, and tried with 19200 does not works either and this is not looking a "normal thing"

any idea from mbed guru's @jeromecoutant @0xc0170 ?

@jeromecoutant
Copy link
Collaborator

Hi
Maybe check table 37 from STM32WLEx Reference Manual:
Mbed-os deepsleep is ST stop 2 mode, and there is some difference between USART1&2 and LPUART

@hallard
Copy link
Contributor

hallard commented Jan 9, 2023

Yeah makes sense difference between LP_UART and USART (like all ST) and I have no problem with that. What I don't understand (and I can't find any reference) is why when we wake from deep sleep (I except wake from LP_UART of course) from any GPIO or RTC for example, why console works back at 9600 and not 115200, does this means that STOP2 does not retain baud rate and waking up reset LP_UART default speed to 9600?

@hallard
Copy link
Contributor

hallard commented Feb 23, 2023

Sorry for revival but looking at documentation LPUART1 is able to wake from STOP2,

image

I'm using LPUART1 and it works at 9600 but only at 9600 any hint to search place why at other speed wake from sleep stop console working and I see nothing in datasheet indicating that it should not work?

@jeromecoutant
Copy link
Collaborator

Hi
About LPUART and low power mode, I know:

  • there is some limitation depending on clock source: see 6.2.21 Peripheral clocks enable => HSI16, LSI, or LSE
  • there is limitation for baud rate: see 34.1 LPUART introduction
    => Only 32.768kHz LSE clock is required to enable UART communications up to 9600 baud/s. Higher baud rates can be reached when the LPUART is clocked by clock sources different from the LSE clock

@hallard
Copy link
Contributor

hallard commented Feb 24, 2023

Thanks for information, interesting.
Do you know where in mbed-os is located stuff done on wake-up for serial console?
I think we may need to add something for this target to reconfigure back console (in case of using LPUART) to baud rate (or other parameter) it was before sleeping. I can take a look and do some test if I have any entry point.

Thanks

@hallard
Copy link
Contributor

hallard commented Feb 24, 2023

FYI just tried these 3 options without any changes

"target.lpuart_clock_source": "USE_LPUART_CLK_HSI",
"target.lpuart_clock_source": "USE_LPUART_CLK_LSE",
"target.lpuart_clock_source": "USE_LPUART_CLK_PCLK1",

@hallard
Copy link
Contributor

hallard commented May 2, 2023

Just found this document, just putting here as a reminder, interesting

ultralowpower of stm32wl

@m0rphex
Copy link

m0rphex commented Aug 25, 2023

With the e5 mini I'm trying to transmit at 1200 baud after deep sleep without success. 4800 baud works. Any insight on how to get the slower baud working would be much appreciated.

EDIT:
Setting the baud after deep sleep, before the write works. myserial.baud(1200); . I suspect that lpuart is not in use after setting the baud, but i need to investigate. In any case I should not have to redefine the baudrate.

It could be that the LSE clock is too high for 1200 baud and would need to be divided down, which in turn would most likely break a lot of other stuff. I'm not very fluent with the mbed target initialization code.

@hallard
Copy link
Contributor

hallard commented Oct 10, 2023

Just as a reminder we found a workaround rewriting hal_deepsleep() for STM32WL

This is the example to setback console after STOP2 deep sleep

This code works on STM32WL only should be adapted for any other target, you've been warned

Anyway you have also ability to select on the fly STOP1 or STOP2 mode with var deep_sleep_mode

// just waked, we have some stuff to do (example UART2) and may have some
// sleep in between that may goes us to STOP2 powering off all our job (UART2 as an example)
// So disable STOP2 let's work on STOP1 (consumption difference in between is not a big deal)
deep_sleep_mode = DEEPSLEEP_MODE_STOP1 ;

// Do you stuf (UART2 / LPTIM2, ...) you can wait/sleep in your stuff

// Finished our stuff, get back to STOP2 for long sleep
deep_sleep_mode = DEEPSLEEP_MODE_STOP2 ;

// Now sleep until something to do
// ...

And here the file where the magic happens, we called deep_sleep_override.cpp in our project

#include "hal/static_pinmap.h"
#include "platform/mbed_critical.h"
#include "sensors/sensors.h"

#define DEEPSLEEP_MODE_STOP1 1
#define DEEPSLEEP_MODE_STOP2 2
volatile uint8_t deep_sleep_mode = DEEPSLEEP_MODE_STOP2;

extern serial_t stdio_uart;

// May be check if console is enabled in mbed_conf ?
void restart_console()
{
    static const serial_pinmap_t console_pinmap = get_uart_pinmap(CONSOLE_TX, CONSOLE_RX);
    serial_init_direct(&stdio_uart, &console_pinmap);
#if MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE
    int baud = MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE;
# else
    int baud = 9600;
#endif 
    serial_baud(&stdio_uart, baud);
}

extern "C"
{
    extern int mbed_sdk_inited;
    extern int serial_is_tx_ongoing(void);
    extern void save_timer_ctx(void);
    extern void restore_timer_ctx(void);
    extern void PWR_EnterStopMode(void);
    extern void PWR_ExitStopMode(void);
    extern void SetSysClock(void);
    extern void ForceOscOutofDeepSleep(void);
    extern void ForcePeriphOutofDeepSleep(void);
    extern void wait_loop(uint32_t timeout);
}

/*  Most of STM32 targets can have the same generic deep sleep
 *  function, but a few targets might need very specific sleep
 *  mode management, so this function is defined as WEAK.
 *  This one works on STM32WL only */
void hal_deepsleep(void)
{
    /*  WORKAROUND:
     *  MBED serial driver does not handle deepsleep lock
     *  to prevent entering deepsleep until HW serial FIFO is empty.
     *  This is tracked in mbed issue 4408 */
    if (serial_is_tx_ongoing())
    { /* FIXME: return or postpone deepsleep? */
        return;
    }

    // Disable IRQs
    core_util_critical_section_enter();

    save_timer_ctx();

    // Request to enter STOP mode with regulator in low power mode
    int pwrClockEnabled     = __HAL_RCC_PWR_IS_CLK_ENABLED();
    int lowPowerModeEnabled = PWR->CR1 & PWR_CR1_LPR;

    if (!pwrClockEnabled)
    {
        __HAL_RCC_PWR_CLK_ENABLE();
    }
    if (lowPowerModeEnabled)
    {
        HAL_PWREx_DisableLowPowerRunMode();
    }

    // If deep sleep stop 1 set it, else use default stop 2
    if (deep_sleep_mode == DEEPSLEEP_MODE_STOP1)
    {
        HAL_PWREx_EnterSTOP1Mode(PWR_STOPENTRY_WFI);
    }
    else
    {
        HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
    }

    if (lowPowerModeEnabled)
    {
        HAL_PWREx_EnableLowPowerRunMode();
    }
    if (!pwrClockEnabled)
    {
        __HAL_RCC_PWR_CLK_DISABLE();
    }

    /* Prevent HAL_GetTick() from using ticker_read_us() to read the
     * us_ticker timestamp until the us_ticker context is restored. */
    mbed_sdk_inited = 0;

    /* We've seen unstable PLL CLK configuration when DEEP SLEEP exits just few
     * µs after being entered So we need to force clock init out of Deep Sleep.
     *  This init has been split into 2 separate functions so that the involved
     * structures are not allocated on the stack in parallel. This will reduce
     * the maximum stack usage in case on non-optimized / debug compilers
     * settings
     */
    ForceOscOutofDeepSleep();
    ForcePeriphOutofDeepSleep();
    SetSysClock();

    /*  Wait for clock to be stabilized.
     *  TO DO: a better way of doing this, would be to rely on
     *  HW Flag. At least this ensures proper operation out of
     *  deep sleep */
    /* Charles : Tried those 3 with no luck, so kept wait_loop(500)
       while (!(RCC->CR & RCC_CR_HSERDY));
       while (!(RCC->CR & RCC_CR_PLLRDY));
       while (!(RCC->CR & RCC_CR_HSIRDY));
   */
    wait_loop(500);

    restore_timer_ctx();

    /* us_ticker context restored, allow HAL_GetTick() to read the us_ticker
     * timestamp via ticker_read_us() again. */
    mbed_sdk_inited = 1;

    // Enable IRQs
    core_util_critical_section_exit();

    // Enable back serial console
    restart_console();

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants