Electronics - STM32 - GPIO overview with the STM32F3-Discovery board

GPIO is one of the most easy peripheral to understand in a microcontroller.

But of course it demands a bit of practice to handle it with ease.

That's the purpose of this STM32F303-Discovery board tutorial.

We are going to blink 4 LEDS in order to get how the basics work.

GitHub

Retrieve this example with full code on my GitHub:

https://github.com/badprog/badprog-stm32f303vct6-blinking-led

Registers

In the STM32F303VCT6, there are of course some GPIOs.

These GPIOs are made of several different registers.

Configuration

  • GPIOx->MODER (mode register)
  • GPIOx->OTYPER (output type register)
  • GPIOx->OSPEEDR (output speed register)
  • GPIOx->PUPDR (pull-up/pull-down register)

Data

  • GPIOx->IDR (input data register)
  • GPIOx->ODR (output data register)

Set/Reset

  • GPIOx->BSRR (bit set/reset register)

Locking

  • GPIOx->LCKR (lock register)

Alternate function selection

  • GPIOx->AFRL (alternate function register low)
  • GPIOx->AFRH (alternate function register high)

Reset

  • GPIOx->BRR (bit reset register)

GPIO_InitTypedef

In order to use the corresponding configuration, we can use the STM32 libraries to set the GPIO_InitTypeDef structure.

This structure is made of 5 elements:

  • Alternate
  • Mode
  • Pin
  • Pull
  • Speed

To set it, it's necessary to pass the adress of this structure to the following function:

  • void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

There are 2 ways of using MCU, libraries and modification by hand.

Let's get coded

To have a LED switched on, there are essentiel steps.

Settings oscillator and clocks

This is done with the following function:

  • void SystemClock_Config(void);

Settings the correspondings port(s) and pins

Of course, in this step, we have to enable the clock of each port we want to use in the program.

There is a function for that:

  • __HAL_RCC_GPIOE_CLK_ENABLE()

To be simple, this function calls:

  • SET_BIT(RCC->AHBENR, RCC_AHBENR_GPIOEEN);

This enable de RCC->AHBENR register with the value RCC_AHBENR_GPIOEEN (0x00200000).

All that to change the bit 21 of RCC->AHBENR from 0 to 1.

Handling the correponding pins

For example, each LED has its own pin.

Onto this board we have:

  • LED_4  = GPIO_E_PIN_8   (top left   - Blue)
  • LED_3  = GPIO_E_PIN_9   (top        - Red)
  • LED_5  = GPIO_E_PIN_10  (top right  - Orange)
  • LED_7  = GPIO_E_PIN_11  (right      - Green)
  • LED_9  = GPIO_E_PIN_12  (bottom right  - Blue)
  • LED_10 = GPIO_E_PIN_13  (bottom  - Red)
  • LED_8  = GPIO_E_PIN_14  (bottom left  - Orange)
  • LED_6  = GPIO_E_PIN_15  (left  - Green)
What is not really obvious neither logic, but anyway it works,
 
So you have to choose which pin you want to handle and initialize them by writing the GPIO_PIN_X number.
 

Alternate function

Each pin can be used by many peripherals.

For example, on the pin PE15 (Port E and pin 15), there are:

  • ADC1_EXTI15
  • ADC2_EXTI15
  • ADC4_IN2
  • TIM1_BKIN
  • USART3_RX
  • GPIO_Input
  • GPIO_Output
  • GPIO_Analog
  • GPIO_EXT15

So to use the PE15 as a GPIO_Output to blink a LED.

If we wanted to use the ADC4 on this pin, we would have set this PE15 as alternate function through the GPIOx_MODER register.

Indeed, the GPIOx_MODER register is used to configure the I/O mode, like:

  • Input
  • Output
  • Alternate Function (AF)
  • Analog

So for ADC, it'll be the Analog mode.

Indeed, each I/O pin has a multiplexer that allows only one peripheral alternate function to be used at a time.

It means that once a pin has been set, if you want to use it as another peripheral, it's necessary to set it again with the corresponding alternate function.

The GPIOx_AFRL and GPIOx_AFRH are also the famous registers to select the appropriate peripherals and have to be modified consequently.

Using the GPIO inside your own code

Once every pins are set, it's time to use them inside your own program.
So far we set the microcontroller to tell electronics, eh guys, please open those gates like that and I will use those gates to do my own way.
 
It means that for the moment we prepare the microcontroller to be used like we wanted.
 
It's time to program it.
 
So let's blink the LED4, LED3, LED5 and LED7.
We are going to do that by setting the corresponding GPIOx_ODR register bits.
  • GPIOE->ODR ^= MY_PINS;

The final code

#include "stm32f3xx_hal.h"

void SystemClock_Config(void);

/**
 * LED_4  = GPIO_E_PIN_8   (top left   - Blue)
 * LED_3  = GPIO_E_PIN_9   (top        - Red)
 * LED_5  = GPIO_E_PIN_10  (top right  - Orange)
 * LED_7  = GPIO_E_PIN_11  (right      - Green)
 *
 * LED_9  = GPIO_E_PIN_12  (bottom right  - Blue)
 * LED_10 = GPIO_E_PIN_13  (bottom  - Red)
 * LED_8  = GPIO_E_PIN_14  (bottom left  - Orange)
 * LED_6  = GPIO_E_PIN_15  (left  - Green)
 *
 */
#define MY_PINS GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11

/**
 * GPIO
 *
 * The Alternate element allows to set the GPIO as an alternate function,
 * for example GPIO_AF10_TIM17 or GPIO_AF4_I2C1 to set this GPIO as a timer pin
 * or an I2C pin.
 * In this configuration, the mode has to be set as GPIO_MODE_AF_xx.
 * But in this example, we won't change anything, because we just want to blink
 * some LEDs.
 */
void badprog_gpio() {

  GPIO_InitTypeDef GPIO_InitStruct;

  __HAL_RCC_GPIOE_CLK_ENABLE()
  ;
  GPIO_InitStruct.Alternate = 0; //
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pin = MY_PINS;              // set all pins needed
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

}

/**
 * Main, the famous one!
 */
int main(void) {
  HAL_Init();
  SystemClock_Config();
  badprog_gpio();
  while (1) {
    GPIOE->ODR ^= MY_PINS;    // let's blink
    HAL_Delay(1000);
  }
}

/** System Clock Configuration
 */
void SystemClock_Config(void) {

  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = 16;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12;
  HAL_RCC_OscConfig(&RCC_OscInitStruct);

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
      | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1);

  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000);

  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

Conclusion

You are now able to understand a bit more than earlier about GPIO and especially with the STM32F3 microcontroller.

Retrieve exhaustive explanation in the reference manual RM0316 from page 228 to 247:

http://www.keil.com/dd/docs/datashts/st/stm32f3xx/dm00043574.pdf

Good job, you know more now! cool

 

 

Comments

Comment: 

Thank you very much! this is the best explanation i've ever seen!

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.