cancel
Showing results for 
Search instead for 
Did you mean: 

GPIO Latency STM32F411 Blackpill

vbesson
Senior

Hello All, 

I am doing this post after trying everything I can to solve my Issue. 

I have a STM32F411 Blackpill connected on an Apple II Bus Slot.

The Apple II has a clock (Phi0) close to 1 Mhz. Phi0 is connected to Pin A2 where I have setup a Falling & Rising Edge interrupt. 

The GPIO B12 is used to check the latency between each Phi0 phase,

- at the falling edge of Phi0 B13 is clear

- at the rising edge of Phi0 B13 is set, 

I have nothing else running on this STM32, I am observing a delay (avg 335 ns) between the interrupt detection and GPIO set or clear.

Using the logic analyzer: 

Screen Shot 2025-05-08 at 20.56.06.png

Debug2 is the GPIO B13

I use the following code :

#define GPIO_SET_PIN(port, pin)       ((port)->BSRR = (pin))
#define GPIO_CLEAR_PIN(port, pin)     ((port)->BSRR = (pin << 16u))

#pragma GCC push_options
#pragma GCC optimize ("-Ofast")

void EXTI2_IRQHandler(void){
    __HAL_GPIO_EXTI_CLEAR_IT(PHI0_Pin);
    if ((PHI0_GPIO_Port->IDR & PHI0_Pin)==0){
      GPIO_CLEAR_PIN(DEBUG1_GPIO_Port,DEBUG1_Pin);    
    }else{
      GPIO_SET_PIN(DEBUG1_GPIO_Port,DEBUG1_Pin); 
    }
}
#pragma GCC pop_options

 The clock setting :

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 384;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
  RCC_OscInitStruct.PLL.PLLQ = 8;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses 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_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
  {
    Error_Handler();
  }
}

 The GPIO Settings:

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, D_CE_Pin|D_DIR_Pin|IRQ_DRV_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, DEBUG1_Pin|DEBUG2_Pin|A_CE1_Pin|A_CE2_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : PC13 */
  GPIO_InitStruct.Pin = GPIO_PIN_13;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pins : RW_Pin DEVSEL_Pin */
  GPIO_InitStruct.Pin = RW_Pin|DEVSEL_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pin : PHI0_Pin */
  GPIO_InitStruct.Pin = PHI0_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(PHI0_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pins : D_CE_Pin D_DIR_Pin IRQ_DRV_Pin */
  GPIO_InitStruct.Pin = D_CE_Pin|D_DIR_Pin|IRQ_DRV_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : SD_EJECT_Pin RESET_Pin */
  GPIO_InitStruct.Pin = SD_EJECT_Pin|RESET_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : DA0_Pin DA1_Pin DA2_Pin DA3_Pin
                           DA4_Pin DA5_Pin DA6_Pin DA7_Pin */
  GPIO_InitStruct.Pin = DA0_Pin|DA1_Pin|DA2_Pin|DA3_Pin
                          |DA4_Pin|DA5_Pin|DA6_Pin|DA7_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pins : DEBUG1_Pin DEBUG2_Pin */
  GPIO_InitStruct.Pin = DEBUG1_Pin|DEBUG2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pins : A_CE1_Pin A_CE2_Pin */
  GPIO_InitStruct.Pin = A_CE1_Pin|A_CE2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI2_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI2_IRQn);

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

Why do I have a huge latency between B13 & PHI0 ?

What can I change / do to reduce that gap ? 

Thank 

Vincent

 

2 REPLIES 2
TDK
Guru

335ns at 100 MHz is 33 ticks. That's about as good as you're going to get with this approach. Might be able to get it down to low 20s.

1) There's a 12-cycle delay for the ISR to fire on the M4 core.

1a) I suspect there are a few clocks of delay between the actual edge and the interrupt becoming pending.

2) There are additional flash wait states if the ISR instruction is not in memory.

3) You have a few instructions before actually setting the GPIO value. These use the same bus to communicate as setting the GPIO. Look at disassembly to see what it's doing.

4) The write to GPIO is not instant and will take a few cycles as well.

 

You can eliminate (2) by putting instructions in RAM. And you can affect (3), but only slightly.

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

Hello TDK, 

Thanks, Do you have an example for 2) 

I will try to see the impact 

Vincent