General Purpose Input Output (GPIO)

  1. General Information
  2. Pull Up Pull Down Resistor and Electrical Current Limitation
  3. Configuring a GPIO pin
  4. Outline
  5. Writing and Reading Data from a GPIO pin
  6. Full Code Examples
    1. Write
    2. Read

General Information

The General Purpose Input Output (GPIO) module is used for pin configuration, direct pin manipulation, sensing and routing for peripheral pin connection. General Purpose input/output pins minimize the software control overhead and are able to fit many communication protocols, are highly configurable, and therefore easy to use.

Each pin on the extension bus can be individually configured as either input or an output with different modes, such as pull up or pull down for inputs and push pull drive for outputs and so on. Every configuration is controlled by the GPIO module. The I/O pins are organized in ports with up to 16 pins each and are named in the scheme PXN, where X indicates the port (A,B,C,…) and N indicates the pin number (0,1,2…15). For example, PB6 refers to pin 6 on port B. The XDKs MCU (EFM32GG390), for example, has up to 90 general use I/O pins and up to 19 I/O pins on the extension bus, which can be used as general purpose pins without the limitations mentioned in the next chapter.

Pull Up Pull Down Resistor and Electrical Current Limitation

This chapter gives a short overview and explanation on the three possible modes for the pins on the extension bus, and which mode has to be set, when external devices are connected to input or output pins on the extension bus.

In digital circuits, it is important that the signal lines are never allowed to float. A floating (undetermined) state is usually causing issues regarding its interpretation. This mainly occurs if no external devices are connected or due to high- impedance. Therefore the signal has to always be either high or low.

To prevent this undetermined state, input pins can be configured to either operate with a pull up or pull down resistor.

A pull-up resistor weakly pulls the voltage level of the wire to the voltage source level, in case of the XDK to 2.5 V, when the other components on the input pin are inactive.

When no other component on the input pin is active (i.e. changing the pin state) or no component is connected to the input, it is in a state of high impedance. When another component on the input pin is connected or actively changing the pin state, it will override the high logic level set by the pull-up resistor. The pull-up resistor assures that the wire is at a defined logic level even if no active devices are connected to it.

Image

The pull down resistor has a similar effect, but pulls the input signal weakly to the ground level instead. It holds the logic signal near zero volt when no other device is actively changing the pin state or no other device is connected to the input pin.

Image

Furthermore, it depends on the external device whether the logic level should either be pulled high or low by a Pull up/Pull down resistor. Nevertheless a floating state of the input pin should be prevented.

Since this configuration is only preventing failures on the input pin, it is also necessary to ensure that the pin is working properly, when configured as output. For that, the electrical current draining from the pin must be controlled. As mentioned in chapter 1.3, this is ensured by an internal programmable current limitation. Please note that the output current also differs from the combined impedance of the internal series resistor of the respective output pin and the impedance of the connected component. The configuration of the electrical current limitation will be explained in detail in the following chapter.

Configuring a GPIO pin

This section describes how to configure a GPIO pin as input or output. It will also show the configurations such as pull up/pull down or a electrical current limitation. As introduced in chapter 2, we are going to use the modules from the low level Emlib API.

#include "em_gpio.h"

The essential functions that are used in this guide are described in the following table. For a full description, please refer to the corresponding API documentation.

FunctionDescription
GPIO_PinModeSet()This function is called to configure a specific pin in its Input/Output characteristic and to configure its mode (i.e pull-up/current limitation and so on).
GPIO_DriveModeSet()This function is called to configure a specific port register in its current drive strength. This configures all pins of the respective register, when gpioModePushPullDrive is chosen as mode to configure a single pin.
GPIO_PinInGet()This function is called to get the current voltage level on the chosen input pin.
GPIO_PinOutSet()This function is called to set the current output voltage level to high.
GPIO_PinOutClear()This function is called to set the current output voltage level to low.

Outline

The following code shows how a pin can be configured as input with a pull-up resistor.

GPIO_PinModeSet(gpioPortA, 1, gpioModeInputPull, 1);
GPIO_PinOutSet(gpioPortA, 1);

The function GPIO_PinModeSet() is used to set the basic configuration. It sets the port location, called port register. It also sets the I/O mode configuration of the pin itself. This determines whether the pin is used as pull-up / pull-down, including an initial value.

The other function GPIO_PinOutSet() sets the output voltage level of the selected pin to high. If the pin is set to input mode, this will enable the pull-up resistor.

The configuration can also be done with a pull-down resistor. For that, only the second function call needs to be changed. GPIO_PinOutClear() has to be called after setting the Pin Mode, instead of GPIO_PinOutSet(), to pull the output voltage level to low, and as such, enable the pull-down resistor.

The overall effect of Code 2 is the configuration of pin PA1 as an input pin with a pull-up resistor.

To configure a pin as output, this can be done as simply as the configuration as input. The following outline shows this with the additional configuration of the electrical current limitation.

GPIO_DriveModeSet(gpioPortA, gpioDriveModeLowest);
GPIO_PinModeSet(gpioPortA, 1, gpioModePushPullDrive, 1);

Before the selected pin’s mode is set, the current drive mode is configured for the entire port. In the above code example the current drive mode for the complete port register A is set to the lowest current of 0.5 mA a single pin can provide.

Afterwards, the pin number one of the port register A is configured as gpioModePushPullDrive to make use of the previous configuration of the port register.

The following table shows which drive modes correspond to which current strength.

EnumerationCurrent Strength
gpioDriveModeHigh20 mA
gpioDriveModeStandard6 mA
gpioDriveModeLow2 mA
gpioDriveModeLowest0.5 mA

Writing and Reading Data from a GPIO pin

This section describes how to write data to an GPIO pin and how to read data from an GPIO pin, i.e. set and read the current logical state of the pin.

Writing to a GPIO pin is either to set the output, and as such write a logical “1” to the GPIO output or by clearing the output, and as such writing a logical “0” to the GPIO output.

The following code shows how to write a logical one to the GPIO output.

GPIO_PinOutSet(gpioPortA,1);

GPIO_PinOutClear(gpioPortA,1);

The input for both these functions is the port and the pin number of the respective pin. As such, the code snippets 4 and 5 respectively set and clear the output of pin PA1.

If a GPIO pin is configured as input, it can be read instead. Do note, the resulting value can only be a logical “1” or “0”.

uint8_t Gpio_Input = GPIO_PinInGet(gpioPortA,1);

Please note that it is recommended to insert a pull-up or pull-down resistor to ensure that the input is not floating. Reading the pin’s value, when no action from an external device (e.g. a button) is made, the logical read output will always have the logical value applied by the pull-up / pull-down resistor.

Additionally, depending on the pull-up / pull-down configuratiuon, the value produced by GPIO_PinInGet() is interpreted differently. For pull-up mode, the default value of the input is 1. That means, if the device attached to this pin is setting a logical 1, a logical 0 will be read on the pin. Therefore, a logical 0 returned by GPIO_PinInGet() represents the “ON”-state of the signal. On the other hand, if the mode is pull-down, the standard value will be a logical 0, which represents the “OFF”-state.

Full Code Examples

Note: The full code example is intended for XDK-Workbench versions 3.4.0 and higher.

Write

/* --------------------------------------------------------------------------- |
 * INCLUDES & DEFINES ******************************************************** |
 * -------------------------------------------------------------------------- */

/* own header files */
#include "XdkAppInfo.h"
#undef BCDS_MODULE_ID  /* Module ID define before including Basics package*/
#define BCDS_MODULE_ID XDK_APP_MODULE_ID_APP_CONTROLLER

/* own header files */
#include "AppController.h"

/* system header files */
#include <stdio.h>

/* additional interface header files */
#include "BCDS_CmdProcessor.h"
#include "FreeRTOS.h"
#include "task.h"

#include "em_gpio.h"

/* --------------------------------------------------------------------------- |
 * HANDLES ******************************************************************* |
 * -------------------------------------------------------------------------- */

static CmdProcessor_T * AppCmdProcessor;

static xTaskHandle AppControllerHandle = NULL;

/* --------------------------------------------------------------------------- |
 * EXECUTING FUNCTIONS ******************************************************* |
 * -------------------------------------------------------------------------- */

static void AppControllerFire(void* pvParameters)
{
    BCDS_UNUSED(pvParameters);

    bool onLED = false;

    while (1) {

        if(onLED) {
            GPIO_PinOutClear(gpioPortA,1);
            onLED = false;
        }

        else {
            GPIO_PinOutSet(gpioPortA,1);
            onLED = true;
        }
    }
}

/* --------------------------------------------------------------------------- |
 * BOOTING- AND SETUP FUNCTIONS ********************************************** |
 * -------------------------------------------------------------------------- */

static void AppControllerEnable(void * param1, uint32_t param2)
{
    BCDS_UNUSED(param1);
    BCDS_UNUSED(param2);
    Retcode_T retcode = RETCODE_OK;

    GPIO_DriveModeSet(gpioPortA, gpioDriveModeLowest);
    GPIO_PinModeSet(gpioPortA, 1, gpioModePushPullDrive, 1);

    if (RETCODE_OK == retcode)
    {
        if (pdPASS != xTaskCreate(AppControllerFire, (const char * const ) "AppController", TASK_STACK_SIZE_APP_CONTROLLER, NULL, TASK_PRIO_APP_CONTROLLER, &AppControllerHandle))
        {
            retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_OUT_OF_RESOURCES);
        }
    }
    if (RETCODE_OK != retcode)
    {
        printf("AppControllerEnable : Failed \r\n");
        Retcode_RaiseError(retcode);
        assert(0);
    }
}

static void AppControllerSetup(void * param1, uint32_t param2)
{
    BCDS_UNUSED(param1);
    BCDS_UNUSED(param2);
    Retcode_T retcode = RETCODE_OK;

    retcode = CmdProcessor_Enqueue(AppCmdProcessor, AppControllerEnable, NULL, UINT32_C(0));
    if (RETCODE_OK != retcode) {
        printf("AppControllerSetup : Failed \r\n");
        Retcode_RaiseError(retcode);
        assert(0);
    }
}

void AppController_Init(void * cmdProcessorHandle, uint32_t param2)
{
    BCDS_UNUSED(param2);

    Retcode_T retcode = RETCODE_OK;

    if (cmdProcessorHandle == NULL)
    {
        printf("AppController_Init : Command processor handle is NULL \r\n");
        retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_NULL_POINTER);
    }
    else
    {
        AppCmdProcessor = (CmdProcessor_T *) cmdProcessorHandle;
        retcode = CmdProcessor_Enqueue(AppCmdProcessor, AppControllerSetup, NULL, UINT32_C(0));
    }

    if (RETCODE_OK != retcode)
    {
        Retcode_RaiseError(retcode);
        assert(0);
    }
}

Read

/* --------------------------------------------------------------------------- |
 * INCLUDES & DEFINES ******************************************************** |
 * -------------------------------------------------------------------------- */

/* own header files */
#include "XdkAppInfo.h"
#undef BCDS_MODULE_ID  /* Module ID define before including Basics package*/
#define BCDS_MODULE_ID XDK_APP_MODULE_ID_APP_CONTROLLER

/* own header files */
#include "AppController.h"

/* system header files */
#include <stdio.h>

/* additional interface header files */
#include "BCDS_CmdProcessor.h"
#include "FreeRTOS.h"
#include "task.h"

#include "em_gpio.h"

/* --------------------------------------------------------------------------- |
 * HANDLES ******************************************************************* |
 * -------------------------------------------------------------------------- */

static CmdProcessor_T * AppCmdProcessor;

static xTaskHandle AppControllerHandle = NULL;

/* --------------------------------------------------------------------------- |
 * EXECUTING FUNCTIONS ******************************************************* |
 * -------------------------------------------------------------------------- */

static void AppControllerFire(void* pvParameters)
{
    BCDS_UNUSED(pvParameters);

    uint8_t Gpio_Input = 0;

    while (1) {

        Gpio_Input = GPIO_PinInGet(gpioPortA,1);

        printf("Gpio Input %u \n\r",(unsigned int) Gpio_Input);
    }
}

/* --------------------------------------------------------------------------- |
 * BOOTING- AND SETUP FUNCTIONS ********************************************** |
 * -------------------------------------------------------------------------- */

static void AppControllerEnable(void * param1, uint32_t param2)
{
    BCDS_UNUSED(param1);
    BCDS_UNUSED(param2);
    Retcode_T retcode = RETCODE_OK;

    GPIO_PinModeSet(gpioPortA, 1, gpioModeInputPull, 1);
    GPIO_PinOutSet(gpioPortA, 1);

    if (RETCODE_OK == retcode)
    {
        if (pdPASS != xTaskCreate(AppControllerFire, (const char * const ) "AppController", TASK_STACK_SIZE_APP_CONTROLLER, NULL, TASK_PRIO_APP_CONTROLLER, &AppControllerHandle))
        {
            retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_OUT_OF_RESOURCES);
        }
    }
    if (RETCODE_OK != retcode)
    {
        printf("AppControllerEnable : Failed \r\n");
        Retcode_RaiseError(retcode);
        assert(0);
    }
}

static void AppControllerSetup(void * param1, uint32_t param2)
{
    BCDS_UNUSED(param1);
    BCDS_UNUSED(param2);
    Retcode_T retcode = RETCODE_OK;

    retcode = CmdProcessor_Enqueue(AppCmdProcessor, AppControllerEnable, NULL, UINT32_C(0));
    if (RETCODE_OK != retcode)
    {
        printf("AppControllerSetup : Failed \r\n");
        Retcode_RaiseError(retcode);
        assert(0);
    }
}

void AppController_Init(void * cmdProcessorHandle, uint32_t param2)
{
    BCDS_UNUSED(param2);

    Retcode_T retcode = RETCODE_OK;

    if (cmdProcessorHandle == NULL)
    {
        printf("AppController_Init : Command processor handle is NULL \r\n");
        retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_NULL_POINTER);
    }
    else
    {
        AppCmdProcessor = (CmdProcessor_T *) cmdProcessorHandle;
        retcode = CmdProcessor_Enqueue(AppCmdProcessor, AppControllerSetup, NULL, UINT32_C(0));
    }

    if (RETCODE_OK != retcode)
    {
        Retcode_RaiseError(retcode);
        assert(0);
    }
}