How fast does ST 32F417 enter Standby mode? - Page 1
Forum section: electronics / general

author: peter-h
author_rank: Super Contributor
timestamp: March 11, 2022, 10:37:03 pm
content: I am doing a "power down data save" feature, where ADC1 is used to measure a 5V rail (which feeds the 3.3V reg which drives the CPU etc) and if below say 4.5V it saves a 512 byte buffer into a dedicated Adesto AD45DB321 FLASH page. This page has already been pre-erased so I can use the "write without pre erase" FLASH function, which takes 3ms instead of the normal 15ms. I actually measure this programming time at 2.5ms.The problem is that the rate at which the power disappears is a little too great. It allworkssuperbly but it is marginal. The 3.3V falls to about 2.3V just before the 3ms is up. Well, 2.3V is the min VCC for this deviceSo I am looking for ways to reduce the system current, right after the FLASH write command has been issued to it.The most obvious is to shut down the CPU. Looking around various articles and appnotes, this is actually really complicated, with so many options. Eventually I foundHAL_PWR_EnterSTANDBYMode();which doesCode:void HAL_PWR_EnterSTANDBYMode(void){/* Select Standby mode */SET_BIT(PWR->CR, PWR_CR_PDDS);/* Set SLEEPDEEP bit of Cortex System Control Register */SET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));/* Request Wait For Interrupt */__WFI();}but for some weird reason it makes little or no difference. Maybe it is taking too long?Is there something else which needs to be done beforehand? I saw some low power modes are exited by an interrupt, which could be the problem. Is there no simple "shut down" bit? I again see various options in the RM but nothing obviously simple. The RTC and the RTC RAM need to be preserved.

author: langwadt
author_rank: Super Contributor
timestamp: March 11, 2022, 11:00:50 pm
content: change the clock frequency?https://www.st.com/resource/en/datasheet/dm00035129.pdfpage 88 -

author: thm_w
author_rank: Super Contributor
timestamp: March 11, 2022, 11:24:37 pm
content: Simple test application:- Start up as normal- Toggle a pin then enter standby modeMeasure the current with a shunt and the pin on an oscilloscope.https://www.st.com/resource/en/application_note/an4365-using-stm32f4-mcu-power-modes-with-best-dynamic-efficiency-stmicroelectronics.pdfMost likely something is waking up the micro.

author: peter-h
author_rank: Super Contributor
timestamp: March 12, 2022, 10:07:41 am
content: I now have the following code which - looking at the slope at which the 5V rail falls - is definitely workingCode: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 caseHAL_PWR_EnterSTANDBYMode();Interestingly it doesn't do a whole lot, but that's because I have other stuff runningThe systick disable should stop the RTOS though.

author: peter-h
author_rank: Super Contributor
timestamp: March 13, 2022, 09:55:09 am
content: The funny thing about coding is that one can spend a day googling and finding mostly- BS which was obviously never tested because it doesn't work- people asking for help and getting none- people asking for help and eventually solving it butnotposting the solutions (developed in company time, etc)- videos which are mostly drossI posted about this also herehttps://www.eevblog.com/forum/microcontrollers/32f417-any-way-to-do-a-hardware-only-adc-measurement-and-threshold-detection/msg4059118/#msg4059118and herehttps://community.st.com/s/question/0D53W00001QfVvzSAF/is-this-the-right-way-to-shut-down-a-32f417and eventually, a little crumble at a time, one gets it working...I've got it working (source code posted) but I still can't find a complete list of what things can terminate a standby mode ending with __WFI. I suspect some class of interrupts can. The WKUP pin can but you can disable that.

author: hans
author_rank: Super Contributor
timestamp: March 13, 2022, 12:05:28 pm
content: The CPU should stop executing instructions as soon as it hits WFI. How long it takes for the voltage regulator to ramp off I don't know, the datasheet only lists the wake-up times. I assume it's probably fairly "instant".The resume options for standby on the STM32F4 lists, ch2.2.19 of datasheet, as (or see reference manual 5.3.6):QuoteThe standby stops all HF OSC and regulators, so no peripheral except for the RTC and IWDG are running. Disabling systick, or any other peripheral that's generating IRQs shouldn't make a difference, because it has no clock to work from.This is different than in Idle, which only halts CPU executing instructions.Most STM32s has different levels of stop modes (this chip only has 1 stop mode), which has intermediate variations on the level of peripherals that are stopped and/or are still accessible. For example, on some of the low power STM32L chips the deepest stop mode can be used to wake up the device on I2C address match, while in standby it is not capable of doing this.Unfortunately since the F series is not really targetted at complicated low power operation, the datasheet does not specify a neat table which has an overview of all the power modes and peripherals that can resume it's operation.

author: peter-h
author_rank: Super Contributor
timestamp: March 13, 2022, 05:44:56 pm
content: I am confused now. I asked a similar Q on the ST forum and the answer wasAll interrupts cause wakeup, so you have to disable all those you don't want to cause wakeup. It's your program, you surely know what interrupts you have enabled. You would want to "deinitialize" (stop, disable, whatever) most of peripherals causing interrupts, before entering sleep, anyway.Alternatively, use WFE. Events can be caused by interrupt sources which go through EXTI, see External interrupt/event line mapping subchapter of EXTI chapter in RM, as enabled in EXTI_EMR register.Surely there must be some way to simply "kill" the CPU? Well, short of having a P channel MOSFET on the power supply

author: hans
author_rank: Super Contributor
timestamp: March 13, 2022, 07:38:08 pm
content: The conditions which wake up the CPU, depend on the power saving mode. This STM32 has 3 modes:- Sleep: CPU stops executing instructions, but rest of the system is alive. Any IRQ will wake up the system.- Stop: HF OSC shuts down, but voltage regulators do not. This means that only a very limited set of peripherals can generate interrupts to wake up, for example the RTC or EXTI/wake-up pins, but also in some cases more complex protocols (such as USB or Ethernet)- Standby: HF OSC shuts down, voltage regulators shut down. All SRAM and register state is lost, except for in RTC domain. Only hard reset, RTC, IWDG, WKUP event can wake up the CPU.(Also see Table 23 of the reference manual)Maybe the person responding confused the sleep and standby modes. (Rant: IMO the power saving naming scheme is not really intuitive at all. For one, intuitively from reading the words I would rank "Stop" as a deeper saving state than "standby". I prefer EFM32s naming of EM0 through EM4 better, although they also started adding several "sub versions" of power saving mode)WFI is Wait-for-interrupt, and WFE is Wait-for-event. Both are similar, but I prefer to use WFI.I presume you're not using one of the wake-up, exti, rtc, iwdg peripherals/pins for any other purpose? If so disable them, clear flags, etc. before entering standby. I see one mention in the errata on standby, that if multiple wake-up sources in standby are enabled, it could cause an instant reset.

author: peter-h
author_rank: Super Contributor
timestamp: March 13, 2022, 08:23:12 pm
content: Thank you.QuoteThe WKUP pin (PA0) is disabled with CLEAR_BIT(PWR->CSR, 0x00000100U); It is initialised to be an input, with a pulldown.The RTC is being used but not with interrupts.The watchdog is on, but is set for a 1.6 second period and the FLASH write takes just 3ms.Noexternalpininterrupt sources.USB and ETH are used and do use interrupts, and while I can disable USB int withCode:#define USB_CF *(volatile uint32_t*) (USB_OTG_FS_PERIPH_BASE+GAHBCFG)USB_CF &= ~1I am not sure how to disable the ETH one (the code is huge and I didn't implement it).There is also Systick (which I disable)Code:#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) )portNVIC_SYSTICK_CTRL_REG = 0UL;and TIM6 (not disabled yet but can do).QuoteIn that case I must be OK. Well, assuming WKUP is PA0.

author: bugnate
author_rank: Regular Contributor
timestamp: March 15, 2022, 04:55:58 am
content: Another gotcha: IIRC, if you hit that __WFI() with a masked interrupt pending of *any* type, the __WFI() effectively becomes a NOP. So you have to be a bit methodical about tidying up particularly if you want to sleep *now*.

author: peter-h
author_rank: Super Contributor
timestamp: March 15, 2022, 06:52:52 am
content: Does that not conflict with the post further aboveQuoteAIUI, the __WFI is never executed.The code sequence I have isCode:SET_BIT(PWR->CR, PWR_CR_PDDS);// select standby modeSET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));// Set SLEEPDEEP bit__WFI();Why is this so complicated? There seems to be no clear description of a procedure to simply shut down the CPU. If you google for it, you get hundreds of hits of people trying to work it out.I have put in some GPIO waggle code in various places:Code:CLEAR_BIT(PWR->CSR, 0x00000100U);// disable WKUP pin, just in caseSET_BIT(PWR->CR, PWR_CR_PDDS);// select standby modeTopLED(true); // this pulse comes outTopLED(false);TopLED(true); // this pulse comes outTopLED(false);SET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));// Set SLEEPDEEP bitTopLED(true); // this pulse comes out, and DAC outputs are seen floating at this pointTopLED(false);TopLED(true); // this pulse doesn't come outTopLED(false);__WFI // this is obviously never reachedClearly it takes many CPU clocks to execute the deep sleep selection. This doesn't matter; it's just interesting. But if the main oscillator stops, there is no point in disabling Systick, is thereAnd you can't possibly get any internal interrupts. Which confirms the quoted text above is probably spot on.However, then I did another test:Code:SET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));// Set SLEEPDEEP bit__WFI();TopLED(true); // this pulse doesn't come outTopLED(false);I suspected that due to the long delay after setting the deep sleep bit, the __WFIdoesget executed, but that last pulse does not come out, so presumably the __WFI blocked execution.More tests show that __WFI is required; the sleep bit does nothing otherwise. And no instructions get executed after __WFI; checked with thisCode:__WFI();TopLED(true);TopLED(false);QuoteIt doesn't appear to be as simple; I have not disabled e.g. a TIM6 1kHz interrupt, but it doesnotterminate the Standby mode. Perhaps you mean if one executes __WFI and at that instant there is an interrupt pending, the __WFI is skipped. But how could one avoid that? If I clear all "IP" bits (lots of them around) there is no guarantee that one of them won't get set when I then execute the __WFI. One would have to disable all interrupts individually, clear the IP bits, and wait a bit. Is that really the procedure??

author: hans
author_rank: Super Contributor
timestamp: March 15, 2022, 09:43:27 am
content: "Masked" interrupt could be where an interrupt still occurs, even if the handler is disabled.For example, on PIC32MX the DMA controller is triggered by peripheral interrupts instead of dedicated DMA events. This means that the peripheral is firing interrupts to the CPU/DMA, but that the interrupt handler is not enabled and so the CPU won't react on it. The data is handled by the DMA controller and should clear the interrupt condition.I suppose that's what bugnate means with pending unhandled IRQs that could block the CPU entering power saving mode. Unfortunately it's impossible to tell how exactly the power saving works. All we observe is Run > Standby in 1 step, but for all we know the CPU executes a statemachine that goes through Run > Sleep > Stop > Standby steps over multiple cycles..Power saving code on MCUs can be quite tricky to get right. Before you know, something can stay left on, or incorrect wake intervals (or it doesn't wake up anymore).Perhaps something to check if it helps:Go to the NVIC and clear all pending IRQ vectors.Go to the RCC bits and turn off all peripherals.By the way, do you also observe current measurements of your system? A 168MHz CPU going to effectively a shutdown state should show some difference in power state of the system. Perhaps that's a way to know if it's working (plotting current vs sleep trigger on o'scope could also be useful).

author: wek
author_rank: Frequent Contributor
timestamp: March 15, 2022, 10:37:27 am
content: Quote from: bugnate on March 15, 2022, 04:55:58 amWhat do you exactly mean by "masked"?JW

author: peter-h
author_rank: Super Contributor
timestamp: March 15, 2022, 10:46:47 am
content: QuoteOne could do that, but- I haven't seen it mentioned anywhere- it just seems bizzare that such a weird thing would really be neededQuoteI don't do Icc measurement directly but I can see it from the slope of the VCC decline, and yes the CPUisgoing to "sleep". What concerns me is the reliability of this whole thing.If this "__WFI becomes a NOP if there an int pending" is real, one could do__WFI__WFIwhich indeedisseen in some code relating to __WFE. That would deal with the case where an int is pending but at the time of __WFI execution has not yet generated the vectoring to the ISR (is that even possible - I mean is it even possible for foreground code to "see" an IP bit set, assuming the interrupt is not prevented from executing?).However, more tricky would be an int pending because a higher priority int is blocking it. That could be a long time. For example in my system I have a USB interrupt which is disabled around some foreground code, for either ~200us (if reading the USB drive) or ~15ms (if writing the USB drive). The latter case I don't need to deal with because the power fail data save will fail anyway (the 3.3V dies well before 15ms). The former case could be dealt with by__WFIwait 300us__WFIAnyway, this is the end result so far, which is satisfactory. I just need to be sure that the CPU won't come out of Standby under some rare circumstancesCode:// Enter CPU standby mode. Some is out of HAL_PWR_EnterSTANDBYMode().CLEAR_BIT(PWR->CSR, 0x00000100U);// disable WKUP pin, just in caseSET_BIT(PWR->CR, PWR_CR_PDDS);// select standby modeSET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));// Set SLEEPDEEP bit__WFI();// CPU clock stops, DAC outputs float, etc// ** Code after this point is not executed (checked by waggling GPIO) **hang_around_us(500);__WFI();// 2nd one to handle some weird cases// Should never get here, but if we did, we want to start with a freshly// initialised unitreboot();

author: newbrain
author_rank: Super Contributor
timestamp: March 15, 2022, 01:46:25 pm
content: QuoteBut is it?How's this 3 ms time guaranteed?I don't know the details of you system, but what if electrolytic caps degrade with time and or temperature? Or the input to the PSU is at a lower than expected voltage so they hold less available charge? And this are just the first two things that came to mind, there might of course be many others.A 16% margin seems very tight, especially considering the datasheet gives 3-4 ms for a page write operation.

author: peter-h
author_rank: Super Contributor
timestamp: March 15, 2022, 02:43:50 pm
content: Check the 3.3V rail on the scope pic. It remains above 2.3V for plenty long enough.All relevant caps are ceramics.My main concern is whether the Standby is being done the right way.

author: Siwastaja
author_rank: Super Contributor
timestamp: March 15, 2022, 02:50:20 pm
content: Quote from: peter-h on March 15, 2022, 02:43:50 pmWhich lose significant amount of capacitance due to aging, easily 20-30%.Add to that unit-to-unit variation which can be also around 20%.This is far from satisfactory. I would consider doubling the amount of capacitance.

author: peter-h
author_rank: Super Contributor
timestamp: March 15, 2022, 05:13:55 pm
content: I can easily add capacitorsBut what you can't see on the above trace is how long the VCC remains above 2.3V. It is a long time... because everything else has died. Also the above pic is from pulling out the USB cable, which kills the 5V rail almost instantly and the 3.3V rail starts to drop within ~100us of that. In normal circumstances the "power failure" will be a loss of 24V DC input to a switch mode PSU which generates its outputs down to about 10V DC in. The USB power option is really just for mucking about on the bench, config after the box was opened, etc. Parts of the product are non-functional with just USB power, for complicated reasons. And if I want to do a better power fail detect off the DC IN (which is isolated, btw) I can do that with a $0.10 optoisolator and then I will have ~100ms warning.Another thing I can do, to gain 1ms, is not poll the ADC on the 1kHz tick but use the window ADC feature to interrupt (see other thread here). That is a "slicker" method but not without some trickery to prevent a barrage of interrupts.I am still looking for input on the Standby code, on which no two people agree - here or elsewhere

author: bugnate
author_rank: Regular Contributor
timestamp: March 15, 2022, 06:39:39 pm
content: Quote from: peter-h on March 15, 2022, 06:52:52 amI meant the latter. A non-wakeup event (where a wakeup event is from the RM table 23) cannot terminate a standby mode, but it can prevent one (the NOP thing) if it comes at a bad time and other conditions are ripe. "Bad time" could be if the interrupt is masked or it happens in some vulnerable moment in the process of going to standby, which you have discovered takes non-zero time. (I don't have any insight on that particular bit and I don't use standby, I'm more of a Stop mode guy).I don't think the procedure is quite as gruesome as you list, but yeah it's something like that. That is async for you. Of course the sensible thing to do is to live with it and write some code to check that the expected sleep occurred (you have the RTC after all) and try again if something spurious happened. But if you need a guarantee that you will go to standby on that exact cycle, you are in for a bit of a bad time (and strictly speaking, 100.0% may not be possible). Honestly in that case I'd think about working a NVIC_SystemReset() in there before sleeping to help with cleaning the slate.By "masked" I lazily meant PRIMASK / __disable_irq() but with an interrupt pending bit set in the NVIC. I did not mean merely having the source enabled.

author: peter-h
author_rank: Super Contributor
timestamp: March 15, 2022, 08:14:30 pm
content: QuoteThat's a really interesting approach.Set a "Standby required" flag in RAM and reboot the CPU, with thisCode:static inline void reboot(void){// Ensure all outstanding memory accesses including buffered write are completed before reset__ASM volatile ("dsb 0xF":::"memory");// Keep priority group unchangedSCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |(SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |SCB_AIRCR_SYSRESETREQ_Msk );__ASM volatile ("dsb 0xF":::"memory");// wait until resetfor(;;){__NOP();}}and then you know there won't be any interrupts pending.Where in the RM does it say that a __WFI is skipped if there is an int pending? This is a 32F417.EDIT: found itpage 127 RM.This is a bugger! Looking for simple ways around this, one could put __WFI in a loop so if it gets skipped a few times, eventually a __WFI will get executed properly.Just realised that NVIC_SystemReset()ismy Reboot code above.

author: bugnate
author_rank: Regular Contributor
timestamp: March 15, 2022, 09:00:00 pm
content: Quote from: peter-h on March 15, 2022, 08:14:30 pmI saw the edit where you already found it, but FYI the reference for this sort of thing is the processor / architecture manual and not the SoC RM, although I guess it did have it after all, and in clear language.Also see attachment from page 562 of Arm®v7-M Architecture Reference Manual. Note than an interrupt is a type of exception.Also see this guy saying basically the same thing:https://stackoverflow.com/questions/46934649/arm-wfi-wont-sleep

author: peter-h
author_rank: Super Contributor
timestamp: March 15, 2022, 09:54:43 pm
content: Amazing, because in your page above it stateswhich is the conclusion I came to also. If the 1st __WFI gets skipped, eventually you will hit one which works.Completely bizzare.Fortunately I have an easy job here, because I need the CPU to shut down for only a few ms, after which I don't care.So now I have thisCode:CLEAR_BIT(PWR->CSR, 0x00000100U);// disable WKUP pin, just in caseSET_BIT(PWR->CR, PWR_CR_PDDS);// select standby modeSET_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.hang_around_us(500);// wait for a bit longer than a FLASH readfor (uint32_t i=0; i<100000; i++){__WFI();}// Should never get here, but if we did, we want to start with a freshly// initialised unitreboot();

author: nctnico
author_rank: Super Contributor
timestamp: March 15, 2022, 10:07:40 pm
content: Quote from: peter-h on March 15, 2022, 09:54:43 pmI think that is a mis-interpretation. I read it as 'the WFI instruction may not be implemented in all hardware'. Remember that ARM delivers a CPU core with options that can be enabled / disabled. If you write universal software that is supposed to run on different ARM implementations, you may encounter systems that do not implement WFI. The scope of the ARM documentation is very wide; you should overlay the documentation of the microcontroller manufacturer to figure out what is actually implemented.For a microcontroller which has a working WFI instruction the way to use WFI is to disable / clear any interrupt sources, execute a few NOPs (3 or so) to make sure all pending interrupts (from before disabling the interrupts) are handled and then execute the WFI instruction.

author: peter-h
author_rank: Super Contributor
timestamp: March 16, 2022, 08:38:18 am
content: Did more work on this today and gained another 2ms "early warning". There was a too-slow lowpass filter on the ADC used to measure the 5V railSo a lot more margin.That rail already has a 470uF capacitor on it, plus a load of ceramics, and looking at the rate of fall on powerdown, it corresponds to a current draw of about 150mA after CPU shutdown.Disconnecting the debugger (which is isolated so target-powered - STLINK V3 ISOL) also helps. Maybe 20mA or so.I think I need to look at shutting down ETH but I don't know how to. Removing the cable reduces the 150mA to 100mA. I am using the reduced interface, to a LAN8742, but the whole ETH subsystem was implemented by someone else and is a mystery to me. There is stuff like HAL_ETH_MspDeInit() but it seems to be empty. It is just the LAN8742 which needs doing because the CPU will be shut down. Looks like TE and RE bits in MACCR. This led me to thisCode:/*** @brief Disables the MAC transmission.* @param heth pointer to a ETH_HandleTypeDef structure that contains* the configuration information for ETHERNET module* @retval None*/static void ETH_MACTransmissionDisable(ETH_HandleTypeDef *heth){__IO uint32_t tmpreg1 = 0U;/* Disable the MAC transmission */(heth->Instance)->MACCR &= ~ETH_MACCR_TE;/* Wait until the write operation will be taken into account:at least four TX_CLK/RX_CLK clock cycles */tmpreg1 = (heth->Instance)->MACCR;ETH_Delay(ETH_REG_WRITE_DELAY);(heth->Instance)->MACCR = tmpreg1;}which has a mysterious delay of1mswhich is completely useless. Are MACCR values pushed out to the LAN8742 at 2400 baud?I tried the obvious; no effectETH->MACCR = 0x00008000;but the above code snippet shows one has to then read back MACCR and write it back in after that long delay. 1ms is way too long for my purpose.I can see there is some sort of "USART" link along which this stuff gets sent. The 8742 data sheet describes the registers. 2 wires: data and clock. Its "SMI timing" shows a max clock of ~2MHz.Anyway, stepping through the ETH init code, I find that the clock field in ETH->MACMIIAR is set to 100 which with a 168MHz clock is a MDC clock of ~1.6MHz, which is fine. So I reckon the comment in the above ST code above the 1ms delay isBS. And nobody ever discovered it...I guess to do this fast, you need a MOSFET to turn the supply to various things.Looks like I will have to settle for this

author: newbrain
author_rank: Super Contributor
timestamp: March 16, 2022, 02:24:49 pm
content: QuoteIt's not too mysterious if one reads theerrata, chapter 2.11.5.Agreed, using HAL_Delay is a waste of time, they should have provided a tailored busy wait.(Also: marked the HAL code as part of an errata!)

author: peter-h
author_rank: Super Contributor
timestamp: March 16, 2022, 02:48:10 pm
content: Sorry; I don't see the connection between the Standby mode, and the way the PHY registers are interfaced, with the strange readback, writeback, and the 1ms wait in between.After much digging around I found the code which is used to write a PHY config register directly:Code:/*** @brief Writes to a PHY register.* @param heth pointer to a ETH_HandleTypeDef structure that contains* the configuration information for ETHERNET module* @param PHYReg PHY register address, is the index of one of the 32 PHY register.* This parameter can be one of the following values:* PHY_BCR: Transceiver Control Register.* More PHY register could be written depending on the used PHY* @param RegValue the value to write* @retval HAL status*/HAL_StatusTypeDef HAL_ETH_WritePHYRegister(ETH_HandleTypeDef *heth, uint16_t PHYReg, uint32_t RegValue){uint32_t tmpreg1 = 0U;uint32_t tickstart = 0U;/* Check parameters */assert_param(IS_ETH_PHY_ADDRESS(heth->Init.PhyAddress));/* Check the ETH peripheral state */if(heth->State == HAL_ETH_STATE_BUSY_WR){return HAL_BUSY;}/* Set ETH HAL State to BUSY_WR */heth->State = HAL_ETH_STATE_BUSY_WR;/* Get the ETHERNET MACMIIAR value */tmpreg1 = heth->Instance->MACMIIAR;// tmpreg1 = 16; // Just to test !!!!!/* Keep only the CSR Clock Range CR[2:0] bits value */tmpreg1 &= ~ETH_MACMIIAR_CR_MASK;/* Prepare the MII register address value */tmpreg1 |=(((uint32_t)heth->Init.PhyAddress<<11U) & ETH_MACMIIAR_PA); /* Set the PHY device address */tmpreg1 |=(((uint32_t)PHYReg<<6U) & ETH_MACMIIAR_MR); /* Set the PHY register address */tmpreg1 |= ETH_MACMIIAR_MW; /* Set the write mode */tmpreg1 |= ETH_MACMIIAR_MB; /* Set the MII Busy bit *//* Give the value to the MII data register */heth->Instance->MACMIIDR = (uint16_t)RegValue;/* Write the result value into the MII Address register */heth->Instance->MACMIIAR = tmpreg1;/* Get tick */tickstart = HAL_GetTick();/* Check for the Busy flag */while((tmpreg1 & ETH_MACMIIAR_MB) == ETH_MACMIIAR_MB){/* Check for the Timeout */if((HAL_GetTick() - tickstart ) > PHY_WRITE_TO){heth->State= HAL_ETH_STATE_READY;/* Process Unlocked */__HAL_UNLOCK(heth);return HAL_TIMEOUT;}tmpreg1 = heth->Instance->MACMIIAR;}/* Set ETH HAL State to READY */heth->State = HAL_ETH_STATE_READY;/* Return function status */return HAL_OK;}and obviously this can be much simplified for just accessing this LAN8742 reg:

author: newbrain
author_rank: Super Contributor
timestamp: March 16, 2022, 03:04:38 pm
content: QuoteI have no idea of the connection - but you were specifically commenting about the strange code (write, read, rewrite): that sequence is suggested as a workaround for the silicon bug described in the errata.Some bits in some register might not "take" a write if it's too close to another write: the register will look (i.e. read) as if it was correctly written, but the HW will merrily go on with its previous state.MACCR is affected by the bug, MACMIIAR is not AFAICS.So, 1 ms (minimum HAL_Delay* granularity) is too much, but a form of delay is needed there.*I see you have ETH_Delay - the current master on GH directly uses HAL_Delay - given you say it's 1 ms I suppose they are the same.

author: peter-h
author_rank: Super Contributor
timestamp: March 16, 2022, 03:42:13 pm
content: There were two issues here.One is how to enter Standby reliably, without the __WFI being terminated by some pending interrupt. That errata you found (thanks!) seems to confirm what a few people have been saying: one has to clean up all interrupt enables, all pending ints, wait a bit, and then __WFI should work. However, the other ST appnote suggests putting __WFI in a loop, so if it doesn't work 1st time because it just hit a pending int, it should work on a subsequent time. Well, unless in your target you have a pending int situation which lasts for many milliseconds and then you have to deal with that specifically. In my target I have two scenarios where there could be a long int pending state: a FatFS sector read (200-300us), and a FatFS sector write (15ms). The 1st I handle specifically; the 2nd is a waste of time because that write will be trashed anyway... the only way would be to have enough capacitance to last >15msat full powerand check the rail before every sector write, but even then you will still get a trashed file system... just like on a PC.The other is regarding shutting down the ETH PHY chip (LAN8742). This uses a dedicated 2-wire (but bidirectional) 64 bit USART. It took me a few hours of digging through the ST libs but eventually I found how this works. The 32F4 has two registers, ETH->MACMIIAR and ETH->MACMIIDR. The former contains (among other stuff, like the USART clock speed, in this case ~1.6MHz) the PHY register address (0-31, which in this case is 0 because the shutdown bit is in reg 0). The latter is a 16 bit data reg whose value is pushed to the addressed PHY chip reg. And there was some funny code around this part, but looking elsewhere (HAL_ETH_WritePHYRegister) this weird code is not needed if you go in low enough. You can just poke the value in there (order: data reg 1st, address reg 2nd, and loading the AR presumably kicks off the 64-clock shift) and go away. You can also read the PHY this way, but I don't need to do that. I still think that 1ms wait is some fossil BS. BTW the 64 bits is 64 because of a preamble of 32 ones; that is the "start bit". See "SMI write operation" in the RM.My greatly cut-down version of HAL_ETH_WritePHYRegister() which just does the shutdown bit is:Code:// Shut down LAN8742 ETH PHY chipuint32_t tmpreg1 = 0U;// point at phy=0 reg=0tmpreg1 = ETH->MACMIIAR;// get existing MACMIIAR valuetmpreg1 &= ~ETH_MACMIIAR_CR_MASK;// mask off 3 clock divider bits (they are 100 for 168MHz/102)tmpreg1 |= 3;// WR=1 BUSY=1 (BUSY=1 prob not needed)ETH->MACMIIDR = 1 << 11;// data reg = SHUTDOWN bit setETH->MACMIIAR = tmpreg1;// write it backThen you have to wait > 40us (at CK=100 i.e. 168MHz/102) for the stuff to shift out to the PHY, before issuing the CPU Standby.Now, if one was doing this properly as a "low power mode" and hoping to wake up from this and run again, forget itWhat is interesting is that the ST code, while supporting funcs for all sorts of PHY features, does not support the shut down mode at all. The PHY_POWERDOWN reg (see below) is not referenced in the whole ETH libCode:/* Section 3: Common PHY Registers */#define PHY_BCR ((uint16_t)0x00) /*!< Transceiver Basic Control Register */#define PHY_BSR ((uint16_t)0x01) /*!< Transceiver Basic Status Register */#define PHY_RESET ((uint16_t)0x8000) /*!< PHY Reset */#define PHY_LOOPBACK ((uint16_t)0x4000) /*!< Select loop-back mode */#define PHY_FULLDUPLEX_100M ((uint16_t)0x2100) /*!< Set the full-duplex mode at 100 Mb/s */#define PHY_HALFDUPLEX_100M ((uint16_t)0x2000) /*!< Set the half-duplex mode at 100 Mb/s */#define PHY_FULLDUPLEX_10M ((uint16_t)0x0100) /*!< Set the full-duplex mode at 10 Mb/s */#define PHY_HALFDUPLEX_10M ((uint16_t)0x0000) /*!< Set the half-duplex mode at 10 Mb/s */#define PHY_AUTONEGOTIATION ((uint16_t)0x1000) /*!< Enable auto-negotiation function */#define PHY_RESTART_AUTONEGOTIATION ((uint16_t)0x0200) /*!< Restart auto-negotiation function */#define PHY_POWERDOWN ((uint16_t)0x0800) /*!< Select the power down mode */#define PHY_ISOLATE ((uint16_t)0x0400) /*!< Isolate PHY from MII */#define PHY_AUTONEGO_COMPLETE ((uint16_t)0x0020) /*!< Auto-Negotiation process completed */#define PHY_LINKED_STATUS ((uint16_t)0x0004) /*!< Valid link established */#define PHY_JABBER_DETECTION ((uint16_t)0x0002) /*!< Jabber condition detected */It is totally pointless to have support for the various convoluted 32F4 low power modes (which according to google only 23.4% of coders ever manage to get to work properly) while your ETH is drawing 100mA

author: peter-h
author_rank: Super Contributor
timestamp: March 16, 2022, 06:46:49 pm
content: Anyway, it works!Current drops from about 155mA to 67mA.I now have > 8ms of adequate VCC.