GPIO Programming in STM32CubeIDE: Complete Beginner's Tutorial with Examples

GPIO Programming in STM32CubeIDE Complete Beginner's Tutorial with Examples

GPIO (General Purpose Input/Output) is one of the first peripherals every embedded systems engineer learns when working with STM32 microcontrollers. Whether you’re building a simple LED blinking project, reading data from a push button, controlling a relay, or interfacing with sensors, GPIO forms the foundation of almost every embedded application. Understanding how GPIO works is essential before moving on to advanced peripherals like UART, SPI, I2C, ADC, PWM, or CAN communication. Thanks to STM32CubeIDE, configuring GPIO has become much simpler than traditional register-level programming. The integrated STM32CubeMX configuration tool allows developers to graphically configure pins, generate initialization code automatically, and focus on application development instead of spending hours setting hardware registers manually. This significantly reduces development time while making firmware easier to understand and maintain. In this GPIO Programming in STM32CubeIDE tutorial, you’ll learn the complete workflow—from understanding GPIO fundamentals to configuring pins in STM32CubeIDE, selecting different GPIO modes, writing Embedded C code using the STM32 HAL library, and building practical applications like LED blinking and push-button interfacing. By the end of this guide, you’ll have the confidence to use GPIO in your own embedded systems projects.

  • GPIO (General Purpose Input/Output) allows STM32 microcontrollers to interact with external hardware devices.
  • GPIO Programming in STM32CubeIDE becomes much easier with the integrated STM32CubeMX graphical configuration tool.
  • STM32 GPIO pins support multiple modes, including Input, Output, Alternate Function, and Analog.
  • Push-Pull and Open-Drain output configurations are used for different hardware requirements.
  • Internal pull-up and pull-down resistors help prevent floating input pins and improve signal stability.
  • The STM32 HAL library provides easy-to-use functions such as HAL_GPIO_WritePin(), HAL_GPIO_ReadPin(), and HAL_GPIO_TogglePin().
  • Understanding GPIO is the foundation for learning advanced peripherals like UART, SPI, I2C, PWM, ADC, and CAN.
  • Practicing with LED blinking, push-button interfacing, and sensor integration is the best way to build confidence in STM32 embedded programming.

What is GPIO in STM32?

GPIO, or General Purpose Input/Output, refers to programmable digital pins available on

Writing Your First STM32 LED Blinking Program

e microcontroller to interact with external hardware devices by either receiving digital signals (input mode) or sending digital signals (output mode). For example, when you press a push button connected to a GPIO pin, the microcontroller detects the change in voltage level and executes the required program. Similarly, when your program turns on an LED, activates a relay, or drives a buzzer, it sends a digital output through a GPIO pin. Unlike dedicated communication peripherals, GPIO pins are highly flexible. A single GPIO pin can perform multiple functions depending on how it is configured. Besides acting as a normal digital input or output, many STM32 GPIO pins can also be assigned to alternate functions such as UART, SPI, I2C, PWM, ADC, DAC, or timer channels. This versatility makes STM32 microcontrollers suitable for a wide variety of embedded applications.

 

 

registor_now_P

 

 

Why GPIO Programming is Important in Embedded Systems

No embedded system can interact with the physical world without GPIO. Every sensor, actuator, display, switch, or communication module ultimately connects to the microcontroller through one or more GPIO pins. For instance, a smart home automation system uses GPIO pins to read motion sensors, control lights through relays, and communicate with wireless modules. In automotive electronics, GPIO is used to monitor switches, control dashboard indicators, and interact with electronic control units. Industrial automation systems rely on GPIO for controlling motors, reading safety switches, and monitoring machine status. Even advanced communication protocols such as UART, SPI, I2C, and CAN begin with proper GPIO configuration because these peripherals internally use GPIO pins configured in Alternate Function mode. Learning GPIO programming provides a strong foundation for understanding the entire STM32 ecosystem and is the first step toward mastering embedded systems programming.

Understanding GPIO Ports and Pins

STM32 microcontrollers organize GPIO pins into multiple ports. Each port is identified by a letter, while each pin within the port is assigned a number.

For example:

GPIO Port

Available Pins

GPIOA

PA0 – PA15

GPIOB

PB0 – PB15

GPIOC

PC0 – PC15

GPIOD

PD0 – PD15

GPIOE

PE0 – PE15

A typical STM32 development board contains several GPIO ports, allowing developers to interface with dozens of external devices simultaneously. Each GPIO pin is independently configurable. This means PA0 can operate as a digital input while PA5 works as an output, PB6 functions as an I2C clock line, and PC13 detects a push-button press—all at the same time. This flexibility is one of the biggest advantages of STM32 microcontrollers.

GPIO Architecture in STM32

Internally, each GPIO pin consists of several hardware blocks that determine how it behaves. These include an input buffer for reading digital signals, an output driver for controlling external devices, pull-up and pull-down resistors for stabilizing floating inputs, and multiplexers that select alternate peripheral functions. When a GPIO pin is configured through STM32CubeIDE, the software automatically generates initialization code that configures these hardware blocks according to your selected settings. Instead of directly manipulating hardware registers, developers can use high-level HAL APIs, making firmware development significantly easier. Although STM32CubeIDE hides much of the hardware complexity, understanding the internal architecture helps developers troubleshoot configuration issues and optimize application performance.

GPIO Modes in STM32

One of the most important aspects of STM32 GPIO programming is selecting the correct operating mode for each pin. STM32 microcontrollers support four primary GPIO modes.

Input Mode

Input mode allows the microcontroller to receive digital signals from external hardware. This mode is commonly used for push buttons, limit switches, proximity sensors, motion detectors, and digital output sensors.

Whenever the voltage level on the GPIO pin changes between HIGH and LOW, the STM32 reads the corresponding logic state and performs the required action in software.

For example, a push button connected to PC13 can be read continuously inside the main loop to determine whether it has been pressed.

Output Mode

Output mode enables the STM32 to drive external hardware. In this mode, the microcontroller controls the voltage level on the GPIO pin by setting it HIGH or LOW.

Typical output applications include:

  • LED control
  • Relay modules
  • Buzzers
  • MOSFET gate drivers
  • Digital displays
  • Motor driver enable pins

Output mode is one of the most frequently used configurations in embedded systems and forms the basis of projects such as LED blinking, traffic light controllers, and home automation systems.

Alternate Function Mode

Many GPIO pins in STM32 are multiplexed with internal peripherals. Instead of acting as standard digital input/output pins, they can be assigned to communication interfaces or timer peripherals.

Common alternate functions include:

  • UART transmission and reception
  • SPI communication
  • I2C communication
  • CAN communication
  • PWM generation
  • Timer input capture
  • Timer output compare

STM32CubeMX automatically assigns the appropriate alternate function whenever one of these peripherals is enabled.

Analog Mode

Analog mode disconnects the digital circuitry from the GPIO pin, allowing analog peripherals to access the signal directly.

This mode is primarily used for:

  • Analog-to-Digital Converter (ADC)
  • Digital-to-Analog Converter (DAC)

Unused GPIO pins are sometimes configured in analog mode as well because it reduces power consumption in low-power embedded applications.

Push-Pull vs Open-Drain Output

When configuring an output pin in STM32CubeIDE, you’ll encounter two output driver types: Push-Pull and Open-Drain. Push-Pull is the default configuration and is suitable for most digital outputs. It actively drives the pin both HIGH and LOW, making it ideal for LEDs, relays, and general-purpose digital circuits. Open-Drain, on the other hand, can only pull the output LOW. To achieve a HIGH state, an external pull-up resistor is required. This configuration is commonly used in communication protocols such as I2C, where multiple devices share the same communication lines. Choosing the correct output type ensures reliable operation and prevents hardware communication issues.

Understanding Pull-Up and Pull-Down Resistors

One of the most common problems beginners face is dealing with floating input pins. When an input pin is left unconnected, it may randomly fluctuate between HIGH and LOW due to electrical noise. STM32 solves this problem by providing internal pull-up and pull-down resistors. A pull-up resistor keeps the GPIO pin at a stable HIGH level until an external device pulls it LOW. A pull-down resistor keeps the GPIO pin at a stable LOW level until an external device drives it HIGH. For push-button applications, internal pull-up resistors are widely used because they simplify hardware design by eliminating the need for additional external resistors.

Introducing STM32CubeIDE

STM32CubeIDE is STMicroelectronics’ official integrated development environment for STM32 microcontrollers. It combines project creation, code editing, compiling, debugging, and peripheral configuration into a single application. One of its biggest advantages is the integration of STM32CubeMX, which provides a graphical interface for configuring GPIO pins and other peripherals. Instead of manually editing hardware registers, developers simply select pin functions from the graphical pinout view, generate initialization code, and begin writing application logic. The IDE also includes the STM32 HAL (Hardware Abstraction Layer), making GPIO programming much easier through user-friendly APIs such as HAL_GPIO_WritePin(), HAL_GPIO_ReadPin(), and HAL_GPIO_TogglePin().

This modern development workflow reduces programming errors, improves code readability, and accelerates embedded software development for both beginners and professionals.

Creating Your First STM32CubeIDE Project

Now that you understand the fundamentals of GPIO, it’s time to build your first application using STM32CubeIDE. The IDE simplifies project creation by integrating STM32CubeMX, allowing you to configure peripherals graphically and automatically generate initialization code.

Before starting, ensure that you have the following:

  • STM32CubeIDE installed on your computer
  • An STM32 development board (such as STM32F103C8T6 Blue Pill or an STM32 Nucleo board)
  • A USB cable for programming and debugging
  • An onboard LED or an external LED with a current-limiting resistor

Launch STM32CubeIDE and select File → New → STM32 Project. You can either search for your development board or directly choose the STM32 microcontroller used on your hardware.

After selecting the device, enter a project name such as GPIO_Blink, choose the default C language settings, and click Finish. STM32CubeIDE automatically creates the project structure and opens the STM32CubeMX configuration window.

Understanding the STM32CubeIDE Project Structure

When a new project is generated, STM32CubeIDE creates several folders containing startup files, drivers, middleware, and application code.

The most important folders for beginners are:

Folder

Purpose

Core

Contains the main application source files.

Drivers

Includes STM32 HAL drivers and CMSIS libraries.

Startup

Contains startup assembly files required during boot.

Debug

Stores compiled binaries after building the project.

Inside the Core/Src folder, you’ll find the main.c file, where most of your application logic will be written. The main.h file inside the Core/Inc folder contains GPIO pin definitions and project-wide declarations.

For beginners, almost all GPIO programming is performed inside the main.c file.

Configuring GPIO Using STM32CubeMX

One of the biggest advantages of STM32CubeIDE is its graphical GPIO configuration interface.

Instead of manually configuring multiple hardware registers, you simply click on the desired pin and assign its function.

Suppose you want to blink an LED connected to PA5.

Click on PA5 in the pinout view and select GPIO_Output.

If you also want to read a push button connected to PC13, click PC13 and select GPIO_Input.

Next, open the GPIO Configuration window and configure the following settings:

LED Output Pin

Parameter

Value

Mode

Output Push-Pull

Pull-up/Pull-down

No Pull

Speed

Low

User Label

LED

Push Button Input Pin

Parameter

Value

Mode

Input

Pull-up/Pull-down

Pull-Up

User Label

BUTTON

Assigning meaningful user labels makes the code easier to understand, especially in larger embedded projects.

Generating GPIO Initialization Code

After completing the STM32CubeIDE GPIO configuration, click Generate Code.

STM32CubeIDE automatically generates all the necessary initialization functions.

One of the generated functions is MX_GPIO_Init(), which configures every GPIO pin based on the settings selected in STM32CubeMX.

static void MX_GPIO_Init(void)
{

    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __HAL_RCC_GPIOA_CLK_ENABLE();

    __HAL_RCC_GPIOC_CLK_ENABLE();

    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);

    GPIO_InitStruct.Pin = GPIO_PIN_5;

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

    GPIO_InitStruct.Pin = GPIO_PIN_13;

    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;

    GPIO_InitStruct.Pull = GPIO_PULLUP;

    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

}

 

Notice that you don’t have to write this code manually. STM32CubeMX generates it automatically based on your graphical configuration.

Understanding the Generated GPIO Code

Although STM32CubeIDE generates the initialization code, it’s important to understand what each line does.

The first step enables the clock for the GPIO ports.

__HAL_RCC_GPIOA_CLK_ENABLE();

__HAL_RCC_GPIOC_CLK_ENABLE();

 

Every GPIO peripheral requires its clock to be enabled before it can be accessed. Forgetting this step is one of the most common mistakes made by beginners.

Next, the GPIO mode is configured.

GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

 

This configures PA5 as a Push-Pull Output, allowing the pin to actively drive both HIGH and LOW voltage levels.

The following line configures the pull resistor.

GPIO_InitStruct.Pull = GPIO_NOPULL;

Since LEDs do not require pull-up or pull-down resistors, the pull configuration is disabled.

Finally, the output speed is selected.

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

For simple digital outputs such as LEDs, low speed is sufficient and also reduces electromagnetic interference and power consumption.

Writing Your First STM32 LED Blinking Program

The LED blinking application is often called the “Hello World” of embedded systems because it introduces the basic concepts of GPIO output control.

Inside the infinite while loop of the main() function, add the following code:

while (1)

{

    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);

    HAL_Delay(500);

    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);

    HAL_Delay(500);

}

 

When this program runs, the LED connected to PA5 turns ON for 500 milliseconds and then turns OFF for another 500 milliseconds. This sequence repeats continuously, creating a blinking effect.

The HAL_Delay() function introduces a delay in milliseconds, making the LED blink at a visible rate.

Using HAL_GPIO_TogglePin() for Cleaner Code

Instead of writing separate commands to turn the LED ON and OFF, the HAL library provides a convenient toggle function.

while (1)

{

    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);

    HAL_Delay(500);

}

 

The HAL_GPIO_TogglePin() function automatically changes the current state of the GPIO pin. If the LED is ON, it turns OFF, and if it is OFF, it turns ON.

This approach makes the code shorter, cleaner, and easier to maintain.

 

 

Explore Courses - Learn More

 

 

Reading a Push Button Using GPIO

GPIO is not limited to controlling outputs; it is equally important for reading digital inputs.

Suppose a push button is connected to PC13 using an internal pull-up resistor.

The following program turns on the LED whenever the button is pressed.

while (1)
{

    if(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_RESET)

    {

        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);

    }

    else

    {

        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);

    }

}


When the button is pressed, the GPIO pin reads a LOW logic level because of the pull-up configuration. The program detects this condition and turns the LED ON.

When the button is released, the GPIO pin returns to a HIGH logic level, causing the LED to turn OFF.

This simple example demonstrates how STM32 digital input output operations work together in a practical application.

Understanding STM32 HAL GPIO Functions

The STM32 HAL library provides several functions that simplify GPIO programming.

HAL_GPIO_WritePin()

This function is used to set the output state of a GPIO pin.

HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);

It is commonly used to control LEDs, relays, buzzers, and other digital output devices.

HAL_GPIO_ReadPin()

This function reads the current logic level of an input pin.

HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13);

It returns either GPIO_PIN_SET or GPIO_PIN_RESET, depending on the voltage present on the pin.

HAL_GPIO_TogglePin()

This function toggles the current output state.

HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);

It is widely used in LED blinking applications because it eliminates the need for separate ON and OFF commands.

HAL_GPIO_Init()

This function initializes a GPIO pin based on the settings stored in the GPIO_InitTypeDef structure.

Although STM32CubeMX automatically calls this function during initialization, understanding its purpose helps when writing custom drivers or dynamically configuring GPIO pins.

Common Mistakes During GPIO Programming

Many beginners encounter similar issues while working with STM32 GPIO. Being aware of these mistakes can save significant debugging time.

  • Forgetting to enable the GPIO clock before accessing a port.
  • Selecting the wrong GPIO mode in STM32CubeMX.
  • Using an incorrect pull-up or pull-down configuration.
  • Connecting the LED or button to a different pin than the one configured in software.
  • Forgetting to regenerate code after changing GPIO settings in STM32CubeMX.
  • Assuming that all development boards use the same onboard LED pin.

Verifying the board schematic and double-checking the pin configuration usually resolves these problems quickly.

Best Practices for STM32 GPIO Programming

As your projects become more complex, following good programming practices becomes increasingly important.

Use descriptive labels for GPIO pins in STM32CubeMX instead of relying only on pin numbers. Organize GPIO-related code into separate functions to improve readability and maintenance. Choose appropriate GPIO speeds based on the application instead of always selecting the highest setting. Whenever possible, use the HAL library for portability and easier code maintenance.

Finally, always test GPIO functionality with simple applications such as LED blinking before integrating more complex peripherals into your project.

Conclusion

Learning GPIO Programming in STM32CubeIDE is the first step toward becoming proficient in embedded systems development. Whether you’re building a simple LED blinking project or developing a sophisticated IoT or automotive application, GPIO serves as the primary interface between the microcontroller and the external world.

In this tutorial, you explored the fundamentals of GPIO, including input and output modes, push-pull and open-drain configurations, pull-up and pull-down resistors, and the role of GPIO in STM32 microcontrollers. You also learned how to configure GPIO using STM32CubeMX, generate initialization code automatically, and write practical applications using the STM32 HAL library.

As you continue your embedded systems journey, you’ll discover that almost every peripheral—such as UART, SPI, I2C, PWM, ADC, and CAN—depends on proper GPIO configuration. Building a solid understanding of GPIO not only simplifies debugging but also helps you design reliable, efficient, and scalable embedded applications.

The best way to strengthen your skills is through hands-on practice. Experiment with multiple LEDs, push buttons, sensors, and external modules to gain confidence in STM32 GPIO programming. Once you’re comfortable with GPIO, you’ll be well-prepared to explore advanced STM32 peripherals and real-world embedded projects.

 

 

Talk to Academic Advisor

Frequently Asked Questions

GPIO (General Purpose Input/Output) is a programmable digital pin available on STM32 microcontrollers. It enables the microcontroller to communicate with external hardware by reading digital inputs from devices like push buttons and sensors or controlling digital outputs such as LEDs, relays, and buzzers. GPIO is one of the most essential peripherals in STM32 embedded programming.

GPIO configuration in STM32CubeIDE is done using the integrated STM32CubeMX tool. You simply select the desired GPIO pin, assign its mode (Input, Output, Alternate Function, or Analog), configure parameters such as pull-up/pull-down resistors and output speed, and then generate the initialization code automatically. This graphical approach eliminates the need for manual register configuration.

The STM32 HAL library provides several functions for GPIO programming. The most commonly used functions include:

  • HAL_GPIO_WritePin() – Sets a GPIO pin HIGH or LOW.
  • HAL_GPIO_ReadPin() – Reads the current state of an input pin.
  • HAL_GPIO_TogglePin() – Toggles the output state of a GPIO pin.
  • HAL_GPIO_Init() – Initializes a GPIO pin based on the selected configuration.

These APIs simplify firmware development and make GPIO programming easier for beginners and professionals alike.

Author

Embedded Systems and IOT Trainer– IIES

Updated On: 01-08-26


10+ years of hands-on experience delivering practical training in Embedded Systems and it's design