2024-11-25 03:43 AM - edited 2024-11-25 03:43 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
#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:
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!
2024-11-25 06:03 PM
Try setting the DMA to Circular mode and enable DMA Continuous Request