STM32 GPIO OUTPUT Config using REGISTERS
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.
OUTPUT MODE
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.
could somebody please help me with this:
i have done the clock configuration and Timer for delay and also the GPIO configuration: the LED does blink with instead of 1s it takes like 4s , i have done a lot of research to find out where the problem might be , but found nothing. here ist my code :
void Clock_Config(void)
{
RCC->CR |=(RCC_CR_HSEON);
while(!((RCC->CR) & (RCC_CR_HSERDY)));
RCC->APB1ENR |= (1<CR |= 1<ACR |= (1<<8)|(1<<9)|(1<<10)|(2<CFGR|=RCC_CFGR_HPRE_DIV1;
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
RCC->PLLCFGR |= (0<<16)|(4<<0)|(180<CR |=(RCC_CR_PLLON);
while(!((RCC->CR) & (RCC_CR_PLLRDY)));
RCC->CFGR |=(RCC_CFGR_SW_PLL);
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}
void Timer_Config(void)
{
RCC->APB2ENR|=(1<PSC=180-1;
TIM1->ARR=0xffff;
TIM1->CR1|=1<SR)& (1<CNT=0;
while (TIM1->CNT<µs);
}
void Delay_ms (int ms)
{
for (uint16_t counter=0;counterAHB1ENR |=(1<MODER|=(1<<26)|(1<OTYPER&=~(0<<13)|~(1<OSPEEDR |=(2<<26) | (2<PUPDR&=~((1<<26) | (1<<28));
}.
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 = PROBE_Pin;
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 = 0; i < 1000000; i++);
GPIOB->BSRR |= (1<<16); // or GPIOB->ODR = 0x00;
for(int i = 0; i < 1000000; i++);
}
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.