In the previous Tutorial of this series, we covered how to setup the clock using Registers. Now for the GPIO configuration, I will cover all the 4 configurations i.e INPUT Mode, OUTPUT Mode, ANALOG Mode and Alternate Function Mode.
In this tutorial we will start with the OUTPUT configuration.


Let’s start with the simplest, and the most used one i.e the output mode. I will be using Pin PA5 as the output pin. Following are the steps required to configure the pin as the output Pin

/************** STEPS TO FOLLOW *****************
1. Enable GPIOA clock
2. Set the PIN PA5 as output
3. Configure the output mode i.e state, speed, and pull

1. Enable GPIOA clock

The GPIO clock can be enabled in the RCC_AHB1ENR Register

As you can see above, the 0th bit of RCC_AHB1ENR Register enables the clock for the GPIOA. That’s why we need to write a 1 in the 0th position

RCC->AHB1ENR |= (1<<0);  // Enable the GPIOA clock

2. Set the PIN PA5 as output

To configure the pin as output, we will modify the GPIOx_MODER Register. This register is responsible for configuring different modes for the GPIO and in this case, we will configure it as the output mode

Since I am using pin PA5, I need to modify the pins 10 and 11. This basically works like, if the PIN is ‘y‘, the we need to configure the bits ‘2y’ and ‘2y+1’
Also, in order to set the pin as output, we need to set the bits (11:10) as 0:1. This means that we need to write a ‘1’ in the 10th position

GPIOA->MODER |= (1<<10);  // pin PA5(bits 11:10) as Output (01)

3. Configure the output mode i.e Output type, speed, and pull

The output type can be configured in the GPIOx_OTYPER Register.

In the simplest words, the difference between the Push-Pull and open Drain can be understood as follows
If you choose open drain, then the output pin can either be LOW (Gnd) or it can be floating, while if you choose Push-Pull, the the output pin can be HIGH or LOW

Since I want to blink the LED on pin PA5, i will choose the push-pull type output, and to do so, I need to write a ‘0‘ in the 5th position

GPIOA->OTYPER &= ~(1<<5);  // bit 5=0 --> Output push pull

Next, The output speed can be configured in the GPIOx_OSPEEDR Register

Here you can select different Speeds for the Output Pin, as per your requirement. GPIO speed controls the rate at which a signal can change between low/high values (the “rise time” and “fall time”).
I want the speed not too high and not too low, so I will choose the Fast speed. To do so, I need to write 1:0 to bits (11:10)

GPIOA->OSPEEDR |= (1<<11);  // Pin PA5 (bits 11:10) as Fast Speed (1:0)

Now the final step is to configure the pull-up/pull-down (GPIOx_PUPDR) register.

Here we can Pull-up the pin to Vcc or Pull-down to ground, as per the requirement. Since I am using the pin to blink LED, i will not use the Pull-up or Pull-down. I will set the bits (11:10) as 0

GPIOA->PUPDR &= ~((1<<10) | (1<<11));  // Pin PA5 (bits 11:10) are 0:0 --> no pull up or pulldown

This completes the configuration for the output mode. Now we will see how to set or reset the pin so that we can toggle the LED attached to it

How to Set/Reset the Pin

Using BSRR

To set or reset an individual pin, we need to modify the GPIOx_BSRR Register

BSRR is a 32 bit Register. The lower 16 bits (bit 0 – bit 15) are responsible to set a bit, and the higher 16 bits (bit 16 – bit 31) are responsible to reset a bit

As I have connected the LED to PA5, in order to set it (turn the LED ON), I will write a 1 to bit 5

GPIOA->BSRR |= (1<<5); // Set the Pin PA5

And to reset the pin PA5 (turn the LED OFF), I will write a 1 to the bit 21

GPIOA->BSRR |= (1<<21); // Reset the Pin PA5

Using ODR

Output Data Register (ODR) can also be used to Set/Reset an individual Pin or the entire Port.

In order to Set a pin, we can directly write a ‘1’ to the respective bit, and in order to Reset it, write a ‘0’ in the respective bit

GPIOA->ODR |= 1<<5; // Set the Pin PA5

GPIOA->ODR &= ~(1<<5); // Reset the Pin PA5

To set the entire PORT HIGH, we can write 0xFFF to the ODR

GPIOA->ODR = 0xFFF;  // Set the PORTA HIGH

GPIOA->ODR = 0x00;  // Reset the PORTA

This will toggle all the Pins of PORTA (PA0 – PA15).

The main CODE

void Delay (uint32_t time)
	while (time--);  

int main (void)
	SysClockConfig ();
	GPIOConfig ();
	while (1)
		GPIOA->BSRR |= (1<<5); // Set the Pin PA5
//		GPIOA->ODR = 1<<5;
		Delay (10000000);  // random delay
		GPIOA->BSRR |= (1<<5) <<16;  // Clear the Pin PA5
//		GPIOA->ODR &= ~(1<<5);
		Delay (10000000);  // random delay

The above code will toggle the LED. The delay is just a random delay for now. Once we cover the timer, we can generate precise delay.


You can help with the development by DONATING
To download the code, click DOWNLOAD button and view the Ad. The project will download after the Ad is finished.

7 Comments. Leave new

  • Gordon Charles
    April 5, 2024 9:42 AM

    You missed the entire point of the BSRR register. It’s purpose is to allow users to modify GPO specific to their context in an atomic call. No issues of being interrupted between the read and write and corruption of state. You don’t have to care about the previous state and can simply write the register with the bits you wish to affect, any zero in the bitfield will result in no changes.

  • seems the | is not necessary. check the HAL_GPIO_WritePin(), they don’t use |. also tested with actual device and oscilloscope, without |, it works well
     GPIOA->BSRR = (uint32_t) PROBE_Pin << 16u;

    • I mean….set another PIN A first before the main while(1), then keep calling the BSRR set and reset in main while(1), check PIN A ‘s value, it remains the same.

  • Thanks for this tutorial. It’s really helpful to understand registers. However, I don’t get success applying this example to the STM32H743ZIT6U, board NUCLEO-H743ZI2.
    I’m trying to blink the LED1 (PB0). My code is as follows.

    #include “stm32h7xx.h”

    int main(void)
        RCC->AHB4ENR |= (1<<1);   // enable the clock to GPIOB
        GPIOB->MODER |= (1<<0);   // set pins to be general purpose output
        GPIOB->OTYPER &= ~(1<<0);     // push pull
        GPIOB->OSPEEDR |= (1<<1);
        GPIOB->PUPDR &= ~((1<<0) | (1<<1));
        for (;;) {
            GPIOB->BSRR |= (1<<0); // or GPIOB->ODR = 0xFFF; 
            for(int i = 0i < 1000000i++);
            GPIOB->BSRR |= (1<<16); // or GPIOB->ODR = 0x00;
            for(int i = 0i < 1000000i++);
        return 0;

    Please, could you have a look at it?

    • For the STM32H743, the default value for GPIOx->MODER = 11
      To set the output, firstly we have to clean up these to bits. Add GPIOB->MODER &= ~((1<<0) | (1<<1)); before GPIOB->MODER |= (1<<0);

    • The compiler removes your delay code because it does nothing.
      for(int i = 0; i < 1000000; i++);
      if you something like asm("") inside, it would not be erased.

  • You should not be setting bits in the BSRR using an OR command. The BSRR register is meant to be atomic and therefore use EQUALS to set and reset bits. It should not use Read-Modify-Write command such as OR/AND/XOR etc. If you want to use these then use them on the ODR.


Leave a Reply

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

Fill out this field
Fill out this field
Please enter a valid email address.


Adblocker detected! Please consider reading this notice.

We've detected that you are using AdBlock Plus or some other adblocking software which is preventing the page from fully loading.

We don't have any banner, Flash, animation, obnoxious sound, or popup ad. We do not implement these annoying types of ads!

We need money to operate the site, and almost all of it comes from our online advertising.

Please add to your ad blocking whitelist or disable your adblocking software.