25/02/2018 LPC1114FN28, ARM, GPIO, interrupt

Arm-cortex m0 programming with LPC1114FN28 , a guild to GPIOs

This is a migrated version of my Wordpress post, written on : 14 Mars 2015

Input/Output

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 :

  • SYSAHBCLKCTRL : System AHB clock control, allows us to enable the clock on the GPIOs and the IOCON (IO configuration block) by turning on bit 6 and 16 of the register.
  • The IOCON registers : control the function (GPIO or peripheral function like SPI,I2C,etc.) and the input mode of all PIOn_m pins, there is a dedicated configuration register for each pin. The majority of these registers are named as IO_CON_PIOn_m, except for some pins with additional special functionality, for example the PIO0_0 is also used as reset pin, so its configuration register is named differently as IO_CON_RESET_PIO0_0. You may want to refer to the UM10398 document (chapter 7) for more informations.
  • GPIOnDIR (n is the number of ports), the data direction register, this one allows us to select the data direction of a GPIO pin by turning on (for output) or off (for input) the corresponding bit. For example, say we want to select pin 9 on port 0 (PIO0_9 or dp2) as output, we must set the bit 9 on the GPIO0DIR to 1 (0 for input).
  • GPIOnDATA (n is the number of port), the data register, which holds the current logic state (HIGH or LOW) of the pin. A read on this register returns the current logic level of the pin (regardless its configuration). A write on this register will take effect (change of logic level) only if the pin is configured as output, that is it's unnecessary to write to an input pin.

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

GPIO Interrupt

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 :

  • GPIOnIS : selects the interrupt on pin x as level sensitive (1) or edge sensitive (0). Says you want to set the interrupt on pin PIO0_9 as edge sensitive, you should clear the bit 9 (0) on GPIO0IS.
  • GPIOnIBE : configure how the interrupt on pin x is controlled, personally i'll let the GPIOnIEV register to control the interrupt, so i'll definitively set bit x on this register to 0.
  • GPIOnIEV : configure how the interrupt on pin x is triggered :
    IF bit x on this register is set to 0, depending on your configuration on the GPIOnIS register, the interrupt on pin x will be triggered when the pin x is LOW (level sensitive) or falling edge (edge sensitive)
    IF bit x on this register is set to 1. the interrupt on pin x will be triggered when the pin x is HIGH (level sensitive) or rising edge (edge sensitive).
  • GPIOnIE : Set 1 to bit x on this register if we want to enable interrupt on pin x, 0 to disable it.
  • GPIOnIC : used to clear (set 1 to bit x) the edge detection logic on pin x that is configured as edge sensitive in GPIOnIS, this has no effect if the pin is configured as level sensitive.
  • GPIOnRIS : hold the interrupt status for all pins on port n. If pin x on this register is 1, that means there are an interrupt triggered on pin x.
  • ISER : allow us to power on the GPIO interrupt 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
    }
}

Related posts

Powered by antd server, (c) 2017 - 2020 Xuan Sang LE