2.1. GPIOs


1.    Introduction

This tutorial is dedicated to the implementation of few BSP functions. In the world of embedded systems, BSP stands for “Board Support Package”. In simple words, it is a collection of functions to address on-board components such as I/Os, displays, sensors, communication interfaces…

A collection of functions that simplifies the software interface with a given peripheral is also called a library, or a driver.

Nucleo boards are rather poor in terms of accessories. Basically, you have a user LED, a user button, and a serial interface through the ST-Link dongle.

LEDs and Buttons are directly interfaced with MCU pins in output or input modes. These are configured by means of GPIO (General Purpose Input Output) peripherals.

Start Eclipse and open my_project project.

  Commit name "Initial project"
  Push onto Gitlab

 

Create a new source file bsp.c under bsp/src folder, and a new header file bsp.h under bsp/inc folder:

image012.png
 

2. LED driver

A look at the board schematics tells us that the green user LED is connected to the pin PA5 of the MCU. Given that LED cathode goes to ground, the LED is "ON" when PA5 is at its high logic voltage.

image013.png

 

Let us take a moment to think about what functions we would like to have in order to play with the LED:

  • A function that turn the LED on
  • A function that turn the LED off
  • A function that toggle the LED state

 

These is no need for any argument (there’s only one LED), and these functions may return nothing. Note that it would be possible to use a single function that sets the LED state using an argument (ON/OFF/Toggle).

We know that a MCU pin needs to be configured to serve as an output. If we include the pin configuration into the previous functions, it will be done every time we want to change the LED state. This is a waste of both CPU time and power consumption as configuration does not change once it is done. Therefore, let us write a separate function to address pin configuration, that will be called only once at the beginning of the main code.

 

Open edit bsp.h and write the following function prototypes:

/*
 * bsp.h
 *
 *  Created on: 5 août 2017
 *      Author: Laurent
 */

#ifndef BSP_INC_BSP_H_
#define BSP_INC_BSP_H_

#include "stm32f0xx.h"

/*
 * LED driver functions
 */

void	BSP_LED_Init	(void);
void	BSP_LED_On	(void);
void	BSP_LED_Off	(void);
void	BSP_LED_Toggle	(void);

#endif /* BSP_INC_BSP_H_ */

 

Note that when creating new headers, Eclipse automatically protects code from recursive inclusion:

#ifndef HEADERNAME_H_
#define HEADERNAME_H_
...
#endif

It is strongly advised to keep it…
Recursive inclusion corresponds to the situation below, that would produce an infinite inclusion loop during the build preprocess if not under the protection of the above directives:

/*
 * header_A.h
 */

#include "header_B.h"

...

 

/*
 * header_B.h
 */

#include "header_A.h"

...

 

Open bsp.c for edit and start the implementation of functions. Adopt a clear strategy in the function and variable naming. That’s VERY important when code growth.

In the following, the function belongs to the BSP, concerns the LED, and its purpose is the initialization of the MCU pin. Let’s name the function BSP_LED_Init():
 

/*
 * bsp.c
 *
 *  Created on: 5 août 2017
 *      Author: Laurent
 */

#include "bsp.h"

/*
 * BSP_LED_Init()
 * Initialize LED pin (PA5) as a High-Speed Push-Pull output
 * Set LED initial state to OFF
 */

void BSP_LED_Init()
{
	// Enable GPIOA clock
	RCC->AHBENR |= RCC_AHBENR_GPIOAEN;

	// Configure PA5 as output
	GPIOA->MODER &= ~GPIO_MODER_MODER5_Msk;
	GPIOA->MODER |= (0x01 <<GPIO_MODER_MODER5_Pos);

	// Configure PA5 as Push-Pull output
	GPIOA->OTYPER &= ~GPIO_OTYPER_OT_5;

	// Configure PA5 as High-Speed Output
	GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_OSPEEDR5_Msk;
	GPIOA->OSPEEDR |= (0x03 <<GPIO_OSPEEDR_OSPEEDR5_Pos);

	// Disable PA5 Pull-up/Pull-down
	GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR5_Msk;

	// Set Initial State OFF
	GPIOA->BSRR |= GPIO_BSRR_BR_5;
}

 

You must refer to the reference manual for a complete description of RCC AHBENR register, and GPIO MODER, OTYPER, OSPEEDR, PUPDR, BSRR registers.

Next comes the three controlling functions:

/*
 * BSP_LED_On()
 * Turn ON LED on PA5
 */

void BSP_LED_On()
{
	GPIOA->BSRR |= GPIO_BSRR_BS_5;
}

/*
 * BSP_LED_Off()
 * Turn OFF LED on PA5
 */

void BSP_LED_Off()
{
	GPIOA->BSRR |= GPIO_BSRR_BR_5;
}

/*
 * BSP_LED_Toggle()
 * Toggle LED on PA5
 */

void BSP_LED_Toggle()
{
	GPIOA->ODR ^= GPIO_ODR_5;
}

That’s it. You’ve written a LED driver. Not too hard?

Test the LED functions in the main code. The example below must be used with the debugger stepping mode, otherwise the state change is too fast to be seen (unless you probe PA5 with an oscilloscope).

/*
 * main.c
 *
 *  Created on: 5 août 2017
 *      Author: Laurent
 */

#include "stm32f0xx.h"
#include "bsp.h"

// Static functions

static void SystemClock_Config(void);

// Main program

void main()
{
	// Configure System Clock
	SystemClock_Config();

	// Initialize LED pin
	BSP_LED_Init();

	// Turn LED On
	BSP_LED_On();

	// Turn LED Off
	BSP_LED_Off();

	while(1)
	{
		// Toggle LED state
		BSP_LED_Toggle();
	}
}

...

 

  Commit name "LED driver"
  Push onto Gitlab

 

3. Push-button driver

Again, we start with the board schematics. The user button is a switch that connects PC13 pin to ground when pushed down. Otherwise, PC13 pin is held at high logic level by means of the pull-up resistor R30.

image020.png

 In order to interface the push-button, we will write two functions:

  • A function to initialize PC13 as an input pin
  • A function that return the state of the button (0 for released, 1 for pushed)

You can add those functions prototype in bsp.h, below LED functions.

/*
 * Push-Button driver functions
 */

void		BSP_PB_Init		(void);
uint8_t	BSP_PB_GetState	(void);

Edit the functions implementation in bsp.c:
 

/*
 * BSP_PB_Init()
 * Initialize Push-Button pin (PC13) as input without Pull-up/Pull-down
 */

void BSP_PB_Init()
{
	// Enable GPIOC clock
	RCC->AHBENR |= RCC_AHBENR_GPIOCEN;

	// Configure PC13 as input
	GPIOC->MODER &= ~GPIO_MODER_MODER13_Msk;
	GPIOC->MODER |= (0x00 <<GPIO_MODER_MODER13_Pos);

	// Disable PC13 Pull-up/Pull-down
	GPIOC->PUPDR &= ~GPIO_PUPDR_PUPDR13_Msk;
}

/*
 * BSP_PB_GetState()
 * Returns the state of the button (0=released, 1=pressed)
 */

uint8_t BSP_PB_GetState()
{
	uint8_t state;

	if ((GPIOC->IDR & GPIO_IDR_13) == GPIO_IDR_13)
	{
		state = 0;
	}
	else
	{
		state = 1;
	}

	return state;
}

 

Then, test your new functions in main.c:

/*
 * main.c
 *
 *  Created on: 5 août 2017
 *      Author: Laurent
 */

#include "stm32f0xx.h"
#include "bsp.h"

// Static functions

static void SystemClock_Config(void);

// Main program

void main()
{
	// Configure System Clock
	SystemClock_Config();

	// Initialize LED pin
	BSP_LED_Init();

	// Initialize User-Button pin
	BSP_PB_Init();

	// Turn LED On
	BSP_LED_On();

	// Turn LED Off
	BSP_LED_Off();

	while(1)
	{
		// Turn LED On if User-Button is pushed down
		if (BSP_PB_GetState() == 1)
		{
			BSP_LED_On();
		}

		// Otherwise turn LED Off
		else
		{
			BSP_LED_Off();
		}
	}
}

The LED is "ON" only when you press the user-button.

 

  Commit name "Push-Button driver"
  Push onto Gitlab

 

4. Summary

In this tutorial, we have written simple drivers for both LED and user-button. Associated functions manipulate GPIOs with both input and output configurations.