cancel
Showing results for 
Search instead for 
Did you mean: 

ADC mutli channel, DMA odd behaviour

BRapo.1
Associate II

Hello, I'm developing a power meter board.
I'm using an STM32F427VIT6 and I need to read the analog values of 4 different channels (PA0 - PA3).


Since I need to calculate the RMS value of the measurement and i need the data points to be evenly spaced in time I decided to do use DMA and trigger the conversion using a timer.

- Configured the ADC in Scan Conversion Mode

- Set it 4 Conversions and changed Rank 1 to Channel 0, Rank 2 to Channel 1, etc...
- Set it to 28 Cycles per conversion

- Set it to be triggered by the Out event of TIM2

image.png

Then I enabled the DMA of ADC1 to be in normal mode, I want to do a bunch of conversions at a time but only when I fire them, not all the time.

image.png

 

Then I configured TIM2 to fire at the rate I want. I decided I want to store around 200ms of data using around of 128KB of buffer, because that's like 65% of my ram.

I may have calculated wrong, but the timer clock is 160Mhz with a Prescaler of 1 and a Counter Period of 1951 I get a frequency of around 41Khz

image.png

--------------------------------------------------------------------------------------------------------------------------------------------

image.png

 

Then I set up a buffer to store the data in

 

 

#define ADC_BUFF_LEN (8192 * 4)

uint32_t ADC_BUFF[ADC_BUFF_LEN] = {0};

 

 

It's multiplied by 4 because the DMA store the data of all 4 channels every conversion so It's actually performing 8192 measurements per channel. At 41 Khz it gives a total time of around 200ms.

I'm starting the ADC, DMA and TIM2:

 

 

  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_USART1_UART_Init();
  MX_TIM2_Init();
  /* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start(&htim2);

 

 

And the when I need to read the values I do

 

 

uint8_t cb = 0;

void read_analog()
{
	cb = 0;
	HAL_ADC_Start_DMA(&hadc1, ADC_BUFF, ADC_BUFF_LEN);
	while(cb == 0);
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
	cb = 1;
}

 

 

After that I get a buffer of length 8192 where the indexes 0, 4, 8, 12... are for ADC channel 0 and 1, 5, 9, 13... are for channel 1, etc...

 

And I calculate the RMS Value of each channel

 

 

float calculate_rms(uint32_t *array, uint16_t length, uint8_t offset)
{
    uint64_t sum_of_squares = 0;
    uint16_t count = 0;

    // Iterate over the array, picking every 4th element starting from 'offset'
    for (size_t i = offset; i < length; i += 4)
    {
        sum_of_squares += (uint64_t)array[i] * array[i];
        count++;
    }

    // Calculate the RMS value
    float mean_of_squares = (float)sum_of_squares / count;
    return sqrtf(mean_of_squares);  // Use sqrtf for float version of sqrt
}

  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  read_analog();
	  float current = calculate_rms(ADC_BUFF, ADC_BUFF_LEN, 0);
	  float volatge = calculate_rms(ADC_BUFF, ADC_BUFF_LEN, 1);
	  float halfVoltage = calculate_rms(ADC_BUFF, ADC_BUFF_LEN, 2);
	  float reference = calculate_rms(ADC_BUFF, ADC_BUFF_LEN, 3);
  }

 

 

I put an interrupt point after calculating the rms


The problem is the following:


current and halfVoltage (offset 0 and 2) are perfect, very stable always read the same.
voltage and reference (offset 1 and 3) oscillate between 2 vales. One is the correct value and the other is incorrect, but always the same.
For voltage It's 2118 (correct value) and  2538 (wrong value). In addition, every time I start a new debugging session it starts with the incorrect value and then every break point it alternates one and one.

Some things I tried:

 

  • There is no electrical instability, I already measured and there is no voltage oscillation.
  • I added a big delay after the DMA call, to be sure I'm not reading the buffer whilst writing
  • I increased the sample cycles to 144 to be sure is not reading instability
  • I added a HAL_ADC_Stop_DMA(&hadc1); after the DMA is done to make sure it stopped.

 

NOTE:

There is one thing that helped. When I changed End Of Conversion Selection to EOC flag at the end of all conversions in the configuration of the ADC. It became more stable, reading correctly maybe 6 out of 10 times, now it oscillates between 3 values, 1 correct and two wrong.

 

Any ideas why this might happen?
Thank you!

 

 

1 REPLY 1
Karl Yamashita
Lead III

Try setting the DMA to Circular mode and enable DMA Continuous Request

Tips and Tricks with TimerCallback https://www.youtube.com/@eebykarl
If you find my solution useful, please click the Accept as Solution so others see the solution.