cancel
Showing results for 
Search instead for 
Did you mean: 

Understanding ADC Sampling

Rogers.Gary
Senior II
Posted on September 16, 2014 at 06:30

I have a simple operation to perform with the STM32F4 (on a Discovery board) but the solution escapes me. Mostly from the fact that, even after looking at sample code and reading the manual, I don't quite get it (yet)

//pseudo code

1 - configure peripherals

2 - begin sampling in background, sample at 1KHz rate

3 - sample until buffer has 2000 ADC samples

4 - when sampling complete, trigger interrupt (should take 2 seconds for buffer to fill)

5 - process samples - ADC should still be buffering next sample block

6 - go back to #4

Seemingly simple, but I do not understand how to get the ADC configured so it samples at the correct rate- in this case, 1Khz. The key is, the sampling rate must be near exact for the fft to perform correctly. Part of it is - in continuous mode, while using DMA to fill the buffer in the background, I don't quite understand the relationship between the Timer's interrupt rate and the sampling frequency.

I need to do some fft processing after the buffer is filled, but do not want that to happen in a timer-driven ADC acquisition because of the fft processing demands.

Can someone point my beak in the right direction on this? A post where there might sample code ( I searched but cannot find) or an explanation.

Thank you in advance...

5 REPLIES 5
Posted on September 16, 2014 at 15:12

Well the example I have to hand is 200 KHz, but it's just a matter of factoring the division between the Prescaler and Period while keeping the Prescaler within 16-bit and the Period 16/32-bit depending on the timer.

The timer can trigger the conversion of the ADC, this can cause the sampling of one, or multiple channels. The sampling of the other channels will each be delayed in the time domain based on the sample rate settings of the ADC, not the triggering rate from the timer.

If you need two channels sampled at the exact same time you can use the Dual mode to bind ADC1 and ADC2 together.

I would simply double the buffer size to permit the Half Transfer (HT) and Transfer Complete (TC) to provide a stable buffer for processing.

https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/ADC%20trigger%20on%20timer%20update&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=502

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
groufosse
Associate II
Posted on September 16, 2014 at 16:44

Clive1,

Thank you for taking the time to answer this. In fact, I did find this code previously. It copied it verbatim, it compiles and appears to work. However, please explain this comment.

void DMA2_Stream0_IRQHandler(void)

 

// Called at 1 KHz for 200 KHz sample rate, LED Toggles at 500 Hz

Please explain why it's called at a 1Khz rate - where is that defined? Or is this a typo?

I still do not see what sets the ADC sampling rate/frequency? Is it not possible to set that

to free run at some frequency, fill the assigned buffer, and interrupt when complete?

The only thing I can see the timer period being set in the function at 200Khz.

At a 1Khz (or 500Hz rate)  the LEDS on my board should flicker, they appear to be on solid.

Sorry for the dum-dum questions....trying to understand.

groufosse
Associate II
Posted on September 16, 2014 at 17:14

Clive1,

Also, in the file stm32f4xx_adc, it describes adc dma mode one as:

#define ADC_DMAAccessMode_1             ((uint32_t)0x00004000)     /* DMA mode 1 enabled (2 / 3 half-words one by one - 1 then 2 then 3)*/

It seems to think it's 3 half words. is this a typo?

Posted on September 16, 2014 at 17:19

Please explain why it's called at a 1Khz rate - where is that defined? Or is this a typo?

 

It's an implied consequence of the math, 200 samples (half the buffer) taken at a 200 KHz (5 us) rate, will result in a 1 KHz (1 ms) interrupt. Toggling a GPIO, will result in a further halving of the frequency as you have 1 ms high, and 1 ms low, ie 2 ms period or 500 Hz.

I still do not see what sets the ADC sampling rate/frequency? Is it not possible to set that to free run at some frequency, fill the assigned buffer, and interrupt when complete?

The timer is triggering the start of each conversion, the end of conversion results in a DMA transfer. The timer is set to generate pulses at the 200 KHz rate (ie update interval, as defined by the combination of the prescaler and period divided into the source clock). I'm sure you can jam up the continuous conversion, and play games with the sample (and conversion) time, but you won't get to 1 KHz, and it won't be convenient. This will require you to understand the internal mechanics of the ADC with a lot more clarity.

The only thing I can see the timer period being set in the function at 200Khz.

 

At a 1Khz (or 500Hz rate)  the LEDS on my board should flicker, they appear to be on solid.

I don't think you can perceive 500Hz with your eyes, I'd recommend you use a scope. My bench Tektronix will measure/display frequency, the example seems to be functioning as described, probing the GPIO side of the Orange LED (LD3).

DMA Modes relates to Dual and Triple mode operation, one would need to review the diagrams in the Reference Manual to between understand how the DMA and the Common Data Register interact. I have a functional understanding of how it works but don't really have the time/desire to document it, ADC's really aren't my thing.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Rogers.Gary
Senior II
Posted on September 16, 2014 at 22:21

Clive1,

After getting down to the details I have figured it out. Now I see there is no direct way to alter the ADC sampling rate. It has to be done either by a timer driven interrupt, or the way I did it.

As you mentioned, I toggled LED3 and scoped it each time I made a change.

I started out by configuring the ADC_SampleTime_XXCycles, and the ADC_Prescaler_DivX, to 480 and 8, respectively. I also configured the GPIO for the pins I required and set up the DMA.

In the ADC interrupt handler, I toggled the LED3 there and measured the frequency. Since it's toggling, the interrupt frequency is twice the frequency of the LED.

I adjusted both the ADC sample time and prescaler (above) until I got in the range I wanted for the resolution of the accumulate and divide of the sample. The end numbers were ADC sample at 144 cycles, and prescaler at Div2. Here are my board's clock setup numbers:

SYSCLK_Frequency = 168000000

HCLK_Frequency = 168000000

PCLK1_Frequency = 42000000

PCLK2_Frequency = 84000000

Here is the handler code:

void ADC_IRQHandler(void)

{

    /* acknowledge interrupt */

    ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);

    uint32_t samplingCounter = SAMPLE_FREQ;

    accumulator += ADCBuffer[1];

    

/*

134 counts = 997Hz - not quite 1Khz

66 = 2KHz exactly

so that either one of these is used:

#define SAMPLING_FREQ  (uint32_t)134

#define SAMPLING_FREQ  (uint32_t)66

        

*/        

        if(counter++ == samplingCounter)

        {

          //divide down the accumulated sample

          accumulator /= counter;

          //create the voltage

          ADC_Values[bufferCounter++] = ( (accumulator * VREF) / ADC_COUNTS );

          

          if(bufferCounter == BUFFER_SIZE)

          {

            bufferCounter = 0;

            bufferFilledFlag = TRUE;

            STM_EVAL_LEDToggle(LED3);

          }

          counter = 0;

          accumulator = 0;

        }

        

}

The variable ''bufferFilledFlag'' is temporary until I set up RTOS, where I'll use a message queue. I now have lots of time to process the samples in fft without being a clock hog.

Hope this helps someone else in setting up their ADC.