cancel
Showing results for 
Search instead for 
Did you mean: 

ADC sample rate impossible to set precisely (calculations added)

Robmar
Senior III

I realise that with ADC timer triggered DMA of 32 samples, you can set the timer precisely, but not the ADC​ per sample clock, so the samples get bunched up or stretched in the frame. The number of clocks per sample is limited, adjusting the APB2 clock hardly helps. A bit more flexibility on sample clocks would be really useful, I guess there is no easy solution?

Objective: capture ADC samples spread evenly at 96 KS/s on STM32F407VG with 8MHz xtal

Reference: STM32F406VG datasheet.

Overview:-

To capture samples from two independent ADCs at 96 KS/s, Timer3 is set to generate

an event every 1/3 mS, i.e. 333.3r uS

ABP1 Clk 84 MHz drives Timer3 from a 42MHz clk via a fixed x2 multiplier.

ADC-DMA is triggered and set to take 32 samples:

APB2 (PCLK2 + prescaler /4,/6,/8) clk drives ADCs at 84 MHz: Period 11.904 nS

Okay, so 3,000 triggers of 32 samples pers second.

1. 3K triggers the ADC-DMA every 333.3r uS

2. 32 samples every 333.3r uS gives 10.416r uS per ADC sample.

3. a1) ADC clk at 84 MHz has a clk period of 11.904761 nS or 0.011904761 uS,

  b1) Prescaler of 8 reduces this to 10.5 MHZ, and a clk period of 0.095238088 uS

  c1) Prescaler 6 clk reduces to 14 MHz and a clk of 0.07142857 uS. 

  d1) Prescaler 4 clk reduces to 21 MHz and a clk of 0.04761904 uS. 

  e1) Prescaler 2 (MX deselected not peritted) clk ... 42MHz ...

  

  b1-R) P8: We need 10.416 uS worth of clocks per sample, giving 109.375008312501 clks at 10.5 MHz

  c1-R) P6: -""-, giving 145.82400291648 clks at 14 MHz

  d1-R) P4: -""-, giving 218.736034997776 clks at 21 MHz

  e1-R) P2: -""-, giving 437.472 clks at 42 MHz (MX deselected)

Each samples takes 15 clks + (3, 15, 28, 56, 84, 112, 144, 480 sample time clks selectable)

Best match: Prescaler 8, our 96KHz sampling rate requires 109.375 clks per 32 sample trigger, subtracting the ADC sample time of 15 for 16-bit conversion = 94.375 clks, nearest selection is Sample time of 84 + 15 = 99, an increase in sample rate by 10.375 clks per sample.

This increase in the sample rate will require a significant non-standard change to my DSP code. A single transfer per trigger would solve this if there was a way that the ADC-DMA could increment the memory address, but so far I can't see that is supported.

43 REPLIES 43

To be precise, is this code correct for one sample per timer3 trigger?

Because it only performs the 32 sample transfer once, and never more, not even if I call HAL_Start... again at the end of conversions interrupt.

Setup:

 htim3.Instance = TIM3;

 htim3.Init.Prescaler = 0;

 htim3.Init.CounterMode = TIM_COUNTERMODE_UP;

 htim3.Init.Period = 875-1; // Trigger clks for 96 KHz = 875-1 Clk: = (1/96K) / (1/84M)

 htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

 if (HAL_TIM_Base_Init(&htim3) != HAL_OK)

 {

  Error_Handler();

 }

 sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;

 if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)

 {

  Error_Handler();

 }

 sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;

 sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

 hadc2.Instance = ADC2; // ADC1 the same

 hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV8;

 hadc2.Init.Resolution = ADC_RESOLUTION_12B;

 hadc2.Init.ScanConvMode = DISABLE;

 hadc2.Init.ContinuousConvMode = DISABLE;

 hadc2.Init.DiscontinuousConvMode = DISABLE;

 hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;

 hadc2.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T3_TRGO;

 hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;

 hadc2.Init.NbrOfConversion = 1;

 hadc2.Init.DMAContinuousRequests = DISABLE;

 hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV; // ADC_EOC_SEQ_CONV (no change using either flag)

 sConfig.Channel = ADC_CHANNEL_2;

 sConfig.Rank = 1;

 sConfig.SamplingTime = ADC_SAMPLETIME_84CYCLES;

  /* ADC1 DMA Init */

  /* ADC1 Init */

  hdma_adc1.Instance = DMA2_Stream0;

  hdma_adc1.Init.Channel = DMA_CHANNEL_0;

  hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;

  hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;

  hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;

  hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

  hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;

  hdma_adc1.Init.Mode = DMA_CIRCULAR;

  hdma_adc1.Init.Priority = DMA_PRIORITY_VERY_HIGH;

  hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_ENABLE;

  hdma_adc1.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;

  hdma_adc1.Init.MemBurst = DMA_MBURST_SINGLE;

  hdma_adc1.Init.PeriphBurst = DMA_PBURST_SINGLE;

Start code:

// Start DMA of single samples at 96 KHz from tier 3 triggers

// Start timer last as that triggers both ADC DMA channels so they run synchronously (tested works)

HAL_ADC_Start_DMA(&hadc1, (uint32_t*)a_in, sizeof(a_in)/sizeof(a_in[0]));

HAL_ADC_Start_DMA(&hadc2, (uint32_t*)b_in, sizeof(b_in)/sizeof(b_in[0]));

HAL_TIM_Base_Start_IT(&htim3);

> ​PS talking of lingo, I don't know what RM is, and googling STM RM lists nothing.

Reference Manual. In case of STM32F407, it's RM0090. That, together with DS (DataSheet) and ES (Errata Sheet) are the basic documentation to the microcontroller you are using, primarily to be found in the web folder of your microcontroller (together with secondary documentation like Application Notes, AN).

"STM" is too vague. Google for "STM32" or directly the model name, "STM32F407".

> Adc dma copies n samples to memory as adc samples complete following the timer trigger

Basically yes, but note, that there is no "ADC DMA". ADC and DMA are two separate modules, connected only by the "request" signal, through which ADC triggers the DMA stream (if that stream has selected that signal in its request multiplexor, DMA_SxCR.CHSEL.

> I​n my code case, we start this requesting 32 samples to a circular buffer, ok?

OK. That would result in DMA throwing a Transfer Complete interrupt (signaled by DMA_LISR/HISR.TCxIF, triggering interrupt if enabled by setting respective DMA_SxCR.TCIE) after 32 samples. But DMA being set to circular, if timer is not stoped or ADC is not disabled, the whole process would continue regardless of what you do in software, and you may run into the risk of having the first few samples in the memory buffer overwritten until the ISR (Interrupt Service Routine) gets to the point where it reads out those data from buffer. That's why the usual way to tackle this is to set DMA_SxNDTR to 2*N i.e. in your case 64 samples, and set up both Transfer Complete and Half Transfer interrupts.

> But, with DMA constant requests disabled​, the bits are setup such that the timer trigger

> starts not the sequential dma transfers of all 32 samples, but just one adc dma transfer,

> of one sample, per trigger, is that what you're saying?

I don't know what exactly are "DMA constant requests". Again, DMA is entirely independent from ADC and does not in any way determine how ADC performs conversions. All DMA does is, that if it sees active request signal (which when set to ADC gets active after each conversion end), it reads out data from address given by DMA_SxPAR and writes it to address given by DMA_SxMAR. So, what you are referring to by "DMA constant requests" is probably the Continuous conversion mode of ADC (see subchapter with this name in ADC chapter in RM), as enabled by setting ADC_CR2.CONT. And yes, that would result in multiple ADC conversions per timer trigger.

> To be precise, is this code correct for one sample per timer3 trigger?

I don't know, I don't use Cube/HAL, and I also recommend against using it. The reason is, that Cube/HAL is designed to provide rapid results when clicking in CubeMX, inevitably (as any other "library") implements only a limited subset of the possible usage cases - those deemed by its authors as "typical", whatever that means - and individual data structures and functions of it have an entangled mutual relationship which is not always satisfactorily documented. In other words, if you can't click your application in CubeMX, chances are, that Cube/HAL will sooner or later get into your way more than help.

One major drawback of this approach is, that ST for years actively deters users from the normal register-based programming and pushes Cube on them agressively.

I understand that going for register-based programming would mean to start from scratch and even unlearn what you've learned so far. In my opinion this pays off in the long term. My recommended way to do things is to learn to crawl, then walk, and only after that run. In this case it would be using the ADC in polled mode in a trivial program which does nothing else (perhaps outputting the ADC readouts onto DAC, or onto a set of LEDs, or in any other suitable but very simple way); then doing the same with timer triggering, and only after that would I add DAC. In this way, only one peripheral at a time is learned, the new information influx is not that overwhelming, and the grasp on the individual concepts gets firmer.

Others have different opinions on this.

> Because it only performs the 32 sample transfer once, and never more, not even if I call

> HAL_Start... again at the end of conversions interrupt.

Again, I don't know what exactly Cube/HAL does, and while I could find it out given Cube is open source, I have absolutely no intention to do so. If you set DMA to Circular, you should not need to start it again, you should see the transfer complete interrupts coming after every 32 samples. If you don't you or Cube did something wrong, and it's upon you to figure out, what. If you present a significant buying power, as expressed in $M of chips purchases, I positively know ST will be happy to assist you through this, just ask through your FAE or through the web support form.

> hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_ENABLE;

Don't use DMA FIFO until you exactly know what does that entail.

JW

AScha.3
Chief II

try : set ADC_SAMPLETIME to lower , 15CYCLES;

and adc clock higher, PCLK_DIV4 (-> 21MHz , good in specs.)

now too close to "impossible" .

just, what i would do.

+

RM = reference manual - or what did you read , to know how to use the soc/chip ?

If you feel a post has answered your question, please click "Accept as Solution".

I now have the reference manual in all its near 1000 pages of details, just what I need so thanks for that.

It never crossed my mind that the Cube MX setup would not allow me to run any reasonable ADC-DMA (ADC to DMA unit) mode, but now at least with the manual I can check it all through.

I use SPI-DMA for the LCD and found that it was less reliable without FIFO, which I understands is a cache that allows packing of data, so enabled it for the ADC. I will try without to see the affect.

Currently the first 32 transfers start and arrive in the buffer with the expected data results.

However, following that there are no more completion interrupts, so the timer trigger or something else is not causing the ADC to repeat.

I guess I´ll have to dig thru it, at least with the RM I have all the info I should need. Thanks

So you think that the transfer fails to repeat because the sample time leaves insufficient time for the units to do their thing? I´ll shorted the sample time down one step from 84, that should be plenty, but I'm not convinced this is the problem.

No idea how I ever missed the RM manual, STMs sites never seemed to reference it, app notes, samples, videos etc., but no chip "bible", great that I now have it, 1000 pages!

AScha.3
Chief II

on H743 , my audio streamer from SD , USB, web-radio, ds is 350 p. , rm is 3270 p. and errata, also must read :) is 42 p.

so you know now, what you have to read first. :)

If you feel a post has answered your question, please click "Accept as Solution".
Piranha
Chief II

> googling STM RM lists nothing

In my Google the 4th result is a link to "RM0008 Reference manual", which is a reference manual for F1 series. Also I told you to read the "reference manual" 3 times and even pointed to a specific section. And I don't know what you downloaded, but the RM0090 is not even close to 1000 pages... short.

hadc2.Init.DMAContinuousRequests = DISABLE;

Looked up what exactly this renamed wonder is... It's a ADC_CR2 bit DDS. Therefore you indeed must enable it.

By the way, searching about DDS bit, I found these suspicious sentences in RM0090.

Section "13.8.1 Using the DMA" at page 400:

At the end of the last DMA transfer (number of transfers configured in the DMA controller’s DMA_SxNDTR register)

Also "Bit 9 DDS: DMA disable selection (for single ADC mode)" description at page 419:

0: No new DMA request is issued after the last transfer (as configured in the DMA controller)

And "Bit 13 DDS: DMA disable selection (for multi-ADC mode)" description ar page 428:

0: No new DMA request is issued after the last transfer (as configured in the DMA controller).

ADC dependent on DMA_SxNDTR register? Or should it be "at the end of ADC sequence", which is not the same thing? @Community member​ , what do you think?

> I now have the reference manual in all its near 1000 pages of details, just what I need so thanks for that.

You don't need to read it back to back. Skim the first few chapters, read RCC, TIM, DMA, interrupt chapters (for interrupts, may want to have a look at the Cortex-M4 PM too). These chapters follow a pattern - there's a narrative going from general to specific, where you can skip the too-specific-doesn't-relate-to-my-case parts; and each chapter ends with description of the registers. After (or maybe during?) reading the narrative, go through the registers and try to match them with what you've read in the narrative. Later you'll refer back to those registers often, but you probably will go back to the narrative only occasionally.

As AScha said above, you may want to read (or at least skim through) also the DS and ES.

>> googling STM RM lists nothing

> In my Google the 4th result is a link to "RM0008 Reference manual", which is a reference manual for F1 series.

Google personalizes the replies and even if you attempt to anonymize yourself, they spent the last two or three decated to be good in producing fingerprints. "STM" is vague enough, if you have access to somebody else's computer, try there, the results may be vastly different (btw. RM0008 was 2th result for me, I often google for STM32 datasheets on this computer).

> It's a ADC_CR2 bit DDS. Therefore you indeed must enable it.

Nice catch! This is most likely it.

> ADC dependent on DMA_SxNDTR register?

There is a signal from DMA towards certain peripherals - besides ADC it's also I2C (see I2C chapter) - based on NDTR approaching zero. It appears to be done in a rather ad-hoc manner, to fit exactly requirements of given peripheral. I've never gotten to investigate this deeper.

JW

What's made it work was setting CR2 Bit 9 DDS (what the MX configurator calls "DMAContinuousRequests".

From the book, DDS: DMA disable selection (for single ADC mode) This bit is set and cleared by software. Set to 1: DMA requests are issued as long as data are converted and DMA=1

To check that this was still being Timer driven I ran the code again but didn't start the timer, and the sampling transfers never started.

Two different people, including a YouTuber on STM32F4 ADC with DMA, said this bit was to be disabled, which seems odd considering what the RM states.

For some reason now my ADC2 channel isn't capturing, will try to find out why, curious!

Most likely for multiple ADC peripherals you have to use the very similar DDS bit in ADC_CCR register. And it looks like, using ADC_CDR, you can save data from both ADC instances with a single DMA and get the data interleaved in a single buffer.