Skip to main content
makas005
Associate II
March 30, 2023
Question

STM32L1 HardFault after wakeup from sleep mode + fix

  • March 30, 2023
  • 3 replies
  • 2479 views

I would like to share a bug I encountered and how to fix it:

I am using an STM32L151, the goal is to set it into SLEEP mode and wake it up using a UART receive interrupt while debugging. All configurations to pins, registers and interrupts are made correctly.

The problem:

MCU goes to sleep just fine. When sending data over UART, the MCU wakes up, but doesnt enter the UART RX ISR. Instead it enters the HardFaultHandler. Readout of the Bus Fault Status Register (BFSR) of the Configurable Fault Status Registers (CFSR) shows that bit 0 is set, indicating an Instruction bus error.

The fix:

Full credit goes to https://web.archive.org/web/20210625090753/https://lists.riot-os.org/pipermail/devel/2016-September/004534.html.

The fix states, before entering wait-for-interrupt instruction, one must disable interrupts (they will still trigger wakeup and be registered), then entering wait-for-interrupt, then issuing a nop-instruction, then re-enabling interrupts.

The fix requires to modify the HAL/SPL, so be careful when using CubeMX code generation. In my case I am using the old Standard Preipheral Library, so in the file "stm32l1xx_pwr.c" in function "PWR_EnterSleepMode" i changed

 if(PWR_SLEEPEntry == PWR_SLEEPEntry_WFI)
 { 
 /* Request Wait For Interrupt */
 __DSB();
 __WFI();
 __NOP();
 __ISB();
 }

to

 if(PWR_SLEEPEntry == PWR_SLEEPEntry_WFI)
 { 
 /* Request Wait For Interrupt */
 __DSB();
 __disable_irq();
 asm ("DMB");
 __WFI();
 asm ("nop");
 __enable_irq();
 
 __NOP();
 __ISB();
 }

This solves the problem. It might be necessary in some cases to insert more than one nop-Instruction after the wfi instruction.

3 replies

Technical Moderator
March 30, 2023

Hello @makas005​ ,

Thanks for sharing the issue and fix :thumbs_up:

I will check this reported issue and take the necessary action.

An internal ticket (ID 148705) is submitted related to this issue.

(PS: ID 148705 is an internal tracking number and is not accessible or usable by customers).

Imen

In order to give better visibility on the answered topics, please click on 'Best answer' on the reply which solved your issue or answered your question. Thanks
Technical Moderator
April 7, 2023

Hi @@makas005 ,

Can you please precise which package version are you using?

This soft code is not existed in the STSW-STM32077 - STM32L1xx standard peripherals library:

if(PWR_SLEEPEntry == PWR_SLEEPEntry_WFI)
 { 
 /* Request Wait For Interrupt */
 __DSB();
 __WFI();
 __NOP();
 __ISB();
 }

Imen

In order to give better visibility on the answered topics, please click on 'Best answer' on the reply which solved your issue or answered your question. Thanks
makas005
makas005Author
Associate II
April 11, 2023

Hi @Imen DAHMEN​, thank you for you quick response.

I am using SPL version 1.3.1. However, i see that the __DSB, __NOP and __ISB functions that i posted in the first code snippet are not present in the SPL that ST provides. They might have been added company internally.

However, i think that the problem can still occur when only the __WFI is used (as it is in the SPL download version), given that the archived forum post of riot-os states the exact same error.

Associate II
June 19, 2024

I'm sure not found in SPL1.3.1, I'm having a similar issue,STM32L151RET6 use low power run cause hardfault - STMicroelectronics Community   I use HAL. change the code to  

void HAL_PWR_EnterSLEEPMode(uint32_t Regulator, uint8_t SLEEPEntry)
{
/* Check the parameters */
assert_param(IS_PWR_REGULATOR(Regulator));
assert_param(IS_PWR_SLEEP_ENTRY(SLEEPEntry));

/* Select the regulator state in Sleep mode: Set PDDS and LPSDSR bit according to PWR_Regulator value */
MODIFY_REG(PWR->CR, (PWR_CR_PDDS | PWR_CR_LPSDSR), Regulator);

/* Clear SLEEPDEEP bit of Cortex System Control Register */
CLEAR_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));

/* Select SLEEP mode entry -------------------------------------------------*/
if(SLEEPEntry == PWR_SLEEPENTRY_WFI)
{
/* Request Wait For Interrupt */
__DSB();//add code
__WFI();
__NOP();//add code
__ISB();//add code
}
else
{
/* Request Wait For Event */
__SEV();
__WFE();
__WFE();
}
}


Problem solving。

 

//////////////////////////////////////////////////////////////////////////////////////////////////

Code in SPL 1.3.1

void PWR_EnterSleepMode(uint32_t PWR_Regulator, uint8_t PWR_SLEEPEntry)
{
uint32_t tmpreg = 0;

/* Check the parameters */
assert_param(IS_PWR_REGULATOR(PWR_Regulator));

assert_param(IS_PWR_SLEEP_ENTRY(PWR_SLEEPEntry));

/* Select the regulator state in Sleep mode ---------------------------------*/
tmpreg = PWR->CR;

/* Clear PDDS and LPDSR bits */
tmpreg &= CR_DS_MASK;

/* Set LPDSR bit according to PWR_Regulator value */
tmpreg |= PWR_Regulator;

/* Store the new value */
PWR->CR = tmpreg;

/* Clear SLEEPDEEP bit of Cortex System Control Register */
SCB->SCR &= (uint32_t)~((uint32_t)SCB_SCR_SLEEPDEEP);

/* Select SLEEP mode entry -------------------------------------------------*/
if(PWR_SLEEPEntry == PWR_SLEEPEntry_WFI)
{
/* Request Wait For Interrupt */
__WFI();
}
else
{
/* Request Wait For Event */
__SEV();
__WFE();
__WFE();
}
}

Associate II
June 19, 2024

These code in STM32L496RGT, It's a very simple line.

 

/* Select SLEEP mode entry -------------------------------------------------*/
if(SLEEPEntry == PWR_SLEEPENTRY_WFI)
{
/* Request Wait For Interrupt */
__WFI();
}