cancel
Showing results for 
Search instead for 
Did you mean: 

The Speed of GPIO is much lower than expected.

AAhma.18.16
Associate

I am using stm32f205ret. I want to toggle as fast as possible. According to the data sheet i should be able to do it at 60 MHZ. I use cubeMX to initialize my code. I basically set all clocks to max. I set one pin (gpio port C pin 5) as GPIO_OUTPUT. I configure the speed of the port to be high in cubeMX. And keep toggling it in a while(1) loop.I cannot go beyond 1MHZ.

I would be really nice if someone can tell what I am missing. I have attached the code for clock setup and gpio setting:

static void MX_GPIO_Init(void)

{

 GPIO_InitTypeDef GPIO_InitStruct = {0};

 /* GPIO Ports Clock Enable */

 __HAL_RCC_GPIOC_CLK_ENABLE();

 __HAL_RCC_GPIOA_CLK_ENABLE();

 /*Configure GPIO pin Output Level */

 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_RESET);

 /*Configure GPIO pin : PC5 */

 GPIO_InitStruct.Pin = GPIO_PIN_5;

 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

 GPIO_InitStruct.Pull = GPIO_NOPULL;

 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

 HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

}

void SystemClock_Config(void)

{

 RCC_OscInitTypeDef RCC_OscInitStruct = {0};

 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

 /** Initializes the CPU, AHB and APB busses clocks 

 */

 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;

 RCC_OscInitStruct.HSIState = RCC_HSI_ON;

 RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;

 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;

 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;

 RCC_OscInitStruct.PLL.PLLM = 13;

 RCC_OscInitStruct.PLL.PLLN = 195;

 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;

 RCC_OscInitStruct.PLL.PLLQ = 4;

 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

 {

  Error_Handler();

 }

 /** Initializes the CPU, AHB and APB busses clocks 

 */

 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK

               |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;

 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;

 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;

 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;

 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)

 {

  Error_Handler();

 }

}

1 ACCEPTED SOLUTION

Accepted Solutions
Danish1
Lead II

This comes up every now and again, particularly with those who are used to PICs or other "small" microcontrollers.

There are a few issues.

  1. How do you toggle. Do you first read the port, then calculate the toggled value, then write back to the port? If so, that's vastly more processing steps than is strictly necessary. It's faster to write the bit-clear-value to the port, then write the bit-set-value to the port as two separate steps.
  2. You haven't mentioned optimisation level for your compiler. If you don't optimise, every write to the port starts by reading in the 32-bit address of the port into one register. Then loading up the value to write into another register. Then doing the write. The compiler does this because when debugging, you might change the program flow to start with a different C instruction, so (for unoptimised code) the compiler cannot assume that values in registers are retained from one C instruction to the next.
  3. FLASH memory is a lot slower than the cpu can run. It takes (in your case) 3 additional clock ticks from the cpu asking for a particular program instruction to it actually having the instruction ready to execute. Normally you don't notice this because (provided there are no jumps in the code) the system fetches several instructions in advance. But as soon as you have a jump, including a loop, there's a delay. Executing from RAM avoids this delay.

Most people go to 32-bit microcontrollers for the amazing amount of processing that's possible. Not for fast I/O.

Hope this helps,

Danish

View solution in original post

4 REPLIES 4
Danish1
Lead II

This comes up every now and again, particularly with those who are used to PICs or other "small" microcontrollers.

There are a few issues.

  1. How do you toggle. Do you first read the port, then calculate the toggled value, then write back to the port? If so, that's vastly more processing steps than is strictly necessary. It's faster to write the bit-clear-value to the port, then write the bit-set-value to the port as two separate steps.
  2. You haven't mentioned optimisation level for your compiler. If you don't optimise, every write to the port starts by reading in the 32-bit address of the port into one register. Then loading up the value to write into another register. Then doing the write. The compiler does this because when debugging, you might change the program flow to start with a different C instruction, so (for unoptimised code) the compiler cannot assume that values in registers are retained from one C instruction to the next.
  3. FLASH memory is a lot slower than the cpu can run. It takes (in your case) 3 additional clock ticks from the cpu asking for a particular program instruction to it actually having the instruction ready to execute. Normally you don't notice this because (provided there are no jumps in the code) the system fetches several instructions in advance. But as soon as you have a jump, including a loop, there's a delay. Executing from RAM avoids this delay.

Most people go to 32-bit microcontrollers for the amazing amount of processing that's possible. Not for fast I/O.

Hope this helps,

Danish

In addition to what Danish wrote, for most cases of fast I/O requirements you can use the timers, they are very flexible. In some other cases (like fast parallel I/O) you can use the FMC/FSMC peripherals.

>>I cannot go beyond 1MHZ.

You're loop, which you fail to show, must be very inefficient. Look at the assembler code generated.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Thanks