EEVblog Electronics Community Forum

Electronics => Microcontrollers => Topic started by: BorisRap on November 26, 2024, 07:19:59 am

Title: STM32F ADC, DMA odd behaviour
Post by: BorisRap on November 26, 2024, 07:19:59 am
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

(https://community.st.com/t5/image/serverpage/image-id/94135i6ADD546080873D66/image-size/large/is-moderation-mode/true?v=v2&px=999)

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.

(https://community.st.com/t5/image/serverpage/image-id/94136iF62E00BD73065D31/image-size/large/is-moderation-mode/true?v=v2&px=999)

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

(https://community.st.com/t5/image/serverpage/image-id/94137iD985B4BA139BF69B/image-size/large/is-moderation-mode/true?v=v2&px=999)

(https://community.st.com/t5/image/serverpage/image-id/94138iEB48EBB6621FC2C5/image-size/large/is-moderation-mode/true?v=v2&px=999)

Then I set up a buffer to store the data in

Code: [Select]
#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

Code: [Select]
  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

Code: [Select]
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

Code: [Select]
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:

 

       

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!
Title: Re: STM32F ADC, DMA odd behaviour
Post by: DavidAlfa on November 26, 2024, 09:17:08 pm
ADC registers are 16-bit so DMA should be set to half word, and declare your buffer as uint16_t.
Try that first.
Title: Re: STM32F ADC, DMA odd behaviour
Post by: BorisRap on November 27, 2024, 06:56:59 am
ADC registers are 16-bit so DMA should be set to half word, and declare your buffer as uint16_t.
Try that first.

Thank you, I already tried, I declared the buffer as uint16_t and set the DMA to half word, the function HAL_ADC_Start_DMA expects a pointer to uint32_t so I cast it like this (uint32_t*)ADC_BUFF; but the buffer is not filled. I remember reading somewhere that the buffer must be of uint32_t type.
Title: Re: STM32F ADC, DMA odd behaviour
Post by: BorisRap on November 27, 2024, 02:58:37 pm
Ok, I figured it out. It turns out one of the VCAP wasn't properly soldered. Crazy thing happen when those caps aren't there...
Title: Re: STM32F ADC, DMA odd behaviour
Post by: DavidAlfa on November 27, 2024, 03:03:04 pm
Yes afaik you have to cast it like that.