STM32F ADC, DMA odd behaviour - Page 1
Forum section: electronics / general

author: BorisRap
author_rank: Contributor
timestamp: November 26, 2024, 07:19:59 am
content: 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 inScan 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 theOut eventofTIM2Then I enabled theDMAof 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 configuredTIM2to 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 aPrescalerof 1 and aCounter Periodof 1951 I get a frequency of around41KhzThen I set up a buffer to store the data inCode:#define ADC_BUFF_LEN (8192 * 4)uint32_t ADC_BUFF = {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 theADC,DMAandTIM2Code: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 doCode: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 channelCode: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 * array;count++;}// Calculate the RMS valuefloat 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 rmsThe problem is the following:currentandhalfVoltage(offset 0 and 2) are perfect, very stable always read the same.voltageandreference(offset 1 and 3) oscillate between 2 vales. One is the correct value and the other is incorrect, butalwaysthe same.ForvoltageIt'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 writingI increased the sample cycles to 144 to be sure is not reading instabilityI 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 changedEnd Of Conversion SelectiontoEOC flag at the end of all conversionsin 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!

author: DavidAlfa
author_rank: Super Contributor
timestamp: November 26, 2024, 09:17:08 pm
content: ADC registers are 16-bit so DMA should be set to half word, and declare your buffer as uint16_t.Try that first.

author: BorisRap
author_rank: Contributor
timestamp: November 27, 2024, 06:56:59 am
content: Quote from: DavidAlfa on November 26, 2024, 09:17:08 pmThank you, I already tried, I declared the buffer as uint16_t and set the DMA to half word, the functionHAL_ADC_Start_DMAexpects 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.

author: BorisRap
author_rank: Contributor
timestamp: November 27, 2024, 02:58:37 pm
content: 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...

author: DavidAlfa
author_rank: Super Contributor
timestamp: November 27, 2024, 03:03:04 pm
content: Yes afaik you have to cast it like that.