EmbeddedRelated.com

GPIO library

Niels Tjørnhøj-Thomsen March 23, 2013 Coded in C for the NXP LPC17xx series

This library provides an efficient and maintainable means of configuring and controlling the GPIO pins on the NXP LPC17xx family of processors.  For other processors in the LPC range, you may need to make small adjustments to the code, but the basic principle remains the same.

The library is particularly useful if you are working on a development board prior to real hardware arriving, so your pin allocation is a bit fluid.  The definition of all GPIO pins for your project is concentrated in one place, so only one change is needed to modify the usage of a pin or to move the signal to another pin.

I usually put all the pin definitions in a file called gpio_pindefs.h which is #included from the gpio.h file as shown below.

The macro PINFUNC_DEF is where it all happens.  Within the gpio.h/.c files it resolves to a const structure definition that encapsulates all the data needed to control the pin.  When included from other modules, it instead resolves to a pointer to the const structure within the gpio.c module.

The library uses the concept of 'active' and 'inactive' to describe the state of a pin, rather than the 0 and 1 state that is often seen.  This is because the signal level is a hardware issue and should be abstracted away in the code.  If the hardware changes, or you realise that an external chip works differently from what you thought it did (happens to me all the time), you can simply change the pin definition with complete confidence that you won't need to change any of the code that uses the pin.

Example of use:

Define a pin GPIO_LED_GREEN using:

PINFUNC_DEF( GPIO_LED_GREEN, 1, 25, L, GPIO )

meaning that the green LED is attached to P1.25 and is active low, then initialise it with:

vGPIODDconfigurePin( GPIO_LED_GREEN, PIN_OUT )

The LED can be lit with:

vGPIODDsetActive( GPIO_LED_GREEN )

and extinguished with:

vGPIODDsetInactive( GPIO_LED_GREEN )

When the hardware engineer then tells you that, actually, the green LED is on port 2.7 and is active high, you only need to change the one line with the PINFUNC_DEF macro and your code stays the same.

This library has stood the test of time and been used in around 15 projects over the last few years.  Necessary variations were easily created, for instance for the LPC2xxx series of processors and for additional functionality such as GPIO pin interrupts.

//---------------------------------------------------------
// start of header file (gpio.h)
//---------------------------------------------------------

#if !defined(_GPIO_H_)
#define _GPIO_H_

typedef unsigned long dword;

typedef struct tGPIOpinTag
{
  dword dwReg;
  dword dwClr;
  dword dwSet;
  int iBit;
  dword dwFunction;
} tGPIOpin;

#define REG32                volatile dword *

//
// bit patterns for alternate pin functions.
//
#define PINFUNC_GPIO         ( 0x00000000 )
#define PINFUNC_ALT1         ( 0x55555555 )
#define PINFUNC_ALT2         ( 0xAAAAAAAA )
#define PINFUNC_ALT3         ( 0xFFFFFFFF )

//
// Active high and low works by swapping the SET and CLR register offsets round
//
#define GPIO_ACTIVE_L        FIO_SET_OFS, FIO_CLR_OFS
#define GPIO_ACTIVE_H        FIO_CLR_OFS, FIO_SET_OFS

//
// Start addresses of GPIO blocks of registers
//
#define LPC_GPIO_BASE        ( 0x2009C000UL )
#define LPC_GPIO0_BASE       ( LPC_GPIO_BASE + 0x00000 )
#define LPC_GPIO1_BASE       ( LPC_GPIO_BASE + 0x00020 )
#define LPC_GPIO2_BASE       ( LPC_GPIO_BASE + 0x00040 )
#define LPC_GPIO3_BASE       ( LPC_GPIO_BASE + 0x00060 )
#define LPC_GPIO4_BASE       ( LPC_GPIO_BASE + 0x00080 )

//
// Offsets to the direction register and PIN register
//
#define FIO_SET_OFS          ( 0x00000018 )
#define FIO_CLR_OFS          ( 0x0000001C )
#define FIO_DIR_OFS          ( 0x00000000 )
#define FIO_PIN_OFS          ( 0x00000014 )
#define FIO_MASK_OFS         ( 0x00000010 )

// Parameters for vGPIODDsetDirection
#define   PIN_IN             ( FALSE )
#define   PIN_OUT            ( TRUE )

//
// Macro for pin definition structure
//
#ifdef _INSIDE_GPIODD_
//
// this version is for use inside the gpio module
//
#define PINFUNC_DEF( name, port, bit, act, func )   \
  const tGPIOpin t##name = {                        \
    LPC_GPIO##port##_BASE,                          \
    GPIO_ACTIVE_##act,                              \
    bit,                                            \
    PINFUNC_##func                                  \
  };                                                \
  const tGPIOpin * const name = &t##name;
#else
//
// and this is how external modules see the pin definition
//
#define PINFUNC_DEF( name, port, bit, act, func )   \
  extern const tGPIOpin * const name;
#endif

//
// include the hardware pin allocations from another file
// (see example below)
//
#include "gpio_pindefs.h"

extern void vGPIODDsetPinFunction( const tGPIOpin * const psPin );
extern void vGPIODDsetPinDirection( const tGPIOpin * const psPin, 
                                    const bool boOutput );
extern void vGPIODDconfigurePin( const tGPIOpin * const psPin, 
                                 const bool boOutput );
extern void vGPIODDsetActive( const tGPIOpin * const psPin );
extern void vGPIODDsetInactive( const tGPIOpin * const psPin );
extern bool boGPIODDgetPin( const tGPIOpin * const psPin );

#endif // _GPIO_H_

//---------------------------------------------------------
// end of header file (gpio.h)
//---------------------------------------------------------

//---------------------------------------------------------
// start of example gpio_pindefs.h
//---------------------------------------------------------
 
#if !defined(_GPIO_PINDEFS_H_)
#define _GPIO_PINDEFS_H_

// LEDs
PINFUNC_DEF( GPIO_LED_GREEN,          1, 25, L, GPIO )

// USB interface
PINFUNC_DEF( GPIO_USB_VBUS,           1, 30, H, ALT1 )
PINFUNC_DEF( GPIO_USB_CONNECT,        2,  9, L, ALT1 )

// Serial Ports
PINFUNC_DEF( UART0_RX,                0,  3, H, ALT1 )
PINFUNC_DEF( UART0_TX,                0,  2, H, ALT1 )

PINFUNC_DEF( UART1_RX,                0, 16, H, ALT1 )
PINFUNC_DEF( UART1_TX,                0, 15, H, ALT1 )

// SPI port
PINFUNC_DEF( SPI_MOSI,                0,  9, H, ALT2 )
PINFUNC_DEF( SPI_MISO,                0,  8, H, ALT2 )
PINFUNC_DEF( SPI_SCK,                 0,  7, H, ALT2 )
PINFUNC_DEF( SPI_SSEL,                0,  6, L, ALT2 )

#endif // _GPIO_PINDEFS_H_

//---------------------------------------------------------
// end of example gpio_pindefs.h
//---------------------------------------------------------

//---------------------------------------------------------
// start of gpio.c
//---------------------------------------------------------

#define   _INSIDE_GPIODD_             ( 1 )
#include "gpio.h"

/* --------------------------------------------------------
   vGPIODDsetPinFunction
   ........................................................
   Description  : Set the pin connect block from a pin 
                  definition 

   Params : psPin - pin definition

   Returns : Nothing
   ----------------------------------------------------- */

void vGPIODDsetPinFunction( const tGPIOpin * const psPin )
{
  if ( psPin )
  {
    // Each PINSELXX register contains the settings 
    // for 16 port pins.  Each PINSEL register is 4 bytes 
    // above the previous one.  Base addresses for ports 
    // are 32 bytes apart.
    dword dwPinSel = LPC_PINCON_BASE + 
                     ((psPin->dwReg - LPC_GPIO0_BASE) / 4) + 
                     ((psPin->iBit / 16) * 4);
    dword dwMask = ( 0x00000003 << ( 2 * ( psPin->iBit % 16 ) ) );
    REG32 pdwPinSel = (REG32)dwPinSel;

    *pdwPinSel = ( *pdwPinSel & ~dwMask ) | 
                 ( psPin->dwFunction & dwMask );
  }
}

/* --------------------------------------------------------
   vGPIODDsetPinDirection
   ........................................................
   Description  : Sets the pin direction from a pin 
                  definition

   Params : psPin - pin definition
            boOutput - set to TRUE to turn the pin into an 
                       output

   Returns : Nothing
   ----------------------------------------------------- */

void vGPIODDsetPinDirection( const tGPIOpin * const psPin, 
                             const bool boOutput )
{
  if ( psPin )
  {
    if( boOutput )
    {
      (*((REG32) (psPin->dwReg + FIO_DIR_OFS))) |= ( 1 << psPin->iBit );
    }
    else
    {
      (*((REG32) (psPin->dwReg + FIO_DIR_OFS))) &= ~( 1 << psPin->iBit );
    }
  }
}

/* --------------------------------------------------------
   vGPIODDconfigurePin
   ........................................................
   Description  : Combination function to configure and set 
                  direction of GPIO pin

   Params : psPin - pin definition
            boOutput - set to TRUE to turn the pin into an 
                       output

   Returns : Nothing
   ----------------------------------------------------- */

void vGPIODDconfigurePin( const tGPIOpin * const psPin, 
                          const bool boOutput )
{
  if ( psPin )
  {
    vGPIODDsetInactive( psPin );
    vGPIODDsetPinFunction( psPin );
    vGPIODDsetPinDirection( psPin, boOutput );
  }
}

/* --------------------------------------------------------
   vGPIODDsetActive
   ........................................................
   Description  : Sets a pin to its active state

   Params : psPin - pin definition

   Returns : Nothing
   ----------------------------------------------------- */

void vGPIODDsetActive( const tGPIOpin * const psPin )
{
  if ( psPin )
  {
    // use the Set register to set a single bit
    (*((REG32) (psPin->dwReg + psPin->dwSet))) = ( 1 << psPin->iBit );
  }
}

/* --------------------------------------------------------
   vGPIODDsetInactive
   ........................................................
   Description  : Sets a pin to its inactive state

   Params : psPin - pin definition

   Returns : Nothing
   ----------------------------------------------------- */

void vGPIODDsetInactive( const tGPIOpin * const psPin )
{
  if ( psPin )
  {
    // use the Clr register to clear a single bit
    (*((REG32) (psPin->dwReg + psPin->dwClr))) = ( 1 << psPin->iBit );
  }
}

/* --------------------------------------------------------
   boGPIODDgetPin
   ........................................................
   Description  : Gets the current state of a pin

   Params : psPin - pin definition

   Returns : TRUE if the pin is in its active state, 
             FALSE otherwise.
   ----------------------------------------------------- */

bool boGPIODDgetPin( const tGPIOpin * const psPin )
{
  if ( psPin )
  {
    dword dwPins = *((REG32) (psPin->dwReg + FIO_PIN_OFS));

    if ( psPin->dwSet > psPin->dwClr )
    {
      dwPins = ~dwPins;
    }

    return ((dwPins & ( 1 << psPin->iBit )) != 0 );
  }
  else
  {
    return FALSE;
  }
}

//---------------------------------------------------------
// end of gpio.c
//---------------------------------------------------------