cancel
Showing results for 
Search instead for 
Did you mean: 

STM32U5G9: wfi() instruction does not execute, no shutdown or standby

robintechnology4u
Associate II

Hello,

I have a issue with my STM32U5G9. It does not go into standby or shutdown mode. I want to achieve a wake-up by the power button of my device. The powerbutton is connected to PWR_WKUP6 (pin PB5). The button has a 100k pull-up. I verified the voltage levels with a scope (3.3V --> 0V).

I checked with the examples in the STM32CubeU5 Github repo. This my code: 

void LowPower::EnterStandbyMode(void) {
    PrepareForLowPower();

    __disable_irq();
    for(int i = WWDG_IRQn; i <= JPEG_IRQn; i++) {
    	NVIC_ClearPendingIRQ((IRQn_Type)i);
    }

    /* Enable WakeUp Pin PWR_WAKEUP_PIN6 connected to PB.5 */
    HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN6_LOW_0);

    /* Clear all related wakeup flags*/
    __HAL_PWR_CLEAR_FLAG(PWR_WAKEUP_FLAG6);

    HAL_DBGMCU_DisableDBGStandbyMode();

    /* Enter the Standby mode */
    HAL_PWR_EnterSTANDBYMode();

    // If entry fails (e.g., if a debugger is attached), reset the system
    NVIC_SystemReset();
}

 I have a similar function for shutdown mode. I tried to clear all interrupt flags to make sure the WFI() instruction executes. I have the following code in my main.c:

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the System Power */
  SystemPower_Config();

  /* Configure the system clock */
  SystemClock_Config();

  /* Configure the peripherals common clocks */
  PeriphCommonClock_Config();

  /* USER CODE BEGIN SysInit */
  /* Uncomment to be able to debug after wake-up from Standby. Consuption will be increased */
  HAL_DBGMCU_EnableDBGStandbyMode();
  if (__HAL_PWR_GET_FLAG(PWR_FLAG_SBF) != RESET)
  {
	  /* Clear Standby flag */
	  __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SBF);
	  /* Check and Clear the Wakeup flag */
	  if (__HAL_PWR_GET_FLAG(PWR_WAKEUP_FLAG6) != RESET)
	  {
		__HAL_PWR_CLEAR_FLAG(PWR_WAKEUP_FLAG6);
	  }
  }
  /* USER CODE END SysInit */

I call the LowPower::EnterStandbyMode() after a long press of my power button, but it just runs into the NVIC_SystemReset(), with or without debugger connected. To test this I tried to put a while(1) loop in the main if the SBF flag is set, but it just starts normal operation.

Is there anything which can block the WFI() instruction? I use the following peripherals: LTDC, GPU2D, DMA2D, USB, I2C, SAI,TIMER, ICACHE, DCACHE, CRC, ADC, DMA, OCTOSPI, RNG. The code runs on ThreadX / TouchGFX. My assumption is standby and shutdown mode stop peripheral clocks and there is no need to disable. Is my assumption correct? 

Or can ThreadX block the sleep / standby? 

Thanks in advance.

Robin 

1 ACCEPTED SOLUTION

Accepted Solutions

I solved the issue, it was quite a search.
To get it to sleep, you need to:

  1. de-init all peripherals which use interrupts or DMA.
  2. Clear and disable all pending interrupts and events. 
  3. Suspend SysTick

Monitor NVIC->ISER and NVIC->ISPR. If one of them is not 0, shutdown / standby will not happen. 

 

View solution in original post

6 REPLIES 6
Saket_Om
ST Employee

Hello @robintechnology4u 

Please refer to the example below:

STM32CubeU5/Projects/NUCLEO-U545RE-Q/Examples/PWR/PWR_STANDBY at main · STMicroelectronics/STM32CubeU5

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
Saket_Om
robintechnology4u
Associate II

I got one step further.. just before I call HAL_PWR_EnterStandbyMode() I watched the contents of the NVIC peripheral in the debugger. The contents of the ISPR registers show that interrupt flag no. 135  doesnt clear (LTDC), so I guess I have my cause, now for the solution. How can I properly power down the LTDC? 

Hello @robintechnology4u 

Just before entering Standby/Shutdown, do something like:

void LTDC_StopAndDeinit(void)
{
    /* 1. Disable LTDC interrupts at peripheral */
    __HAL_LTDC_DISABLE_IT(&hltdc, LTDC_IT_LI | LTDC_IT_FU | LTDC_IT_TE | LTDC_IT_RR); 
    // adapt mask to what you actually use

    /* 2. Clear all pending LTDC interrupt flags */
    __HAL_LTDC_CLEAR_FLAG(&hltdc, LTDC_FLAG_LI | LTDC_FLAG_FU | LTDC_FLAG_TE | LTDC_FLAG_RR);

    /* 3. Disable LTDC */
    __HAL_LTDC_DISABLE(&hltdc); // or HAL_LTDC_DeInit(&hltdc);

    /* 4. Disable NVIC interrupt */
    HAL_NVIC_DisableIRQ(LTDC_IRQn);

    /* 5. Clear any pending in NVIC (after source is disabled/reset) */
    NVIC_ClearPendingIRQ(LTDC_IRQn);
}
To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
Saket_Om
// A. Stop the peripheral logic first
__HAL_LTDC_DISABLE(&hltdc);

// B. Silence the peripheral interrupts
LTDC->IER = 0;             // Disable ALL LTDC interrupts
LTDC->ICR = 0x3F;          // Clear ALL LTDC flags
(void)LTDC->ISR;           // Force bus sync
__DSB();

// C. Now it is safe to kill the clock
__HAL_RCC_LTDC_CLK_DISABLE();

		// D. Now the NVIC can be cleared without it "springing back"
		NVIC_ClearPendingIRQ(LTDC_IRQn);
		NVIC_ClearPendingIRQ(LTDC_ER_IRQn);

The code above solved the persistent LTDC interrupt. Next problem is it doesnt wake up anymore from standby. The PWR_WKUP6 does not work. Any thoughts on that? 

@Saket_Om  Your latest post crossed mine. I implemented your solution but still no luck. I do have all the interrupt pending bits (NVIC->ISPR[0 - 15]) cleared now before calling standby, but it still resets. In the post before I thought it stayed in standby, but I was fooled by some test code in the main init code. I measured my current consumption by removing the ferrite on the MCU 3V3 supply line: 

 

WhatsApp Image 2026-02-02 at 09.41.37.jpeg

It looks like it enters standby shortly, but on the small dip the current is still 130 uA, so it is not fully in standby. I have the following code to stop all peripherals using GPDMA:

void PowerThread::PowerManagerCallback(PowerManager::EventType event) {
	if(event == PowerManager::EventType::Event_Sleep) {
		Scheduler::Stop();
		__disable_irq();

		_batteryCharger.EnterLowPowerMode();

		//stop and disable all peripherals using DMA and possibly interfering with standby mode
		GPU2D_StopAndDeinit();
		DMA2D_StopAndDeinit();
		JPEG_StopAndDeinit();
		LTDC_StopAndDeinit();
		USB_StopAndDeinit();
		SPI2_StopAndDeinit();

		_enable5V.SetLow();
		_enable3V3.SetLow();
	}
}

void PowerThread::LTDC_StopAndDeinit(void)
{
    /* 1. Disable LTDC interrupts at peripheral */
    __HAL_LTDC_DISABLE_IT(&hltdc, LTDC_IT_LI | LTDC_IT_FU | LTDC_IT_TE | LTDC_IT_RR);
    // adapt mask to what you actually use

    /* 2. Clear all pending LTDC interrupt flags */
    __HAL_LTDC_CLEAR_FLAG(&hltdc, LTDC_FLAG_LI | LTDC_FLAG_FU | LTDC_FLAG_TE | LTDC_FLAG_RR);

    /* 3. Disable LTDC */
    __HAL_LTDC_DISABLE(&hltdc); // or HAL_LTDC_DeInit(&hltdc);

    /* 4. Disable NVIC interrupt */
	HAL_NVIC_DisableIRQ(LTDC_IRQn);
	HAL_NVIC_DisableIRQ(LTDC_ER_IRQn);

	/* 5. Clear any pending in NVIC (after source is disabled/reset) */
	NVIC_ClearPendingIRQ(LTDC_IRQn);
	NVIC_ClearPendingIRQ(LTDC_ER_IRQn);
}

void PowerThread::GPU2D_StopAndDeinit(void)
{
	HAL_GPU2D_DeInit(&hgpu2d);
    /* 4. Disable NVIC interrupt */
    HAL_NVIC_DisableIRQ(GPU2D_IRQn);
    HAL_NVIC_DisableIRQ(GPU2D_ER_IRQn);

    /* 5. Clear any pending in NVIC (after source is disabled/reset) */
    NVIC_ClearPendingIRQ(GPU2D_IRQn);
    NVIC_ClearPendingIRQ(GPU2D_ER_IRQn);
}

void PowerThread::DMA2D_StopAndDeinit(void) {
	HAL_DMA2D_Abort(&hdma2d);
	__HAL_RCC_DMA2D_CLK_DISABLE();
	HAL_DMA2D_DeInit(&hdma2d);
	HAL_NVIC_DisableIRQ(DMA2D_IRQn);
	NVIC_ClearPendingIRQ(DMA2D_IRQn);
	__HAL_RCC_DMA2D_CLK_DISABLE();
}

void PowerThread::JPEG_StopAndDeinit(void) {
	HAL_JPEG_DeInit(&hjpeg);
	if (hjpeg.hdmain != NULL) {
		HAL_DMA_Abort(hjpeg.hdmain);
	}
	if (hjpeg.hdmaout != NULL) {
		HAL_DMA_Abort(hjpeg.hdmaout);
	}

	__HAL_RCC_JPEG_CLK_DISABLE();
	HAL_NVIC_DisableIRQ(JPEG_IRQn);
	NVIC_ClearPendingIRQ(JPEG_IRQn);
	__HAL_RCC_DMA2D_CLK_DISABLE();
}

void PowerThread::USB_StopAndDeinit(void) {
	HAL_PCD_Stop(&hpcd_USB_OTG_HS);
	HAL_PCD_DeInit(&hpcd_USB_OTG_HS);
	HAL_NVIC_DisableIRQ(OTG_HS_IRQn);
	NVIC_ClearPendingIRQ(OTG_HS_IRQn);
	__HAL_RCC_USB_OTG_HS_CLK_DISABLE();
	__HAL_RCC_USBPHYC_CLK_DISABLE();
}

void PowerThread::SPI2_StopAndDeinit(void) {
	// 1. Wait for SPI to finish the last byte
	while (HAL_SPI_GetState(&hspi2) != HAL_SPI_STATE_READY);

	// 2. Disable SPI
	__HAL_SPI_DISABLE(&hspi2);

	// 3. Stop the associated DMA Channel
	// This is crucial: an enabled DMA channel blocks low-power entry
	if (hspi2.hdmatx != NULL) {
		HAL_DMA_Abort(hspi2.hdmatx);
	}

	// 4. Disable Peripheral Clocks
	__HAL_RCC_SPI2_CLK_DISABLE();
}

After PowerManagerCallback the following function is called:

void LowPower::EnterStandbyMode(void) {
    PrepareForLowPower();

    EXTI->RPR1 = 0xFFFFFFFF;
    EXTI->FPR1 = 0xFFFFFFFF;

    // 4. Clear the NVIC Pending bits
	for (uint8_t i = 0; i < 8; i++) {
		NVIC->ICPR[i] = 0xFFFFFFFF;
	}

    /* Enable WakeUp Pin PWR_WAKEUP_PIN2 connected to PC.13 */
    HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN6_LOW_0);

    /* Clear all related wakeup flags*/
    __HAL_PWR_CLEAR_FLAG(PWR_WAKEUP_FLAG6);

    HAL_PWREx_EnableUltraLowPowerMode();
    /* Enter the Standby mode */
    HAL_PWR_EnterSTANDBYMode();

    // If entry fails (e.g., if a debugger is attached), reset the system
    NVIC_SystemReset();
}


With or without debugger attached, it just resets, so the WFI() is aborted for some reason. Can you point me in the right direction?

Thanks,

Robin

 

I solved the issue, it was quite a search.
To get it to sleep, you need to:

  1. de-init all peripherals which use interrupts or DMA.
  2. Clear and disable all pending interrupts and events. 
  3. Suspend SysTick

Monitor NVIC->ISER and NVIC->ISPR. If one of them is not 0, shutdown / standby will not happen.