Skip to main content
PHolt.1
Senior
March 12, 2022
Question

Is this the right way to "shut down" a 32F417?

  • March 12, 2022
  • 24 replies
  • 4448 views

I am implementing a data save procedure when power fail is detected and want to shut down as much stuff as possible to give myself the most time to copy some data into FLASH.

Shutting down the CPU has many modes which are quite complicated. I have been reading the RM and various appnotes. Googling around shows a lot of people are having problems.

Is this a right way to do it? It never needs to come out of it, and it must not come out of it for any interrupt

CLEAR_BIT(PWR->CSR, 0x00000100U);		// disable WKUP pin, just in case
		#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) )
		portNVIC_SYSTICK_CTRL_REG = 0UL;		// stop systick, just in case (also stops RTOS)
		SET_BIT(PWR->CR, PWR_CR_PDDS);			// select standby mode
		SET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));	// Set SLEEPDEEP bit
		__WFI();								// request "wait for interrupt"

Some of the above is out of HAL_PWR_EnterSTANDBYMode().

I don't get what __WFI is supposed to do.

Thank you for any help or suggestions.

This topic has been closed for replies.

24 replies

PHolt.1
PHolt.1Author
Senior
March 15, 2022

You are right; shutting the CPU reduces the power draw only about 2x, but is still worth doing. I posted some scope traces at the link above.

PHolt.1
PHolt.1Author
Senior
March 16, 2022

I can see that is possible but in this case it was not a planned feature.

This is what it looks like currently:

0693W00000Kcv33QAB.png

Pavel A.
March 17, 2022

But the CPU cannot sleep. It should do work. Write to the flash.

PHolt.1
PHolt.1Author
Senior
March 17, 2022

Sorry, I don't understand. This is a loss of power situation. With the Adesto AT45DB321 you prepare data in one of the two 512-byte RAM buffers and then at some later point, could be days later, issue a write to flash command, and then the device will do the programming fully internally, in about 3ms. So the CPU can shut down right after the command was issued (allowing for SPI to FLASH shifting time, and allowing for the LAN8742 serial config interface shifting time). The SPI part has to be blocking anyway so you can raise CS at the right point.

Pavel A.
March 21, 2022

My wrong, I thought that the flash is internal.

Piranha
Principal III
October 19, 2022
PHolt.1
PHolt.1Author
Senior
October 19, 2022

I am not sure that example is for the 32F417 registers. This is now 6 months after my question, but in case anybody comes across this, this is the entire shutdown code I am using and which works:

		// Stop systick, just in case (also stops RTOS)
 
		#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) )
		portNVIC_SYSTICK_CTRL_REG = 0UL;
 
		// Turn off all GPIO-driven LEDs and P3 P4 RS485 drivers
 
		GPIOD->BSRR = 0x0000fc80;
		GPIOC->BSRR = (uint32_t) 1 << 8;
 
		// Shut down LAN8742 ETH PHY chip. This saves ~100mA.
		// See "SMI Write Operation" in the RM.
		// This uses a special USART to shift 64 bits at 1.6MHz to the LAN8742 so needs to
		// be started well before CPU shutdown
 
		uint32_t tmpreg1 = 0U;					// point at phy=0 reg=0
		tmpreg1 = ETH->MACMIIAR;				// get existing MACMIIAR value
		tmpreg1 &= ~ETH_MACMIIAR_CR_MASK;		// mask off 3 clock divider bits
		tmpreg1 |= 3;							// WR=1 BUSY=1 (BUSY=1 prob not necessary)
		ETH->MACMIIDR = 1 << 11;				// data reg = SHUTDOWN bit set
		ETH->MACMIIAR = tmpreg1;				// write it back; WR=1 starts the 64 bit transfer
 
		// Shut down other stuff - display_cls takes about 100us
		// ** and this delay matters - see comments above **
 
		// Disable USB interrupts, just in case
		#define USB_CF *(volatile uint32_t*) (USB_OTG_FS_PERIPH_BASE+8)
		USB_CF &= ~1;
 
		// Blank LEDs on display board
		display_cls();
 
		// Stop any ARINC429 activity
		KDE_ARINC_reset();
 
		// We are now ~100us after issuing the ETH PHY shutdown. So the LAN8742 Serial Management
		// Interface (SMI) 64-bit USART has finished (takes ~40us)
		// Enter CPU standby mode. Some is out of HAL_PWR_EnterSTANDBYMode(). There is some
		// weird stuff around the shutdown code e.g.
		// https://www.eevblog.com/forum/microcontrollers/how-fast-does-st-32f417-enter-standby-mode/msg4062652/#msg4062652
 
		CLEAR_BIT(PWR->CSR, 0x00000100U);		// disable WKUP pin, just in case
		SET_BIT(PWR->CR, PWR_CR_PDDS);		// select standby mode
		SET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));	// Set SLEEPDEEP bit
		__WFI();								// This actually does the "shutdown"
 
		// CPU clock stops, DAC outputs float, etc
		// ** Code after this point is not executed (checked by waggling GPIO) **
		// But sometimes __WFI can fail - if e.g. there is an interrupt pending
		// e.g. USB serial FLASH read interrupt can be blocked for 200-300us.
 
		// Note that if the supply didn't actually die but we ended up here, the watchdog
		// will eventually trip (see KDE_watchdog_init()).
 
		hang_around_us(500); 					// wait for a bit longer than a FLASH read
 
		for (uint32_t i=0; i<100000; i++)
		{
			__WFI();
		}
 
		// Should never get here, but if we did, we want to start with a freshly
		// initialised product.
 
		reboot();

Piranha
Principal III
November 2, 2022

> I am not sure that example is for the 32F417 registers.

Then port it to your MCU. The issues and general principles are still the same. The code you presented starting from the line 38 manages to ignore and be broken regarding all 4 of the issues I do explain and solve in my article to which I gave the link. The code you needed to port is literally just 3 simple lines:

PWR->CSR &= ~PWR_CSR_EWUP;
PWR->CR |= PWR_CR_CWUF | PWR_CR_PDDS;
(void)PWR->CR;

P.S. @PHolt.1​ , also check the private messages - I sent you a solution to the DMA problem you reported on the EEVblog forum.

PHolt.1
PHolt.1Author
Senior
November 5, 2022

That DMA solution is not applicable to the problem I had.

PHolt.1
PHolt.1Author
Senior
November 5, 2022

This is my current "shut down everything" code

If it is still wrong, perhaps somebody can tell me.

// We are now ~100us after issuing the ETH PHY shutdown. So the LAN8742 Serial Management
		// Interface (SMI) 64-bit USART has finished (takes ~40us)
		// Enter CPU standby mode. Some is out of HAL_PWR_EnterSTANDBYMode(). There is some
		// weird stuff around the shutdown code e.g.
		// https://www.eevblog.com/forum/microcontrollers/how-fast-does-st-32f417-enter-standby-mode/msg4062652/#msg4062652
		// Also https://community.st.com/s/question/0D73W000001nnJOSAY/detail
		
		PWR->CSR &= ~PWR_CSR_EWUP;				// disable WKUP pin, just in case
		PWR->CR |= PWR_CR_CWUF | PWR_CR_PDDS;	// select standby mode (deep sleep)
		(void)PWR->CR;							// readback to ensure the bit is set before next
		__WFI();								// This actually does the "shutdown"
 
		// CPU clock stops, DAC outputs float, etc
		// ** Code after this point is not executed (checked by waggling GPIO) **
		// But sometimes __WFI can fail - if e.g. there is an interrupt pending
		// e.g. USB serial FLASH read interrupt can be blocked for 200-300us.
 
		// Note that if the supply didn't actually die but we ended up here, the watchdog
		// will eventually trip
 
		hang_around_us(500); 		// wait for a bit longer than a FLASH read
 
		for (uint32_t i=0; i<100000; i++)
		{
			__WFI();
		}
 
		// Should never get here, but if we did, we want to start with a freshly
		// initialised unit.
 
		reboot();

Piranha
Principal III
November 5, 2022

You've forgot setting the SCB_SCR_SLEEPDEEP bit, resetting the DBGMCU_CR register and using the DSB instruction macro. Instead the code has a useless (the first one) WFI instruction, a useless magic delay, a finite (and therefore broken) loop with a magic number of iterations and a useless reset.

I cannot understand why you don't just take the much simpler and correct code from my article. Starting from the line 14 it doesn't even have to be modified for your MCU.

PHolt.1
PHolt.1Author
Senior
November 5, 2022

Dear Piranha

Why don't you just tell me what code to use for the 32F417?

You refer to various other posts but always leave out the key piece of info somebody needs.

Kind regards,

Peter

Is this right?

		PWR->CSR &= ~PWR_CSR_EWUP;				// disable WKUP pin, just in case
		PWR->CR |= PWR_CR_CWUF | PWR_CR_PDDS;	// select standby mode
		(void)PWR->CR;							// readback to ensure the bit is set before next
		SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;		// deep sleep
		
		// CPU clock stops, DAC outputs float, etc
		// ** Code after this point is not executed (checked by waggling GPIO) **
		// But sometimes __WFI can fail - if e.g. there is an interrupt pending
		// e.g. USB serial FLASH read interrupt can be blocked for 200-300us. Hence the loop.
		
		for (;;) 
		{
			__DSB();
			__WFI();
		}
 
		// Should never get here, but if we did, we want to start with a freshly
		// initialised unit.
 
		reboot();