STM32 gotchas
211.Unexpected ADC readings when using interrupt, and ISR execution is too slow

ADC implementations vary significantly across STM32 families, so register/bit names and mechanisms mentioned here (based on 'L0) are only illustrative and in particular STM32 family may be different.

Similarly to other peripherals which produce (or consume) data (such as UART, I2C, SPI, etc.), data produced by ADC can be read out basically in three ways:

  1. polling - processor reads repeatedly the flag indicating data are ready (ADC_ISR.EOC), and then reads the freshly converted value from the data register (ARC_DR)

  2. interrupt - the data-ready flag (ADC_ISR.EOC) can usually trigger an interrupt, after that is enabled through a flag specific for this event (ADC_ISR.EOCIE) and through the NVIC enable bit respective to ADC. Then, processor in the ISR (possibly after having qualified the interrupt source) reads out the new value from data register.

  3. DMA - after enabling DMA trigger in ADC (ADC_CFGR1.DMAEN) and setting up the respective DMA channel/stream, after each conversion finishes, DMA performs a transfer from the ADC data register to buffer in memory. It is then up to the processor to process the data in the memory buffer, usually after DMA triggers its half-complete and transfer-complete interrupts.

The pace (rate), at which ADC produces data, is determined by several factors:

So, in the cases, where conversion is started automatically by ADC immediately after previous conversion has finished, i.e. in cases of whole sequence conversion or the continuous mode, the rate at which data are produced is given by the selected ADC clock and prescaler (ADC_CFGR2.CKMODE/ADC_CCR.PRESC and in case of asynchronous clock, also RCC_CCIPR.ADCSEL), selected sampling time(s) (ADC_SMPR), duration of conversion itself - which in turn is dependent on the conversion resolution (ADC_CFGR1.RES), and oversampling, if enabled (ADC_CFGR2.OVSE/OVSR, taking in consideration possibility of triggered oversampling, ADC_CFGR2.TOVS).1

Fairly often, users push this rate towards the maximum allowed by the related settings (and datasheet), which is usually in the few MHz range. It means, that in case of such automatic successive conversions, at a typical 25-100MHz system clock, there is one ADC sample produced per a few dozens of system clock.

Given that both interrupt prologue and epilogue in Cortex-M processors takes around a dozen of cycles, if ADC is read out by interrupts, in such setting, the system is saturated by interrupts, and the ISR may miss ADC conversions, resulting in confusing ADC readouts. This is especially so if Cube/HAL is used, due to its convoluted interrupt handling.2

In some STM32, there is a ADC_CFGR1.WAIT bit, which disables automatic conversion start until the previous result is read from ADC_DR. This alleviates the above problem, but then may result in irregular conversion rate, which may also be a problem in some sort of applications.

1.In ADC of some STM32 families, there is an additional DELAY register bitfield allowing to spread automatically triggered conversions further apart.

2.The root of this problem is similar than that discussed for too frequent timer interrupts.