cancel
Showing results for 
Search instead for 
Did you mean: 

The timing for reading the ADC with DMA seems way too long. I calculate just under 36 us to read 8 ADC channels with a STM32L443CCT running at 64 MHz.

KiptonM
Lead

I am trying to trigger every millisecond, or 1000 microseconds, but my counters in the interrupt are seeing 167 interrupts per second as opposed to the expected 1000 interrupts per second.

I am reading 6 channels with a sample rate of 92.5 clocks per sample.

I am reading two channels with a sample rate of 247.5 clocks per sample.

Adding the 12.5 clocks for the ADC processing, that totals up to 1150 ADC clocks.

The Processor is running at 64 MHz, but the ADC Clock can not run that fast, so I divide by 2. or 32 MHz or 0.03125 us per clock cycle. Multiply by the 1150 clocks and I get 35.9375 us for the ADC to sample all 8 channels.

Since the tick timer is running at 1000 Hz or every 1000 us so the ADC completing in 36 us should be a piece of cake.

However it looks like the ISR is executing a complete ADC transaction with the DMA exactly 1000 times in 6 seconds not 1000 times in 1 second.

My first thought was the ISR overhead, with all of the HAL stuff. So I short circuited it with a call in the DMA1_Channel1_IRQHandler(void), so it does not call HAL_DMA_IRQHandler(&hdma_adc1);

void DMA1_Channel1_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel1_IRQn 0 */
  if ((DMA1->ISR & 0xF) != 0) ADC_DMA_Interrupt();
  else
  /* USER CODE END DMA1_Channel1_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_adc1);
  /* USER CODE BEGIN DMA1_Channel1_IRQn 1 */
 
  /* USER CODE END DMA1_Channel1_IRQn 1 */
}

And my ISR is very simple for now;

void ADC_DMA_Interrupt(void)
{
	if (DMA1->ISR & DMA_ISR_TCIF1) // transfer Complete
	{
		ADC_transfer_cnt++;
		DMA1->IFCR |= DMA_ISR_TCIF1;
	}
	if (DMA1->ISR & DMA_ISR_HTIF1) // Half Transfer
	{
		ADC_half_transfer_cnt++;
		DMA1->IFCR |= DMA_ISR_HTIF1;
	}
	if (DMA1->ISR & DMA_ISR_TEIF1)
	{
		ADC_error_cnt++;
		DMA1->IFCR |= DMA_ISR_TEIF1;
	}
}

What am I missing? Why is it so slow?

1 ACCEPTED SOLUTION

Accepted Solutions
KiptonM
Lead

Thanks Jan.

Sometimes I get too clever for my own good.

I had set up a circular DMA buffer. And the length was from a preprocessor #define

A few months ago I had set it up as twice the length needed. And forgot about it.

I moved the code to another processor and first I had the clocks wrong. I found that but could not understand why the counter in the transfer complete interrupt was half of the trigger count.

I had done that so both the half complete and complete interrupt gave me a full set of ADC values.

I was also moving the data in the interrupts as 4 32-bit moves as opposed to 8 16-bit moves to take less time, in the interrupt. (Which made it appear as moving 4 readings as opposed to the reality of 8 ADC readings.) Now appropriate comments are in the code.

It is working now.

View solution in original post

8 REPLIES 8
KiptonM
Lead

My calculations forgot to take into consideration oversampling 16. So it is 18400 clocks at 32 MHz or 0.03125 us per clock. Which totals out to 575 us to collect all samples. That is still less than 1000 us for the triggers. The issue is still there, just not as big as I thought.

KiptonM
Lead

I thought the clock into the ADC was 64 MHz. Looking at the output of the ADC Clock MUX it was 18 MHz which I divided by 2 made the time 2044.44 us which explains why I was getting 1 trigger out of 3,

KiptonM
Lead

The Temperature and Voltage reference both need 5 ms to sample. So basing the clock on that, with 247.5 clocks sample time with 12.5 clocks processing time, If the ADC Clock was 52 MHz that would equal 5 us. That leaves all the rest of the conversions at just over 2 us since they use 92.5 clock sample rate.

My calculations say that should equal 353.85 us for everything when oversampling by a factor of 16.

It appears that I am still missing a trigger as I am getting 500 readings per second when I am triggering 1000 times a second.

KiptonM
Lead

I cut the over sampling by a factor of 2, to 8 oversamples, and it did not make a difference. Still 500 readings per second, not 1000.

KiptonM
Lead

I completely got rid of oversampling and am still seeing half as many as I should. When I trigger every millisecond, I get 500 samples per second. And it does not matter whether I do no oversampling or 16 oversamples.

When Oversampling I think it should take < 354 us when not oversampling it should take about 22 us.

Make sure your clocks run as you think they do: write a simple program which sets up the clocks identically to your program, sets up a single ADC conversion, toggles a pin, starts conversion, waits polling until conversion end and toggles pin again. That gives you the true ADC clock.

Then in your program make the time which triggers the conversions output a PWM, so you can see it's true frequency.

Make sure the program does nothing else just the ADC conversions, so that you don't influence it by anything else (especially interrupts of any source).

JW

KiptonM
Lead

Thanks Jan.

Sometimes I get too clever for my own good.

I had set up a circular DMA buffer. And the length was from a preprocessor #define

A few months ago I had set it up as twice the length needed. And forgot about it.

I moved the code to another processor and first I had the clocks wrong. I found that but could not understand why the counter in the transfer complete interrupt was half of the trigger count.

I had done that so both the half complete and complete interrupt gave me a full set of ADC values.

I was also moving the data in the interrupts as 4 32-bit moves as opposed to 8 16-bit moves to take less time, in the interrupt. (Which made it appear as moving 4 readings as opposed to the reality of 8 ADC readings.) Now appropriate comments are in the code.

It is working now.

Thanks for coming back with the solution.

The ability to move around 16 and 32 bits of data, in contrast to 8-bit only in the 8-bitters, is most of the time a blessing, but sometimes a curse too... The real ******** "fun" is to move MSB-first data for 24-bit peripheral through SPI set to 16-bit for speed, using DMA set to 32-bit again for speed... ask me how I know... 🙂

JW