cancel
Showing results for 
Search instead for 
Did you mean: 

Best way to configure ADC reading for speed

evanpol
Associate III

Hello,

 

I am trying to configure my STM32H723ZG to be able to read the value of a few current sensors. My main concern at the moment is the speed at which I need to read these. I'm hoping to be reading upwards of rate of 20kHz. 

My questions are about which method might be best, the standard way of reading the ADCs seems insufficient, I've seen online some methods are DMA and injected conversions. If anyone could provide some insight into what might be best and how to go about implementing them that would be great.

Additionally, I wanted to ask how to determine an appropriate sampling time of the ADC.

 

Thank you in advance for your help!

7 REPLIES 7
Aime
ST Employee

Hi @evanpol,

 

Here's a detailed explanation and solution to help you achieve fast and efficient ADC readings on your STM32H723ZG, targeting a sampling rate of around 20 kHz for multiple current sensors.

 

Challenges

  • Reading multiple ADC channels at 20 kHz means a total sampling rate of at least 20 kHz per channel.
  • Standard polling-based ADC reading is CPU-intensive and often too slow for such rates.
  • You need a method that offloads data transfer and minimizes CPU overhead.

a) DMA (Direct Memory Access) with Regular Conversions

  • How it works: ADC conversions are triggered (either by software or timer), and results are automatically transferred to memory via DMA.
  • Advantages:
    • CPU is free during conversion and data transfer.
    • Continuous and efficient data acquisition.
  • Implementation tips:
    • Configure ADC in scan mode to sample multiple channels sequentially.
    • Use a timer trigger (e.g., TIMx) to start ADC conversions at precise intervals (e.g., 50 µs for 20 kHz).
    • Configure DMA in circular mode to continuously fill a buffer.
    • Process data in the background or via DMA transfer complete interrupts.

b) Injected Conversions

  • Injected conversions are typically used for high-priority or asynchronous conversions.
  • They can be triggered independently from regular conversions.
  • Use case: When you need to sample a subset of channels at irregular intervals or higher priority.
  • For your use case (regular high-speed sampling), DMA with regular conversions is usually more efficient.

c) ADC with Timer Trigger + DMA (Best Practice)

  • Use a hardware timer to trigger ADC conversions at exactly 20 kHz.
  • ADC performs scan conversions of your sensor channels.
  • DMA transfers results to RAM without CPU intervention.
  • You get a continuous stream of samples with minimal CPU load.


How to Implement DMA + Timer Trigger ADC Sampling on STM32H7

Configure ADC:

  • Enable scan mode.
  • Select channels corresponding to your sensors.
  • Set sampling time (see next section).
  • Enable DMA request.
  • Set ADC to be triggered by timer event (e.g., TIM1 TRGO).

Configure Timer:

  • Set timer frequency to 20 kHz (period = 50 µs).
  • Configure timer to generate TRGO event on update.

Configure DMA:

  • Set DMA channel linked to ADC.
  • Configure circular mode to continuously fill buffer.
  • Set buffer size = number of channels × number of samples per batch.
  • Enable DMA interrupts if needed (e.g., half-transfer, transfer complete).

Start timer and ADC:

  • Start timer.
  • Start ADC in DMA mode.

Data Processing:

  • Process ADC data in DMA interrupt or main loop.
  • Use double buffering if needed for continuous processing.

 

Determining Appropriate ADC Sampling Time

The ADC sampling time is the time the ADC input is connected to the sampling capacitor before conversion starts. It affects:

  • Accuracy: Longer sampling time allows the capacitor to charge fully, especially for high source impedance sensors.
  • Speed: Shorter sampling time increases conversion rate but may reduce accuracy if the input impedance is high.

How to choose sampling time:

Check sensor output impedance:

  • If low (e.g., < 1 kΩ), short sampling times (e.g., 2.5 to 7.5 ADC clock cycles) are sufficient.
  • If high, increase sampling time accordingly.

Calculate total conversion time:

Tconv = Tsampling+12.5×TADCclock​​

  • For STM32H7, ADC conversion requires 12.5 ADC clock cycles plus sampling time.
  • ADC clock frequency depends on ADC prescaler and clock source.

Since you want 20 kHz per channel, even with multiple channels, this is achievable with ample margin.

 

Good luck,
Aime

MOBEJ
ST Employee

Hello @evanpol  , 

You can check the example for using the ADC peripheral to perform a single conversion on the internal temperature sensor and calculate the temperature in degrees Celsius at this link:

https://github.com/STMicroelectronics/STM32CubeF7/blob/master/Projects/STM32F767ZI-Nucleo/Examples_LL/ADC/ADC_TemperatureSensor/Src/main.c

This example uses the polling method and shows basic ADC configuration and reading. You can follow its structure as a starting point for your own application.

You can also check our other examples demonstrating ADC with different programming models such as DMA and interrupt-based methods in this link below : 
STM32CubeWB/Projects/P-NUCLEO-WB55.Nucleo/Examples_LL/ADC at master · STMicroelectronics/STM32CubeWB · GitHub

Br

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.
bramble
Senior

Hi @evanpol ,

You have a very good answer from @Aime , just to add a little detail to their recommended method (timer trigger + DMA). I'm assuming that you're using FreeRTOS, and if not then the following would need to be adapted, but the general approach is still applicable.

Let's say that you want to capture 5 channels each at 20kHz and that you want your software to process received data in chunks of 1024 samples at a time. In that case each chunk of samples will arrive at intervals of 1024 (samples) ÷ [5 (chan) x 20000 (samp/sec/chan)] = 10.24 ms.

You then need to assign an ADC buffer to capture the data, and the length of this should be double the length of each chunk: 2 x 1024 = 2048 samples.

Then implement the half-complete ADC interrupt handler. This should be very simple. A simple semaphore is given each time the interrupt fires.

Meanwhile, you have a task sitting in an endless loop (which it enters after creating the semaphore, initialising the timer and starting the ADC etc..). This task takes the semaphore, which will block the task until each half complete ISR fires. The task then has up to about 10ms (ideally taking much less time) in which to do whatever you need to do with the 1024 ADC samples.

On each firing of the ISR and processing of 1024 samples, you need to use the first and second half of the allocated ADC buffer in an alternating way: start with the first half, then the second half, then the first again etc - hence double buffering.

I hope this is helpful and apologies if I'm telling you stuff that you already know!

evanpol
Associate III

Hi all @bramble @MOBEJ @Aime,

Thank you very much for your help. Today I was able to successfully decrease the computation time of my control loop significantly, thanks to the ADCs running much faster!

I was wondering if any of you had ideas regarding how I can get data out of the STM without compromising performance? I was originally sending data through to a serial monitor using printf however this is very intensive and slows things down a lot and won't be viable.

Any thoughts are welcome, thank you for your help.

Hi @evanpol ,

It really depends on where you want the data to go. For example, if you have some kind of external data logger, then presumably you have a defined hardware interface to it? 

I wonder, do you actually need to output data, rather than capturing it to SD-Card on your STM32? Or maybe you can do whatever your external system needs to do with the data on the STM32 target, then reduce the required data rate for final output, rather than perhaps outputting to a PC (or something) for data analysis?

I'm speculating because I don't understand your system very well. If you can clarify your fundamental objective, and whatever constraints you need to work to, then I'm sure people will have better suggestions for you.

Hi @bramble , thank you for your advice and sorry for the slow reply!

Whilst it would be nice I don't necessarily need access to the data live, what's more important to me is overhead of the process. I am running a control look at 20kHz, currently the full loop takes ~40us to run leaving a max of 10us for any data logging. 

Today I explored using a J-Link, i quickly found out I needed to convert floats to strings to use them in RTT viewer and that the overhead for it was substantial. I also tried RTT logger and saving the data as a raw binary file. Whilst this was good computation-wise the post processing of the binary file proved to be quite difficult and I wasn't able to get any useful data.

I was hoping to get some advice on what you think the next move should be? What's the overhead like on reading to an SD card, what hardware do I need to make that work, and what's the best way to implement it?

I'd also be curious to see if you know of a better way to read data with J-Link, as that it a route I'd prefer to go down.

Thanks again for your help!

Hello, @MOBEJ @Aime  if anyone could shed some light on my previous question that would be great.

Thank you for your time!