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
  init_GPIO();

  /* 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

				switch(i){

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

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

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

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

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

24 thoughts on “STM32F4 GPIO tutorial

  1. Ayman Ahmed Kamal

    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

    Reply
    1. Elia Post author

      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 :)

      Reply
      1. Charles

        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).

        Reply
    2. CC

      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.

      Reply
  2. Charlie

    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.

    Reply
    1. Elia Post author

      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.

      Reply
    1. Elia Post author

      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.

      Reply
  3. jake eum

    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?

    Reply
  4. Steven G

    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?

    Reply
  5. jason

    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.

    Reply
    1. Elia Post author

      That’s why I put the information up! I have had several problems with this when I started off using the STM32 micros :)

      Reply
  6. Russel

    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.

    Reply
    1. Elia Post author

      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).

      Cheers,
      Elia

      Update: Here is a dedicated page on that

      Reply
  7. Swati

    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???

    Reply
    1. Elia Post author

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

      Cheers,
      Elia

      Reply
  8. Lahiru

    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

    Reply
  9. Jason B

    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 Girshwin.com/pinsandports

    Reply
  10. Pingback: Blink for stm32f4-discovery board on Linux with Makefile | Liviu's blog

Leave a Reply

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


+ 8 = fifteen

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>