cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7ZI2 NUCLEO - ADC DMA Multi channels are so slow - 10us at 75Mhz ADC_CLK || 400MHz Main Clock

DPham.2
Associate II

Sorry for my bad English. I am using NUCLEO STM32H743ZI2 with ADC multi-channel. I am using 3ADC: ADC 1, 2, and 3 independent mode triggered by TIM1CH1

The CPU clock is 400MHz using an external HSE

STM32H7-> The TIM PWM works well, and it can trigger the ADC measurement. ADC can read the value properly, However, the ADC sampling and conversion took a lot of time than expected with around 10us !!! - too long a time. (I selected 1.5cycle)

Compared to TI TMS320F 28379D - with around 1us, the STM32H7 is much slower :(

COULD YOU PLEASE TELL ME WHAT IS MY ISSUE related to the ADC sampling and converting time ??? . I have tried to read many threads on the internet but no hope, disable ADC2 and ADC3 do not improve the performance :(

I have attached the project so you can import the project and see the delay.

TIM1CH1 (the one trigger ADC) : PE9

GPIO used to check the computation time PG12

https://1drv.ms/u/s!AuUAN7PWAxFS8l3wJ1uhJ_1y7zx8?e=jOM8Gd

0693W00000BdVJLQA3.png0693W00000BdVKOQA3.png0693W00000BdVKEQA3.jpg0693W00000BdVJuQAN.jpg0693W00000BdVJfQAN.png0693W00000BdVJQQA3.png========================CODE =================

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) //

{

 if(hadc->Instance == ADC1)

 {

if(SW_CASE1==50 )

{TIM1->CCR1 = 0; SW_CASE1=0; } // TESTING

  /* Invalidate Data Cache to get the updated content of the SRAM on the second half of the ADC converted data buffer: 32 bytes */

 if (__HAL_ADC_GET_FLAG(hadc, ADC_FLAG_EOS))

 {

 HAL_GPIO_WritePin(GPIOG, GPIO_PIN_12,1); // //GPIO TEST COMPUTATION BURDEN

for (int i =0; i<4; i++) // 4_ADC-> 0,1,2,3

{

adc1_value[i] = ADC_DATA[i];

}

for (int i =0; i<4; i++) // 4_ADC-> 0,1,2,3

{

adc2_value[i] = ADC_DATA_2[i];

}

for (int i =0; i<4; i++) // 4_ADC-> 0,1,2,3

{

adc3_value[i] = ADC_DATA_3[i];

}

HAL_GPIO_WritePin(GPIOG, GPIO_PIN_12,0); // //GPIO TEST COMPUTATION BURDEN

 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8,0); // //GPIO TEST COMPUTATION BURDEN

  } // END __HAL_ADC_GET_FLAG

 }//END hadc->Instance

} // END HAL_ADC_ConvCpltCallback

void HAL_TIM_PeriodElapsedCallback ( TIM_HandleTypeDef * htim)

{

HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8,1); // //GPIO TEST COMPUTATION BURDEN

}

int main(void)

{

 /* USER CODE BEGIN 1 */

 /* USER CODE END 1 */

 /* MCU Configuration--------------------------------------------------------*/

 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */

 HAL_Init();

 /* USER CODE BEGIN Init */

 /* USER CODE END Init */

 /* Configure the system clock */

 SystemClock_Config();

/* Configure the peripherals common clocks */

 PeriphCommonClock_Config();

 /* USER CODE BEGIN SysInit */

 /* USER CODE END SysInit */

 /* Initialize all configured peripherals */

 MX_GPIO_Init();

 MX_DMA_Init();

 MX_ADC1_Init();

 MX_ADC2_Init();

 MX_ADC3_Init();

 MX_SPI1_Init();

 MX_SPI3_Init();

 MX_SPI4_Init();

 MX_TIM1_Init();

 MX_TIM2_Init();

 MX_TIM4_Init();

 MX_TIM8_Init();

 MX_USART1_UART_Init();

 MX_USART2_UART_Init();

 MX_USART3_UART_Init();

 /* USER CODE BEGIN 2 */

 //=============================================================

 //TIM1_CH1

 HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);

 HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1); // turn on complementary channel

 //TIM1_CH2

 HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);

 HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2); // turn on complementary channel

 //TIM1_CH3

 HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);

 HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_3); // turn on complementary channel

//=============================================================

 //TIM8_CH1

  HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_1);

  HAL_TIMEx_PWMN_Start(&htim8, TIM_CHANNEL_1); // turn on complementary channel

  //TIM8_CH2

  HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_2);

  HAL_TIMEx_PWMN_Start(&htim8, TIM_CHANNEL_2); // turn on complementary channel

  //TIM8_CH3

  HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_3);

  HAL_TIMEx_PWMN_Start(&htim8, TIM_CHANNEL_3); // turn on complementary channel

 //initial PWM Setup

TIM1->CCR1 = 32767;

TIM1->CCR2 = 32767;

TIM1->CCR3 = 32767;

TIM8->CCR1 = 32767;

TIM8->CCR2 = 32767;

TIM8->CCR3 = 32767;

// ADC_DMA Startup

// HAL_ADC_Start_DMA(&hadc1, buffer, 4); // handle ADC1 -> Save to BUFFER value

//  TIM1->CCR1= 32767; // for ADC_TRIGGER TIM1-CCR1 NO OUTPUT |OR| TIM1_CCR1 USE AS PWM

HAL_TIM_Base_Start_IT(&htim1) ;

// IMPORTANT -> void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) for the update event

//HAL_TIM_OC_Start_IT(&htim1, TIM_CHANNEL_1); // Activate the TIM peripheral

// IMPORTANT -> void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) for the compare event

// ADC calib

// not corect ?? while(HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED) != HAL_OK);

// ================ ADC_DMA ===============================================

if (HAL_ADC_Start_DMA(&hadc1, (uint32_t *)ADC_DATA, ADC_CONVERTED_DATA_BUFFER_SIZE) != HAL_OK)

{

Error_Handler();

}

if (HAL_ADC_Start_DMA(&hadc2, (uint32_t *)ADC_DATA_2, ADC_CONVERTED_DATA_BUFFER_SIZE) != HAL_OK)

{

Error_Handler();

}

if (HAL_ADC_Start_DMA(&hadc3, (uint32_t *)ADC_DATA_3, ADC_CONVERTED_DATA_BUFFER_SIZE) != HAL_OK)

{

Error_Handler();

}

// ================ ADC_DMA ===============================================

TIM1->ARR = 39999/2;

TIM1->CCR1= 0.5* 39999/2;

 /* USER CODE END 2 */

 /* Infinite loop */

 /* USER CODE BEGIN WHILE */

 while (1)

 {

  /* USER CODE END WHILE */

 while_function();

  /* USER CODE BEGIN 3 */

 }

 /* USER CODE END 3 */

}

14 REPLIES 14
TDK
Guru

Toggling pins in interrupts isn't a good method to determine the speed of the ADC at very fast speeds when you're only converting 4 samples at a time. There is a significant (in this context) amount of overhead introduced with the IRQ callbacks.

If you feel a post has answered your question, please click "Accept as Solution".
Sebastiaan
Senior

You could read the TIM1 count value in the callback to calculate the speed.

Also, what is the output speed of your GPIO? (slow|medium|high|very high)? -> maybe this could cause some delay as well?

AMend.5
Associate III

If you need performance, forget the HAL and use LL library instead. Your problem is the overhead between ADC interrupt and HAL callback.

0693W00000BdcGdQAJ.jpgThanks for your reply, I had set up the GPIO to be VERY HIGH speed mode. Hence the delay caused by GPIO is not much high

Dear TDK,

in my simple GPIO speed test measurement,

HAL_GPIO_WritePin(GPIOG, GPIO_PIN_12,0); // //GPIO TEST COMPUTATION BURDEN

 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8,0); // //GPIO TEST COMPUTATION BURDEN

The delay between 2 command lines is around 33MHz - not a big value.

Even if I Toggling pins or not, the ADC speed is not much high as I expected, assumed that the whole ADC processing is 8us from the picture, it means each ADC Channel needs 2us! I think it is Too much high compared to TI DSP. :(

I am not sure if I config anything wrong or not?

I wish that there would be LL library example about it, I am using STM32CUBE IDE for programming. I tried to search on the internet about it, but there is a small number of examples related to STM32CUBE IDE.

Most example uses IAR or KEIL C -

For me, I did not use KEIL MDK before, but the STM32CUBE IDE is more familiar to me since the UI is similar to TI code composer studio

Which overhead exactly? There isn't that much overhead between interrupt and the callback...

Issue turned out to be the GPIO output speed.

I may not understand the overhead meaning. From my understanding, TImer 1CH1 trigger the ADC1, ADC2, and ADC3 (they works in independent mode). When ever the ADC transfer its data to DMA completely, the call back is activated,

From the experiment, it took around 10us, which is too long - I think

There are many more than 2 lines between the IRQ request and your call to HAL_GPIO_WritePin. Set a breakpoint at that line and look at the call stack when it hits. The IRQ handler calls DMAx_Streamy_IRQHandler, which then calls HAL_DMA_IRQHandler which calls something else which calls something else which calls HAL_ADC_ConvCpltCallback. It also has to process the half transfer complete interrupt which also causes overhead.

HAL IRQ handlers are designed to be flexible at the expense of speed. They are not designed to be called at hundreds of kHz.

If you feel a post has answered your question, please click "Accept as Solution".