STM32F4 GPIO tutorial

This tutorial shows how to configure and use the GPIO pins of the STM32F407. The example switches around the LEDs by setting and resetting I/O pins according to the number of button presses.

Again the comments should explain the procedure pretty well. If the code view is to bad on this site, please hop over to GitHub, where you can have a better look at the code.

#include <stm32f4xx.h>
#include <stm32f4xx_gpio.h>

void Delay(__IO uint32_t nCount) {
  while(nCount--) {

/* This funcion shows how to initialize
 * the GPIO pins on GPIOD and how to configure
 * them as inputs and outputs
void init_GPIO(void){

	/* This TypeDef is a structure defined in the
	 * ST's library and it contains all the properties
	 * the corresponding peripheral has, such as output mode,
	 * pullup / pulldown resistors etc.
	 * These structures are defined for every peripheral so
	 * every peripheral has it's own TypeDef. The good news is
	 * they always work the same so once you've got a hang
	 * of it you can initialize any peripheral.
	 * The properties of the periperals can be found in the corresponding
	 * header file e.g. stm32f4xx_gpio.h and the source file stm32f4xx_gpio.c
	GPIO_InitTypeDef GPIO_InitStruct;

	/* This enables the peripheral clock to the GPIOD IO module
	 * Every peripheral's clock has to be enabled
	 * The STM32F4 Discovery's User Manual and the STM32F407VGT6's
	 * datasheet contain the information which peripheral clock has to be used.
	 * It is also mentioned at the beginning of the peripheral library's
	 * source file, e.g. stm32f4xx_gpio.c
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

	/* In this block of instructions all the properties
	 * of the peripheral, the GPIO port in this case,
	 * are filled with actual information and then
	 * given to the Init function which takes care of
	 * the low level stuff (setting the correct bits in the
	 * peripheral's control register)
	 * The LEDs on the STM324F Discovery are connected to the
	 * pins PD12 thru PD15
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15 | GPIO_Pin_14 | GPIO_Pin_13 | GPIO_Pin_12; // we want to configure all LED GPIO pins
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; 		// we want the pins to be an output
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; 	// this sets the GPIO modules clock speed
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; 	// this sets the pin type to push / pull (as opposed to open drain)
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; 	// this sets the pullup / pulldown resistors to be inactive
	GPIO_Init(GPIOD, &GPIO_InitStruct); 			// this finally passes all the values to the GPIO_Init function which takes care of setting the corresponding bits.

	/* This enables the peripheral clock to
	 * the GPIOA IO module
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

	/* Here the GPIOA module is initialized.
	 * We want to use PA0 as an input because
	 * the USER button on the board is connected
	 * between this pin and VCC.
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;		  // we want to configure PA0
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; 	  // we want it to be an input
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//this sets the GPIO modules clock speed
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;   // this sets the pin type to push / pull (as opposed to open drain)
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN;   // this enables the pulldown resistor --> we want to detect a high level
	GPIO_Init(GPIOA, &GPIO_InitStruct);			  // this passes the configuration to the Init function which takes care of the low level stuff

int main(void){

  // initialize the GPIO pins we need

  /* This flashed the LEDs on the board once
   * Two registers are used to set the pins (pin level is VCC)
   * or to reset the pins (pin level is GND)
   * BSRR stands for bit set/reset register
   * it is seperated into a high and a low word (each of 16 bit size)
   * A logical 1 in BSRRL will set the pin and a logical 1 in BSRRH will
   * reset the pin. A logical 0 in either register has no effect
  GPIOD->BSRRL = 0xF000; // set PD12 thru PD15
  Delay(1000000L);		 // wait a short period of time
  GPIOD->BSRRH = 0xF000; // reset PD12 thru PD15

  // this counter is used to count the number of button presses
  uint8_t i = 0;

  while (1){

		/* Every GPIO port has an input and
		 * output data register, ODR and IDR
		 * respectively, which hold the status of the pin
		 * Here the IDR of GPIOA is checked whether bit 0 is
		 * set or not. If it's set the button is pressed
		if(GPIOA->IDR & 0x0001){
			// if the number of button presses is greater than 4, reset the counter (we start counting from 0!)
			if(i > 3){
				i = 0;
			else{ // if it's smaller than 4, switch the LEDs


					case 0:
						GPIOD->BSRRL = 0x1000; // this sets LED1 (green)
						GPIOD->BSRRH = 0x8000; // this resets LED4 (blue)

					case 1:
						GPIOD->BSRRL = 0x2000; // this sets LED2 (orange)
						GPIOD->BSRRH = 0x1000; // this resets LED1

					case 2:
						GPIOD->BSRRL = 0x4000; // this sets LED3 (red)
						GPIOD->BSRRH = 0x2000; // this resets LED2

					case 3:
						GPIOD->BSRRL = 0x8000; // this sets LED4
						GPIOD->BSRRH = 0x4000; // this resets LED3

				i++; // increase the counter every time the switch is pressed
			Delay(3000000L); // add a small delay to debounce the switch

40 thoughts on “STM32F4 GPIO tutorial

  1. thanks alot for this code, it is wonderful and helpful.
    i want to ask about the “Delay function”, like: Delay(3000000L);
    3000000L : how much does is it ?and which unit? us or msec
    and do we have any other forms for the delay function

    1. It basically is 3000000 instruction cycles, because comparing the value with 0 takes 1 cycle, decrementing the variable takes another. I am not entirely sure because I’m not familiar with the internal instruction set and workings but that’s pretty much how it would work for an 8 bit micro.
      So for short, I don’t know :)

      1. Correction to my comment re: time delay…I have tested my clock speed using led blinking and oscilloscope and I’m actually running at about 83MHz, around half of the max 168MHz. Thus, one clock tick was (1/83) 10^(-6) seconds, and on my device that x your delay of 3e6 gives you seconds of delay. Actually, I extended it more.
        In fact, TIM2 also same frequency without prescale(well, set by me for 0 which defaults to 1 according to Ref Manual).

    2. A late correction: I discovered that in fact my system clock is running at 11.4Mz….I tested this by using the Delay function to toggle bits(no interrupt) and monitored on oscilloscope.
      Research shows that in fact after reset(or startup I guess) the F4 chip is set to select the HSI clock which has a maximum of 16Mhz.
      An Excel based configuration tool is available from STM, but does not work under Excel 2010 no matter what I do to allow Macros to run, including “Trust Center” modifications.

  2. In my brief experience with AVR timers, I would guess that since every count takes a clock cycle, that, at 50MHz one count would then take the inverse of that frequency(period), so multiplying by 1000000 would give you how many seconds delay. Thus: .02 sec or 20msec.
    Someone please correct me if I’m wrong.

    1. Well the pull up or pull down resistors pull the GPIO pin high or low (respectively) when not driven by the GPIO driver circuitry.
      I have disabled the pull up / down resistors because in this case it doesn’t really matter because we are using the GPIO pins connected to the LEDs as outputs.

      The internal pull up / down resistors are intended for use when the GPIO pin is set to be an input. In that case you might only want to detect a low level on a button for example. You will use the internal pull up resistor in order to set the input pin to a defined level.

      1. Elia,
        Can you please send me the code for STM32f407VG for ADC simpler application in Continuous mode without using DMA and without Overrun error for small ADC frequencies…
        I severely stuck here because I am a fresh user of STM32F407VG..
        Hope you will help me soon

    1. That is more of a random setting ;)
      For this simple task any I/O speed would suffice.
      The STM32F4 allows us to choose from 2, 25, 50 and 100 MHz GPIO speed.

  3. Hey man
    i have trouble with
    “cannot open source input file “stm32f4xx_gpio.h””
    eventhough i include file in the running system
    Im new to this program
    can you explain how to avoid that error?

  4. NIce tutorial! I was wondering if the GPIO pins could be connected to an amplifier like a LM386. I am trying to amplify an audio signal but instead of sending the amplified signal out to a speaker I want to send it back to the F4 board, go through an ADC, and be able to transfer the data. I know it sounds pointless but is it possible?

  5. This is great work! I have done mostly arduino in the past and have been really wanting to get into ARM but there seems to be not a lot of resources on that basics such as toggling GPIO pins. I really appreciate you taking the time to do this, it helped me break through that initial mental block and get started.

  6. I am currently using Keil uVision 4 Lite Edition. How do I use the tutorial code with inc and src files in uVision? I am confused on how to structure it out and get it to build and load.

    1. Yep I had problems with that as well. It’s a tricky one to figure out as there are alot of options you have to set exactly right to get it to work (mostly because Keil is a general IDE for ARM core based controllers by all manufacturers or at least tries to be).

      The easiest way to set up a workspace is downloading the STM32F4 Discovery Firmware package from the STM32F4 Discovery product page, extracting it and opening the Master Workspace project with Keil (located in the Mater Workspace folder).


      Update: Here is a dedicated page on that

  7. Can I use two different pins on the same port as Output and AF??

    Do I have to write a separate initialisation block each time???

    1. Yes, you can use other pins on the same port as output.
      You can configure each pin individually to be either AF or output.


  8. Really your blog has been wonderful….Good luck to you…Evn though i just got a BEng in electronics, i couldn’t figure some stuff in the discovery board and read your blog to get through…. You should be really proud of yourself….. Good luck for your future endeavours!!!! …..Lahiru from Sri Lanka

  9. This was extremely helpful to me getting started. To make it easier to prototype new code I put together a small library that handles the pins much like the arduino platform. It also allows you to set up serial ports quickly. The library still has a ways to go to get the serial ports fully functional but the basic uses are there. I posted the library and a quick guide on my site. anyone is free to use it and add to it if they wish

  10. Hi Elia, I really appreciate your work here, it helps a lot. I’ve got a question, maybe a weird one, but I really need your help.
    I use Keil to compile codes, and I got problems with the header files, it keeps alert errors about “source input file “stm32f4xx.h” not found” or “stm32f4xx_conf.h” although I added them all to the project through “add files”.
    I tried to find some solutions on the net, but maybe no one had the same problems.
    Hope you can spend some minutes on it, thank you very much for your help

  11. Thanks, spent a day on all this and finally managed to get a few blinky lights, but I cant say I understand any of what’s happening yet though.

    I’ve been using C for 30 years and the headers and descriptions from these “proper” libraries are a horrible mess IMHO, shouldn’t abstraction make things easier not harder?

    1. Yes, many people have complained about the barely existent documentation by ST. It really is a shame that they start off very good by giving away free dev boards and then offer really poor documentation on their product and the firmware.

  12. Hi Elia,

    I come to your experience, I need to know about the max voltage for a STM42F407 device for ADC input (PC0). The tutorial I saw was using max voltage 5v, and at the same time the Datasheet, makes a note (4) Note page 57/185
    4. FT = 5 V tolerant except when in analog mode or oscillator mode (for PC14, PC15, PH0 and PH1).
    and in this situation we are in analog mode.

    Thanks for your help whatever is your answer

    1. Referring to the ADC section in the reference manual (RM0090), it states that the ADC input range is equal to:
      Vref- <= Vin <= Vref+
      meaning that the input voltage on these pins can lie anywhere between the positive and negative ADC reference voltage.
      According to Table 65 in RM0090 the minimum voltage on Vref- = Vss and the voltage range of Vref+ is 1.8V <= Vref+ <= VDD.


  13. Hi Elia,

    I’m very new to this stm32f4Discovery , and i’d really want to learn how to configure things on this thing.

    as of the moment I followed your tutorial here:

    i’ve already done what your guide said, and i also downloaded the STM32F4-Discovery_FW_V1.1.0 from the website of ST. could you help me how to upload a test program to my Discovery board? I was confused about where to put the
    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */

    also, do i need a makefile inside my STM32F4-Discovery_FW_V1.1.0 folder in order to upload it to my board? and how do i do just that? I was confused about the main.elf thing :(

    sorry about all these questions. I'm just new to this and i really really want to learn.

    I really hope you can enlighten me with these newbie dilemma of mine Elia.
    Thank you for being such a blessing to newbies, and for all these knowledge you are sharing sir! :)

    1. Yes, you will need a valid Makefile in order to be able to compile your sourcecode as I have shown on that page.

      The easiest way of setting up a working directory is by cloning my STM32F4 working directory from GitHub: STM32F4-workarea.

      As to the line:
      SCB->CPACR |= ((3UL < < 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
      This should be put at the beginning of your main function in your source file.

      I just have noticed that that's a little bit unclear from my article, I'll add more details so it's easier to understand.
      After you've cloned my working directory from the above address you can find a USART example in STM32F4-workarea/Projects/USART-example.

      I hope this makes it a little easier for you ;)

      1. Hey Elia!

        Thanks for replying very quickly! \m/,

        I’ve already downloaded your file, and placed the
        SCB->CPACR |= ((3UL < < 10*2)|(3UL <‘ token
        make: *** [main.elf] Error 1

        I am terribly doing something wrong :(

  14. sorry for the double post. kindly delete the previous one.

    it seems that my post was truncated somehow.

    here’s the error when i tried to make the USART example from the folder

    main.c:1:4: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘->’ token
    make: *** [main.elf] Error 1

  15. I may have something set up incorrectly. I’m using coIDE but for this code to work for me I have to add:


    Other than that the tutorial is great. I’m using it to get a jumpstart learning the basics with the Discovery board.

  16. I want to know that the header files that you included in this particular example from where should I get these header files. Will this code run on micro controller if I will try using KEIL Version 4.?

    1. The header files are included from the Makefile and are come with the STM32F4 Firmware library provided by ST.

      Have a look at my STM32F4-workarea or download the STM32F4-Discovery firmware package from ST.


Leave a Reply

Your email address will not be published. Required fields are marked *

1 × = six