Author Topic: STM32F ADC, DMA odd behaviour  (Read 1069 times)

0 Members and 1 Guest are viewing this topic.

Offline BorisRapTopic starter

  • Contributor
  • Posts: 41
  • Country: de
STM32F ADC, DMA odd behaviour
« 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



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.



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





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:

 

   
  • 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!
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 6331
  • Country: es
Re: STM32F ADC, DMA odd behaviour
« Reply #1 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.
« Last Edit: November 26, 2024, 09:20:10 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline BorisRapTopic starter

  • Contributor
  • Posts: 41
  • Country: de
Re: STM32F ADC, DMA odd behaviour
« Reply #2 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.
 

Offline BorisRapTopic starter

  • Contributor
  • Posts: 41
  • Country: de
Re: STM32F ADC, DMA odd behaviour
« Reply #3 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...
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 6331
  • Country: es
Re: STM32F ADC, DMA odd behaviour
« Reply #4 on: November 27, 2024, 03:03:04 pm »
Yes afaik you have to cast it like that.
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf