OCFreaks!

LPC1343 GPIO Programming Tutorial

In this tutorial we will go through LPC1343 GPIO Programming. This tutorial is also applicable for LPC13x1/x2/x3 as well. LPC1343 is a ARM Cortex-M3 based MCU from NXP(Philips). The Name of Registers, Data structures that we will be using in programming examples in this tutorial are defined in LPC11xx.h header file.

Most of the function pins on LPC1343 Micro-controllers are grouped into Ports. LPC1343 has 4 ports viz. Port 0 to 3. By default, all pins are configured as inputs after reset.

The registers for each port are grouped into a structure with the following naming convention: LPC_GPIOx , where x is the port number. The GPIO ports are 12-bit wide i.e. a maximum 12 pins can be mapped, but depending you MCU package each port may have a few or many pins which cannot be used i.e. they are ‘reserved’. For this tutorial I will be using LPC1343 in LQFP48 package as reference. For HVQFN33 package please refer table no. 146 on page 132 of the user-manual (Rev 5) for which pins are available for use.

For LPC1343 (LQFP48):

The naming convention(as used in the Datasheet) for Pins on MCU is ‘PIOx_y’ where ‘x’ is the port number (0, 1, 2 or 3) and ‘y’ is simply the pin number in port ‘x’. For example : PIO0_4 refers to Pin number 4 of Port 0 , PIO1_8 refers to Pin number 8 in Port 1 on some development boards like LCPXpresso it will be marked Px.y instead of PIOx_y. Both mean the same.

GPIO Registers in LPC134x

The GPIO block has many registers. We will only go through some of these registers that are within the scope of this tutorial.

1) DATA : This register can be used to Read or Write values directly to the pins. Regardless of the pin direction set or any function begin selected for the particular pins, it gives the current state of the GPIO pin when read. When a pin is configured as GPIO input, then writing to this register won’t have any affect on the pin. Similarly when a pin is configured for any other digital function, a write will have no effect. Used as LPC_GPIOn->DATA in programming.

2) DIR : This register is used to control the Direction of Pins. Setting a bit to 0 in this register will configure the corresponding Pin[0 to 11] to be used as an Input while setting it to 1 will configure it as Output. Used as LPC_GPIOn->DIR in programming.

IOCON (I/O Configuration) Registers – Each functional pin on LPC1343 has a dedicated IOCON register with which we can control: the function of PIN, enable pull-up or pull-down, Hysteresis to filter out spurious changes in input, and other modes like I2C, ADC and Open-Drain. For using digital GPIO we are only concerned with : Changing Pin function , enabling internal pull-up/down resistors and hysteresis. In CMSIS, all the IOCON registers are grouped under LPC_IOCON structure. While programming these can be accessed as LPC_IOCON->[register-name]. Where [register-name] is the name of the IOCON register for a specific PIN as given in datasheet/manual.

The general bit description for IOCON register is as shown in the diagram below:

First 2 bits [2:0] are used for selecting PIN function: 0x0 – 1st function, 0x1 – 2nd function, 0x2 – 3rd function, 0x3 – 4th function. Refer datasheet for which functions are available for the given PIN.

Next 2 bits [3:4] are used to select on-chip pull resistors: 0x0 – Both pull-up/down resistors disabled, 0x1 – Pull-down resistor enabled, 0x2 – Pull-up resistor enabled, 0x3 – Repeater mode.

5th Bit [5] is for Hysteresis. Setting it to 0 will disable Hysteresis and 1 will enable it.

I have discussed IOCON register in detail for LPC1343 in my previous tutorial : LPC11xx and LPC13xx LPC_IOCON Register Tutorial

Most of the PINS of LPC134x MCU are Multiplexed i.e. these pins can be configured to provide up to 4 different functions. Not all pins on LPC134x are configured as GPIO by default after reset! For these pins you will need to explicitly re-assign the function to GPIO using IOCON register for the respective pins. Other pins can be directly used as GPIO, since their default function is GPIO (configured as inputs with pull-ups enabled) after reset.

LPC1343 GPIO Programming & Example Code in C/C++

LPC13xx.h header is based on CMSIS(Cortex Microcontroller System Interface Standard) developed by ARM. The register definitions for Cortex-M3 LPC134x MCUs are organized into groups depending on their functionality using “C Structure” definitions. From C/C++ programming point of view, this makes interfacing peripherals simple. For example all registers for Port 0 are grouped into structure defined as LPC_GPIO0.

LPC_GPIOx is defined as a pointer to a structure, which contains GPIO registers for Port x, in LPC13xx.h header. Hence to use any register, for e.g. DATA, we must use the arrow “->” operator to de-reference members of structure (since the structure itself is a pointer) to access the register as follows : LPC_GPIO0->DATA = value. For creating projects you can either use KEIL uV5, LPCXpresso or MCUXpresso, but make sure you include CMSIS library. If you are using LPCXpresso LPC1114 board you can check out this tutorial.

Prerequisite : Before we start programming LPC134x you need to have basic understanding of Binary and Hexadecimal system and Bitwise operations in C/C++, here are two tutorials which can go through (or if you are already acquainted with these you can skip these and continue below) :

Now lets see how we can assign values to registers. We can use Hexadecimal notation & decimal notation for assigning values. If your compiler supports other notations like binary notation use can use that too. Lets say, we want to set PIN 6 of Port 1 as output. It can be done in following ways:


CASE 1. LPC_GPIO1->DIR = (1<<6); //(binary using left shift - direct assign: other pins set to 0)

CASE 2. LPC_GPIO1->DIR |= 0x0000040; //or 0x40; (hexadecimal - OR and assign: other pins not affected)

CASE 3. LPC_GPIO1->DIR |= (1<<6); //(binary using left shift - OR and assign: other pins not affected)

First thing to note here is that preceding Zeros in Hexadecimal Notation can be ignored because they have no meaning since we are working with unsigned values here which are assigned to Registers. For eg. 0x7F and 0x07F and 0x007F all mean the same.

Note that bit on extreme left is Bit 31 which is the MSB and the bit on extreme right is Bit 0 which is LSB which is the Big Endian Format. Hence, bit 0 is the 1st bit from right , bit 1 is the 2nd bit from right and so on. BIT and PIN Numbers are Zero(0) indexed which is quite evident since Bit ‘x’ refers to (x-1)th location in the corresponding register.

example codes:

Ex. 1)

Consider that we want to configure Pin 8 of Port 0 i.e PIO0_8(P0.8) as Output and want to drive it HIGH. This can be done as :


LPC_GPIO0->DIR |= (1<<8); //Config PIO0_8 as Ouput
LPC_GPIO0->DATA |= (1<<8); //Drive PIO0_8 HIGH

Ex. 2)

Driving Pin 5 HIGH of Port 1 i.e PIO1_5(P1.5) and then LOW can be does as follows:


LPC_GPIO1->DIR |= (1<<5); //Set PIO1_5 as Output
LPC_GPIO1->DATA |= (1<<5); //PIO1_5 driven HIGH
LPC_GPIO1->DATA &= ~(1<<5); //PIO1_5 driven LOW

Ex. 3)

Configuring PIO0_6(P0.6) and PIO0_2(P0.2) as Output and Setting them High:


LPC_GPIO0->DIR |= (1<<6) | (1<<2); //Config PIO0_6 and PIO0_2 as Output
LPC_GPIO0->DATA |= (1<<6) | (1<<2); //Drive Output High for PIO0_6 and PIO0_2

Ex. 4)

Configuring Pins 0 to 7 of Port 2 (PIO2_0 to PIO2_7) as Output and Setting them High:


LPC_GPIO2->DIR |= 0xFF; //Set PIO2_0 to PIO2_7 as Output
LPC_GPIO2->DATA |= 0xFF; //Drive PIO2_0 to PIO2_7 HIGH

Ex. 5)

In this example code, we will configure Pin 10 of Port 2 as Input with Pull-Down & Hysteresis enabled :


LPC_GPIO2->DIR &= ~(1<<10); //Config PIO1_5 as input (It will be anyways input after reset)
LPC_IOCON->PIO2_10 = (1<<3) | (1<<5); 
//Enable on-chip Pull-down resistor [4,3]=01, Enable HYS [5]=1

When using switches as inputs, you can use an RC filter with Hysteresis enabled to debounce the input. Bouncing is the spurious changes in input which occurs until the contacts of the switch have stabilized. This can be filtered out using deboucning techniques, either in Software or Hardware.

Now lets play with some real world examples.

The below examples are given, assuming 72Mhz CCLK which is configured & initialized by system startup code generated by Keil UV5/UV4, MCUXpresso, LPCXpresso, CoIDE, etc.

Ex. 6)

LPC13xx Blinky Example Code - Here we drive pin 7 of port 0 (PIO0_7) repeatedly high to low. PIO0_7(P0.7) of LPC13xx devices have High current capability so you can directly drive an LED with it. Connect LED between PIO0_7 and GND. Here we will introduce some "hard-coded" delay between making all pins HIGH and LOW (and vice-versa) so it can be noticed.


#include <lpc13xx.h>

void delay(void);

int main(void)
{
	LPC_GPIO0->DIR = (1<<7); //Config PIO0_7 as Output
	
	while(1)
	{
		LPC_GPIO0->DATA = (1<<7); //Drive output HIGH to turn LED ON
		// Better way would be LPC_GPIO0->DATA |= (1<<7);
		
		delay();
		LPC_GPIO0->DATA = 0x0; //Drive output LOW to turn LED OFF
		// Better way would be LPC_GPIO0->DATA &= ~(1<<7);
		
		delay();
	}
	return 0; //normally this wont execute
}	

void delay(void) //Hard-coded delay function
{
	int count,i=0;
	for(count=0; count < 4500000; count++) //You can edit this as per your needs
	{
		i++; //something needs to be here else compiler will remove the for loop!
	}
}

Ex. 7)

In this example we will configure PIO1_5 (P1.5) as Input and monitor it for a logic LOW on the pin. Here we will use a tactile switch whose one end is connected to PIO1_5 and other to GND (+3.3V). PIO0_7(P0.7) is configured as output and connected to an LED. Initially LED will be off but when the switch is pressed it will turn the LED. Once the LED is turned ON it will stay ON until the MCU is reset externally. The setup is shown in the figure below:


#include <lpc13xx.h>

int main(void)
{
	LPC_GPIO1->DIR &= ~((1<<5)) ; //config PIO1_5 as Input
	LPC_GPIO0->DIR |= (1<<7); //config PIO0_7 as Output

	LPC_GPIO0->DATA &= ~(1<<7); //drive PIO0_7 LOW initially

	while(1)
	{
		if( LPC_GPIO1->DATA & (1<<5) ) //Evaluates to True if PIO1_5 is HIGH
		{
			LPC_GPIO0->DATA |= (1<<7); //drive PIO0_7 High to turn LED ON
			//Now PIO0_7 will be held high unless the MCU is reset
		}
	}
	return 0; //this won't execute normally
}


Ex. 8)

Now lets extend example 7 so that when the button is pressed, the LED will glow and when released or not pressed the LED won't glow. Note that in both Example 7 and 8: Since internal pulls are enabled by default when switch is not pressed, internal pull-up resistor will force the input to be HIGH.


#include <lpc13xx.h>

void tinyDelay(void);

int main(void)
{
	LPC_GPIO1->DIR &= ~((1<<5)); 
	LPC_GPIO0->DIR |= (1<<7);
	
	LPC_GPIO0->DATA &= ~(1<<7); //Turn off LED initially
	
	while(1)
	{
		if( !(LPC_GPIO1->DATA & (1<<5)) )
		{
			LPC_GPIO0->DATA |= (1<<7); //Input LOW, LED = ON
		}
		else
		{
			LPC_GPIO0->DATA &= ~(1<<7); //Input HIGH, LED = OFF
		}
	}
	return 0; //normally this won't execute
}
Imp. Note: As mentioned in Datasheet, many of the Pins on LPC134x are 5V tolerant in digital mode. However, I would recommend that whenever possible, use a level-translator(5V<->3.3V) or buffer when interfacing external 5V signal as digital input. If you need any help regarding level translation just let me know in the you comment below.