# STM32 F4 max GPIO toggle frequency [duplicate]

I just started using the STM32 F401VC Discovery Evaluation board and I'm trying to get the maximum GPIO toggling frequency without assambler. So my tought process is following:

The code I was planning to use is:

#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx.h"
#include "stm32f4xx_spi.h"

void initializeGreenLed(void){
GPIO_InitTypeDef GPIO_InitStructure;

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD,&GPIO_InitStructure);
}

int main(void)
{
//RCC_CFGR;
initializeGreenLed();
//initialiseSysTick();
GPIO_SetBits(GPIOD,GPIO_Pin_12);
while(1)
{
GPIO_ToggleBits(GPIOD,GPIO_Pin_12);
}
}


The max pin toggle frequency I get is around 230 KHz. Since the MCU can be run at 84 MHz, I have a feeling, that it's possible to increase the max toggle frequency beyond 230 KHz.

So I tought that I need to change/modify the clock source for RCC_AHB1Periph_GPIOD in RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);. For that I used the STM32 F4 reference manual. So my plan was to use PLLCLK as SYSCLK. The PLLCLK would be sourced from 8MHz HSE.

Browsing through YouTube, found a nice example:

#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx.h"
#include "stm32f4xx_flash.h"

void initializeGreenLed(void){
GPIO_InitTypeDef GPIO_InitStructure;

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD,&GPIO_InitStructure);
}

int main() {

//---------------------------down is clc stuff---------------------------

RCC_DeInit();

ErrorStatus Errsts;
RCC_HSEConfig(RCC_HSE_ON);
Errsts = RCC_WaitForHSEStartUp();

if (Errsts == SUCCESS) {
//external clc is ok
RCC_PLLConfig(RCC_PLLSource_HSI, 8, 336, 2, 7);
RCC_PLLCmd(ENABLE);
RCC_GetFlagStatus(RCC_FLAG_PLLRDY == RESET);

FLASH_SetLatency(FLASH_Latency_5);

RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div4);
RCC_PCLK2Config(RCC_HCLK_Div2);

RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE);

initializeGreenLed();
while(1){
GPIO_SetBits(GPIOD,GPIO_Pin_12);
}

}
else {

while(1);
}
}


But it doesn't work, as in it just stays in one state.

So I have two questions:
1. Why isn't my code working?
2. If my abovementioned strategy is incorrect, how to increase the toggle frequency beyond 230 KHz?

## marked as duplicate by Michel Keijzers, Dmitry Grigoryev, Elliot Alderson, Voltage Spike, FinbarrOct 18 '18 at 9:41

• Well you say you want the HSE, but you use the HSI as PLL source in the code. Which one is it? And you want the PLL to be the source of the SYSCLK and use HSE. If you just need a frequency as output, you are much better on your way with using a timer peripheral and an output from there. It's all done in hardware then. – Arsenal Oct 17 '18 at 11:43
• The best way is to do not use the slow HAL libraries. You can toggle much faster. The max from the D'S is not archivable by toggling only when you use the peripheral like timer to generate the OWN signal – P__J__ Oct 18 '18 at 12:52

In your second code, you have the line GPIO_SetBits(GPIOD,GPIO_Pin_12);, do you actually reset the pin at some point ?

It is faster to set the pin through the register rather than using the library function, as there is plenty of overhead.

To Set PC8

GPIOC->ODR |= 0x00000100; //( 0b00000000000000000000000100000000)

To clear pin PC8 independent of all other pins on GPIOC (RMW) you could use:

GPIOC->ODR &= ~(0x00000100); //( 0b00000000000000000000000100000000)

So you can try

while (1)
{
GPIOC->ODR |=  0x00000100;
// maybe some delay needed here
GPIOC->ODR &=  ~(0x00000100);
}


Of course adjusting to the proper pin / register you need.

Also the pin toggle timing is limited by the hardware.

You might toggle the pin so fast in software side, that the hardware might not have time to follow and thus stay stuck in one state.

It also depends what is connected to that pin, long wire, capacitance, impedance and inductance will slow down the toggle timing as it will be limited by the current the IC is able to draw from that pin, and the internal hardware switching latency.

Hardware latency are defined on the datasheet on page 95, for this example it cannot exceed 50-100Mhz depending the conditions.

• You really didn't provide any kind of answer to the question. And why on earth would we need to "make sure" that the pin actually changes? – Elliot Alderson Oct 17 '18 at 11:39
• in uC you have latency on the actual hardware state of the pin and the currently processed software. If you just toggle as fast as you can, it will likely stay in one state. But OP uses GPIO_ToggleBits, and if you go check the library you can be sure this function actually reads the state of the pin. You can test it for yourself @ElliotAlderson – Damien Oct 17 '18 at 11:47
• @Damien I wouldn't throw out stuff you haven't checked for yourself: void GPIO_ToggleBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin){ /* Check the parameters */ assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); GPIOx->ODR ^= GPIO_Pin; } That is the implementation of the ToggleBits function I found and it doesn't check the state of the pin. Is there a different version around? – Arsenal Oct 17 '18 at 12:13
• GPIOx->ODR ^= GPIO_Pin; this is actually reading the pin state and applying the opposite of that state. @Arsenal – Damien Oct 17 '18 at 12:15
• No it is not. It is reading the value of the output data register, which does not contain the current state of the pin, it contains the value you want the pin to be. The IDR register is the one you need to check if you want to see if the voltage of the pin is above or below the threshold voltages. – Arsenal Oct 17 '18 at 12:18