This is a migrated version of my Wordpress post, written on : 14 Mars 2015
The LPC1114FN28 has two pin ports (0 and 1) that make totally 22 pins (12 for port 0 and 10 for port 1, as shown in the image below). All of these pins could be used as GPIOs. By default, they are all input and pull up-enable (that is each pin is connected to an internal pull-up resistor) except for PIO0_5 (dp5) and PIO04 (dp27) which are "open-drain" (a transistor connects to low and nothing else). Their functionalities can be configured easily by software.
The figure below shows the pin out of the chip, note that each PIOn_m refers to the pin m on the port n.
To configure these pins as GPIOs, we need to work with the following registers :
Ok, it sounds a little bit complicated ! Actually no, thing is easier when you start coding. Let's take an example, assume we have an LED connected to PIO0_8 (dp1), this pin acts as an output pin that drives the LED. On the PIO1_5 (dp14) we have a button attached to it, the pin will be configured as input to take command from the button. Now we want the led to be lighted on whenever the button is pressed.
Note that, in the wiring, we don't need a pull-up resistor for the GPIO1_5 pin since we will use its internal pull-up resistor instead ( enabled by default).
The first thing we need to do is to enable the clock for GPIOs and IOCON:
// Turn on clock for GPIO, IOCON
SYSAHBCLKCTRL |= (1<<6) + (1<<16);
Then, we select the GPIO function for each pin using the IOCON registers :
// power on GPIO function on PIO0_8
IOCON_PIO0_8 &= ~(0x7);
// power on GPIO function on PIO1_5
IOCON_PIO1_5 &= ~(0x7);
This mean that we set the value 0x0 to the bits [2:0] of the registers to power on the GPIO function block. We use this value for almost pins to allow the GPIO function except for PIO0_10, PIO0_11,PIO1_0-PIO1_3,PIO0_0 which use the value 0x1 instead (you can find more from the UM10398 document).
The final step is to select the data direction for each pin (input for PIO1_5 and output for PIO0_8) :
// configure PIO0_8 as output
GPIO0DIR |= (1<<8);
// configure PIO1_5 as input
GPIO1DIR &= ~(1<<5);
That's all for the GPIOs configuration, now we just read the PIO1_5 pin state and control the LED by writing value to the PIO0_8 using the data register
// turn the led off
GPIO0DATA &= ~(1<<8);
while(1)
{
// When the button is pressed,
// the PIO1_5 pin state is LOW (0)
if((GPIO1DATA & (1<<5)) == 0)
{
// turn the led on
GPIO0DATA |= (1<<8);
}
else
{
// turn the led off
GPIO0DATA &= ~(1<<8);
}
}
That's it, now the led should light on each time you press the button
Some time, we want to use an input GPIO as an interrupt source for triggering a specific event (stop the motors when the car hit the wall for example). To use the interrupt on the GPIOs, you need to deal with more registers related to this functionality :
Now return to the previous example, but this time, we will use the button on PIO1_5 as interrupt source, when the button is pressed, it will trigger the interrupt on PIO1_5 and light on the LED.
Assume that the GPIO1_5 has been configured as input (on the previous section).
First we need to power on the GPIO interrupt functionality on port 1 by turning on the bit 30 on the ISER register (bit 31 if you use a pin on port 0) :
ISER |= (1<<30);
We will trigger the interrupt when the value of PIO1_5 change, so, we configure the interrupt on the pin as edge sensitive :
GPIO1IS &= ~(1<<5);
Let the GPIOnIEV control how the interrupt is triggered :
GPIO1IBE &= ~(1<<5);
We want the interrupt will be triggered on rising edge of GPIO1_5, so turn on the bit 5 on GPIO1IEV (turn off this bit if you want falling edge) :
GPIO1IEV |= (1<<5);
And finally, enable the interrupt on the GPIO1_5 pin :
GPIO1IC = (1<<5);
GPIO1IE |= (1<<5);
That's how the GPIO interrupt is configured on pin 5 of port 1. If you use different pin and/or port, you need to adapt the register name and pin number to your own ones.
Now, when the interrupt is triggered, it's your responsibility to handle what to do with it, for that, you need to define a interrupt handler, so go ahead an open the init.c file where the interrupt vector is defined and do some modifications as below :
#include "lpc111x.h"
void init(void);
void clock_init();
void Default_Handler(void);
extern void gpio1_irq(void);
...
const void * Vectors[] __attribute__((section(".vectors"))) ={
(void *)0x10001000, /* Top of stack */
init, /* Reset Handler */
Default_Handler, /* NMI */
.....
gpio1_irq, /* PIO1 */
Default_Handler /* PIO0 - change here if you use port 0 */
};
This means that we insert the definition of our handler function, and configure the interrupt vector to used this function as the interrupt handler for GPIO on port 1. If you use port 0, you should change the interrupt vector accordingly.
Now back to our main.c file and put the implementation of the handler function here :
void gpio1_irq(void)
{
// if the interrupt is triggered on pin 5 of port 1
if((GPIO1RIS & (1<<5)) > 0)
{
// toggle the led
GPIO0DATA ^= (1<<8);
// clear the edge detection logic
GPIO1IC = (1<<5);
}
}
Here the complete main.c file :
/*The LED is connected to the dp1 of the chip (PIO0_8)*/
#include "lpc111x.h"
void gpio1_irq(void)
{
// if the interrupt is triggered on pin 5 of port 1
if((GPIO1RIS & (1<<5)) > 0)
{
// toggle the led
GPIO0DATA ^= (1<<8);
// clear the edge detection logic
GPIO1IC = (1<<5);
}
}
int main()
{
// Turn on clock for GPIO, IOCON
SYSAHBCLKCTRL |= BIT6 + BIT16;
// power on GPIO function on PIO0_8
IOCON_PIO0_8 &= ~(0x7);
// configure PIO0_8 as output
GPIO0DIR |= (1<<8);
// turn off the led
GPIO0DATA &= ~(1<<8);
// power on GPIO function on PIO1_5
IOCON_PIO1_5 &= ~(0x7);
// configure PIO1_5 as input
GPIO1DIR &= ~(1<<5);
// turn on interrupt function
ISER |= (1<<30);
// interrupt as edge sensitive
GPIO1IS &= ~(1<<5);
// interrupt triggered on rising edge
GPIO1IBE &= ~(1<<5);
GPIO1IEV |= (1<<5);
//enable interrupt on GPIO1_5
GPIO1IC = (1<<5);
GPIO1IE |= (1<<5);
while(1)
{
;;//do other stuff here
}
}