32F417: Any way to do a hardware-only ADC measurement and threshold detection? - Page 1
Forum section: electronics / general

author: peter-h
author_rank: Super Contributor
timestamp: March 05, 2022, 07:12:51 pm
content: I have a 5V (nominal) supply feeding a 3.3V LDO regulator which feeds the CPU.I need to detect the 5V rail falling down to say 4V and then interrupt from that, within say 100us. But I don't want a 10kHz interrupt doing an ADC measurement.I know one can set up a timer to trigger an ADC reading, at say 10-100kHz, and one could trigger a DMA channel to read the ADC and copy it to RAM, at a fixed location if desired.The obvious way - an ISR from the DMA complete event - would be quite short - just a few instructions. But is there any cunning way to generate an interrupt only on a specific ADC value?

author: Cerebus
author_rank: Super Contributor
timestamp: March 05, 2022, 07:25:30 pm
content: Quote from: STM32F417xx data sheet

author: peter-h
author_rank: Super Contributor
timestamp: March 05, 2022, 08:04:30 pm
content: Well spotted!

author: SiliconWizard
author_rank: Super Contributor
timestamp: March 05, 2022, 08:07:25 pm
content: Some other MCUs have internal comparators, but apparently the STM32F417 does not (that I could find.) But that's what I would have used instead of using ADC measurements.If the voltage threshold doesn't need to be programmable (fixed, approx. 4V), provided that you can still modify the hardware, you could just add an external voltage supervisor and tie its output to any GPIO configured for interrupting on a given state.Voltage supervisors can be found with a variety of fixed thresholds while drawing minimal power (and being cheap). This is just one example, but there are tons of others:https://www.onsemi.com/products/power-management/voltage-references-supervisors/voltage-supervisors/cat809

author: peter-h
author_rank: Super Contributor
timestamp: March 05, 2022, 09:15:03 pm
content: The purpose of this exercise is described in this posthttps://www.eevblog.com/forum/microcontrollers/can-a-write-to-the-rtc-battery-backed-register-domain-be-corrupted-(stm32g4)/msg4041865/#msg4041865I've done measurements of how fast the 5V and 3.3V rails decay. I didn't think it would be any good because the SW power supply has no real capacitance in it. But the time from the 5V rail (poorly regulated) falls below 4.25V, to the time the 3.3V rail starts to fall, is 3-5ms. In reality one has a lot more time because the Adesto FLASH is good (to finish the 3ms programming cycle) down to 2.6V, and that takes a lot longer; more like 10ms.So the plan is to simply run the ADC continuously and poll the value from an existing 1kHz timer ISR. It means I will lose 1ms but that's ok.To tweak things more, one can, from the ISR detecting the power fail, immediately turn off other things such as LEDs and such.It's a very neat scheme for saving 512 bytes of critical data at power-down. This was always a struggle with battery backed CMOS RAM, which is dead easy to corrupt, so one needed solid VCC detection and a write inhibit, which is not much fun given the /WR is, ahem, active-lowYou just need to keep a FLASH page aside, already erased, because the full programming cycle (with pre-erase) is ~15ms.I bet you a lot of the supervisor chips are unobtainable right now. I spent a day or two replacing a Maxim MAX706 (4.5V threshold) with an ST "706" which comes only in lower values like 3.0V. I found some 25 year old faxes with Hitachi (H8/300) telling me the 5V CPU is actually same silicon as the 3.3V CPU, so doing a reset at 3V is OK

author: DavidAlfa
author_rank: Super Contributor
timestamp: March 05, 2022, 10:42:37 pm
content: You can use ADC "continuous dma requests" to run a DMA in circular mode.That will keep running in the background.Just take care of the ADC clock, so it doesn't run a lot faster than needed, consuming memory bandwidth and hurting the system performance.

author: SiliconWizard
author_rank: Super Contributor
timestamp: March 05, 2022, 11:05:12 pm
content: Quote from: peter-h on March 05, 2022, 09:15:03 pmFor sure. As pretty much anything else.But the CAT809J (4V version) happens to be available on Mouser (CAT809JTBI-GT3) with over 4k pieces. =)

author: peter-h
author_rank: Super Contributor
timestamp: March 06, 2022, 07:14:38 am
content: QuoteLooking at the RM, one could use a continuous conversion mode and just read ADC_DR out of a timer ISR (IOW, not use the ADC interrupt, for simplicity), ignoring the OVF flag etc. But this runs the risk of reading ADC_DR when the ADC is busy. It's not clear if there is a valid value in DR. The RM doesn't say there isn't.Whereas if one links the ADC to DMA and gets the DMA to write the value into a (fixed) RAM location, that location will always have a valid value in it.

author: Siwastaja
author_rank: Super Contributor
timestamp: March 06, 2022, 08:06:27 am
content: The analog watchdog functionality. You can keep ADC converting continuously its sequence, and get an interrupt when a specified channel goes outside specified range, no need to read and compare anything in code.Page 392 on RM0090.

author: peter-h
author_rank: Super Contributor
timestamp: March 06, 2022, 08:39:20 am
content: Yes; I was merely exploring other ways to do it.One has to be careful with interruptsIf this power fail detector gets mis-set somehow, or the supply rail being measured becomes marginal or noisy, you will get a solid stream of interrupts. Yes, I know one can disable that interrupt once it has been serviced and re-enable it from say a timer tick, in order to prevent this, but it is more complexity, whereas polling the ADC from a timer tick is "safe". I wonder how many people have discovered this themselves.It is the same argument with say servicing ETH packets. If you service it from a timer tick then you can't be overloaded by a solid incoming stream.

author: Kleinstein
author_rank: Super Contributor
timestamp: March 06, 2022, 09:10:04 am
content: I don't think you would get a stream of interrupts: there should be just 1 interrupt for the first time the supply gets too low. With the STM32 one usually has to clear the interrupt flag in the ISR anyway. So this is nothing unusual with the analog watchdog. The speed the ADC runs set the speed of the checks.If needed one may still do another ADC reading in the ISR to check if the 1st drop was just a glitch.Doing it by hand in a timer interrupt only makes things more complicated and adds to the delays. One would have to care about recuring power drops of glitches in the timer ISR as well. So I see no advantage in not using the extra ADC feature to do the comparison. It may complicate the use of the ADC for other purposes a little.

author: uer166
author_rank: Super Contributor
timestamp: March 06, 2022, 09:30:18 am
content: Quote from: peter-h on March 06, 2022, 08:39:20 amI'm a fan of fully time-triggered architectures where no interrupts are allowed (except the scheduler update interrupt). The whole thing is basically equivalent to a time triggered task that is called from a timer tick. I wholly disagree that polling the ADC values from such task is somehow "more complex" than an ISR called from a analog watchdog, or PVD, or comparator interrupt, because you always maintain control flow. It is trivial to simulate/prove the scheduling correctness, and the worst case response time, which is helpful in some special cases.

author: Siwastaja
author_rank: Super Contributor
timestamp: March 06, 2022, 11:17:29 am
content: Quote from: uer166 on March 06, 2022, 09:30:18 amI see myself using more and more a pattern where a timer interrupt drives a state machine, and peripherals with deterministic timing (SPI master, ADC, UART, ...) are controlled by that state machine. Of course the relevant status flags are checked by the state machine; but considered as fatal errors, leading to error reporting - possibly to error recovery, but now that is possibly very complex.But because the timing is usually completely trivial to prove, such error checks would only indicate things like "MCU silicon has failed", or "programmer's interpretation of documentation does not match the actual behavior". Former requires physical replacement; latter requires bugfix. Both are catastrophic conditions.So I'm turning around the classic "wait for peripheral event while keeping time for timeout and error handling", to "wait for timer for expected completion of peripheral, check if event was registered as it should have been". The result is basically the same, but I think what I'm doing is easier to analyze for timing.All this can be done with or without an RTOS. And it doesn't even need to be a tick with constant period. You can set the timer to trigger the next state at any time.

author: Kleinstein
author_rank: Super Contributor
timestamp: March 06, 2022, 12:52:14 pm
content: The undervoltage interrrupt would not really interfere with normal operation. As long as the supply is good the interrupts would not fire at all. If an undervoltage is detected, the safe shutdown usually has higherst priority and there is no more normal operation. The pritorites usually change for the last for ms before the power runs out. Starting an Flash/EEPROM write usually does not take very long. So the "normal" operation may continue with relatively little delay.Quote from: uer166 on March 06, 2022, 09:30:18 amThe single do all state machine is a quite limiting setup. It may work in some cases, but it adds quite some overhead and delay when asynchronous tasks need to be handled. The whole setup can be quite a bit more complicated to start with. Not all tasks are easy to chop down in suitable size chunks to do in quasi parallel in a state maching: very small chunks cause more overhead and large chunks would increase the worst case delay and limit the speed / block other tasks too long. One may be able to calculate the worst case response time - to get everything elese done before. The direct use of interrups essentially allways gets a better response time and an upper limit for the response time is also easy to calculate. It may be just tricky to prove how much faster it is.

author: Marco
author_rank: Super Contributor
timestamp: March 06, 2022, 03:22:37 pm
content: Datasheet says it has a PVD ... why not use that?

author: Siwastaja
author_rank: Super Contributor
timestamp: March 06, 2022, 04:03:02 pm
content: AWD can detect falling input voltage before the regulators way earlier than the 3V3 (or whatever) of the MCU is falling. This leaves more time for the power-down handler code.

author: Marco
author_rank: Super Contributor
timestamp: March 06, 2022, 04:35:58 pm
content: I didn't realise how limited the PVD was, just VDD makes it a bit useless.

author: peter-h
author_rank: Super Contributor
timestamp: March 06, 2022, 05:14:54 pm
content: QuoteDone properly, it's called an RTOSand it works very well.Does anyone know if ADC_DR is always readable? If not, I could set it up for asingleconversion, and get a 1kHz timer tick to read DR and then start a new conversion. I have used DMA for a number of things but reading the ADC into a single RAM location, just to get an always-valid value, is a waste of a DMA. Especially as the 32F4 DMA channels are mostly hard-coded to specific peripherals.

author: Cerebus
author_rank: Super Contributor
timestamp: March 06, 2022, 06:11:44 pm
content: Unless you're starved for DMA resources making use of the hardware practically designed to give you an interrupt on power failure would seem to me to be by far the easiest, safest, most reliable way to implement it. Set it up and running and then you can forget about it until you get the interrupt. The run time resources it will take are virtually nothing, you don't have to account for it in any code except stuff that will actively interact with the interrupt. All around it seems less trouble and less fuss.

author: uer166
author_rank: Super Contributor
timestamp: March 06, 2022, 06:25:07 pm
content: Quote from: Kleinstein on March 06, 2022, 12:52:14 pmI don't completely disagree, but I think it basically moves all the work "up front" so to speak. Instead of fixing bugs for a year because you made a huge mess of a dozen interrupts of various priorities, as well as foreground, and FreeRTOS on top of that, you simply chop up your application into co-operative chunks from the start in a way that avoids any and all timing-related bugs by design.This means splitting tasks into smaller pieces that normally would be too long to execute within a single timer tick, it also means making all "asynchronous" tasks synchronous in various ways. It requires a lot of hardware automation to sync it with the real world, such as DMA (without interrupts), timers, all kinds of peripheral bolt-ons that help with this, and occasionally some external logic gates.I wouldn't want to write a Ethernet stack with such an architecture, but if I'm building a jet engine FADEC, or in my case code that handles GFCI and other human safety functions (or in a previous life skydiving altimeters), it helps immensely. It's not even to convince UL or other agencies that it works to 5-sigma or whatever, but more to convince myself. It also allows you to do really cool stuff like cross-check the task sequence execution against the expected sequence that can be hard-coded, which is able to detect bugs that you couldn't even anticipate.

author: peter-h
author_rank: Super Contributor
timestamp: March 07, 2022, 08:46:10 am
content: As an aside, is there any way that the ST ADC could hang and never do a "conversion complete"?I am cleaning-up the auto generated HAL code (which does run but the sheer size of it is unsuitable for any real use) and for example there is thisQuotewhich implements a timeout, but a) the timeout will not work until the 1kHz interrupt is running (so cannot be used in early boot code) and b) surely the only way the timeout can happen is if the silicon is defective?Lots of things wrong with the above code e.g. it is checking for ADC misconfig butyoudamn well know how it is configured

author: peter-h
author_rank: Super Contributor
timestamp: March 07, 2022, 02:02:16 pm
content: I've edited the above since anybody posting more than 20 lines of code gets no responseBasically they start an ADC conversion (which in the context of this ADC runs for only some microseconds) and then while waiting for EOC they run a timeout.But surely that timeout will never occur unless the silicon is defective?

author: Siwastaja
author_rank: Super Contributor
timestamp: March 07, 2022, 02:05:58 pm
content: Quote from: peter-h on March 07, 2022, 02:02:16 pmBasically, this.There are interesting cases though, for example you can trigger ADC conversion from an external source. In such case the possibility for errors grow from nearly non-zero.But for software-triggered or continuous ADC, with such simple peripheral like the ADC is, the only reason would be silicon failure. It's still a good idea to have that timeout, but doing anything else than to report fatal error with some unique error ID would likely be waste of time. Recovering from serious failures is difficult! Especially if you can't afford even a device reset.

author: peter-h
author_rank: Super Contributor
timestamp: March 12, 2022, 12:22:55 pm
content: This is all working fine. Thank you all for your help. This is a bit of code from the 1kHz timer tick ISR. The extra ISR exec time is only about 5us.Code:// Read ADC1. Done directly, to avoid mutex-protected function which can't be called// from an ISR.ADC1->SR = ~(ADC_FLAG_EOC | ADC_FLAG_OVR);ADC1->CR2 |= (uint32_t)ADC_CR2_SWSTART;while (((ADC1->SR) & ADC_FLAG_EOC)==0) {}ADC1->SR = ~(ADC_FLAG_STRT | ADC_FLAG_EOC);adcval = (uint16_t) ADC1->DR;if ( adcval < ADC1_5V_FAIL_LIMIT ){// Issue the (non blocking) FLASH write command (takes about 20us)// This is a Buffer 2 -> FLASH writeWritePage_NoErase_Buf2(PD_PAGE); // Issue a non blocking write command to the FLASH (takes 2.5ms)// At this point it would be good to shut down anything that draws a lot of powerdisplay_cls();// Blank LEDs// Enter CPU standby modeCLEAR_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 modeSET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));// Set SLEEPDEEP bit__WFI();// request "wait for interrupt"// Should never get herewhile(true){}

author: uer166
author_rank: Super Contributor
timestamp: March 12, 2022, 10:30:03 pm
content: Quote from: peter-h on March 12, 2022, 12:22:55 pmIsn't this a blocking call that forces synchronization with ADC peripheral? I would simply add a check and keep going, accepting that no value might be present at that time, than risk the entire ISR getting blocked.. In the next ISR call it's guaranteed that the conversion will be complete, if ADC is properly setup. If not, it's trivial to add logic that detects consecutive missed readings, and shut down safely.

author: peter-h
author_rank: Super Contributor
timestamp: March 13, 2022, 07:16:59 am
content: That wait is for the ADC conversion to complete.I agree in general but- the whole start + conversion time is ~2us (measured, with the 3 clock sampling period); the entire ISR exec time, which includes loads of stuff like decrementing dozens of timers, executes in under 10us, which is 1% of CPU time- the ST 32F experts here (see above) agree that the only way that loop can hang is if the silicon is defective- the obvious solution (starting the ADC on one tick and reading the result on the next tick) was originally implemented but it delays the detection of power failure by 1ms, which is too significant in this case (I spent a whole day on checking the VCC fall, versus how much stuff I turned off to conserve power)- there is no practical way to report defective silicon, and a timeout would just give you a rarely-used feature which will be duffI re-arranged stuff a little, disabling Systick early on, because that stops the RTOS which is usefulCode:// Read ADC1. Done directly, to avoid mutex-protected function which can't be called// from an ISR.ADC1->SR = ~(ADC_FLAG_EOC | ADC_FLAG_OVR);ADC1->CR2 |= (uint32_t)ADC_CR2_SWSTART;while (((ADC1->SR) & ADC_FLAG_EOC)==0) {}ADC1->SR = ~(ADC_FLAG_STRT | ADC_FLAG_EOC);adcval = (uint16_t) ADC1->DR;if ( adcval < ADC1_5V_FAIL_LIMIT){// stop systick, just in case (also stops RTOS)#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) )portNVIC_SYSTICK_CTRL_REG = 0UL;// Issue the (non blocking) FLASH write command (takes about 20us)// This is a Buffer 2 -> flash writeWritePage_NoErase_Buf2(xxxx);// At this point it would be good to shut down anything that draws a lot of powerdisplay_cls();// Blank 7-segment LED displayif (g_ARINC429) ARINC_reset();// Stop any ARINC429 activityGPIOD->BSRR = 0x0000fc80;// Turn off all LEDs and 485 driver// 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();// request "wait for interrupt"// Should never get herewhile(true){}The __WFI is not obvious. The description in the CPU RM PM0214 page 47/48 suggests various things could bring the CPU out of this mode. There is also thishttps://www.eevblog.com/forum/microcontrollers/how-fast-does-st-32f417-enter-standby-mode/msg4057513/#msg4057513