3.2 inch QVGA display with the LPCXpresso Board

This project demonstrates how easy it is to use the 3.2 inch QVGA display from Embedded Artists with the LPCXpresso by using SPI.


QVGA display connected to Base Board
Figure 1 - QVGA display connected to Base board (Mandelbrot)

 

Required Parts

Preparation

The QVGA LCD Display must be connected to the corresponding pins on the female extension connector on the Embedded Artsists LPCXpresso Base Board (see Table 1 for correct setup).

QVGA LCD Display extension connector LPCXpresso extension connector Usage
VCC pin 1 VCC Supply voltage
VCC pin 39 VCC Supply voltage
GND pin 40 GND Ground
SCK pin 35 GPIO 2.11 SPI-CLK
CS pin 29 GPIO 1.11 SPI-SSEL (GPIO)
RS pin 31 GPIO 2.8 Control signal for data/register
SI pin 34 GPIO 0.9 SPI MOSI
SO pin 36 GPIO 0.8 SPI MISO (not used)
RST pin 30 GPIO 0.0 Reset of display
LED_SHDN pin 38 GPIO 2.5 Control of display backlight
PS0 pin 3 GPIO 2.9 Configuration of display interface
PS1 pin 5 GPIO 2.6 Configuration of display interface
PS3 pin 7 GPIO 2.7 Configuration of display interface
PS2 pin 6 GPIO 2.4 Configuration of display interface

Table 1 - Transmit Packet format

There are four signals; PS0, PS1, PS2 and PS3 that are static configuration signals. After reset these signals determins which interface the display controller shall have. The display controller supports different parallel and serial interfaces. We will use the 4-wire SPI interface in this project. SPI is used since only four wires are needed for communication. There are parallel interfaces also but then more pins are needed to interface the display. The trade-off is that display updates takes longer time with a serial interface compared to a parallel interface.

4-wire SPI means that SPI-CLK, SPI-MOSI, SPI-SSEL and a control signal is used. The control signal determine if a SPI transfer is a data or register transfer. In this project the SO pin is optional because we will not use the touch functionality of the QVGA LCD display.


QVGA LCD connector pinning
Figure 2 - QVGA LCD connector pinning


QVGA LCD connected to Base Board
Figure 3 - Display connected to connector on the Base Board

Software

One thing to think about when working with SPI is that the SPI clock frequency is set to 1.125 MHz by default on the LPC1343 LPCXpresso peripheral drivers. In function SSPInit() that can be found in the file ssp.c, it's possible to change the serial clock rate by changing the value of LPC_SSP->CR0. The maximum SPI clock frequency for the display is 10 MHz. In the example below we set the SPI clock frequency to 9 MHz by setting LPC_SSP->CR0 to 0x0007. The SPI clock frequency calculation is (System Clock)/((SSP_PCLK divider)*(Prescale Clock Divider)*(Serial Clock Rate +1)), which will be in this case (72MHz/(4*2*(0 +1)) = 9MHz. For more information of SPI see the user manual for LPC1343.

void SSPInit( void )
{
  uint8_t i, Dummy=Dummy;

  LPC_SYSCON->PRESETCTRL |= (0x1<<0);
  LPC_SYSCON->SYSAHBCLKCTRL |= (1<<11);
  LPC_SYSCON->SSPCLKDIV = 0x02;			/* Divided by 2 */
  LPC_IOCON->PIO0_8           &= ~0x07;	/*  SSP I/O config */
  LPC_IOCON->PIO0_8           |= 0x01;		/* SSP MISO */
  LPC_IOCON->PIO0_9           &= ~0x07;	
  LPC_IOCON->PIO0_9           |= 0x01;		/* SSP MOSI */
#ifdef __JTAG_DISABLED
  LPC_IOCON->SCKLOC = 0x00;
  LPC_IOCON->JTAG_TCK_PIO0_10 &= ~0x07;
  LPC_IOCON->JTAG_TCK_PIO0_10 |= 0x02;		/* SSP CLK */
#endif
 
#if 1
  /* On HummingBird 1(HB1), SSP CLK can be routed to different pins,
  other than JTAG TCK, it's either P2.11 func. 1 or P0.6 func. 2. */
  LPC_IOCON->SCKLOC = 0x01;
  LPC_IOCON->PIO2_11 = 0x01;/* P2.11 function 1 is SSP clock, need to combined
	  						with IOCONSCKLOC register setting */
#else
  LPC_IOCON->SCKLOC = 0x02;
  LPC_IOCON->PIO0_6 = 0x02;	/* P0.6 function 2 is SSP clock, need to combined
	  						with IOCONSCKLOC register setting */
#endif

#if USE_CS
  LPC_IOCON->PIO0_2 &= ~0x07;	
  LPC_IOCON->PIO0_2 |= 0x01;		/* SSP SSEL */
#else
  LPC_IOCON->PIO0_2 &= ~0x07;		/* SSP SSEL is a GPIO pin */
  /* port0, bit 2 is set to GPIO output and high */
  GPIOSetDir( PORT0, 2, 1 );
  GPIOSetValue( PORT0, 2, 1 );
#endif
		
  /* Set DSS data to 8-bit, Frame format SPI, CPOL = 0, CPHA = 0, and SCR is 0 */
  LPC_SSP->CR0 = 0x0007;

  /* SSPCPSR clock prescale register, master mode, minimum divisor is 0x02 */
  LPC_SSP->CPSR = 0x2;

  for ( i = 0; i < FIFOSIZE; i++ )
  {
	Dummy = LPC_SSP->DR;		/* clear the RxFIFO */
  }

  /* Enable the SSP Interrupt */
  NVIC_EnableIRQ(SSP_IRQn);
	
  /* Device select as master, SSP Enabled */
#if LOOPBACK_MODE
  LPC_SSP->CR1 = SSPCR1_LBM | SSPCR1_SSE;
#else
#if SSP_SLAVE
  /* Slave mode */
  if ( LPC_SSP->CR1 & SSPCR1_SSE )
  {
	/* The slave bit can't be set until SSE bit is zero. */
	LPC_SSP->CR1 &= ~SSPCR1_SSE;
  }
  LPC_SSP->CR1 = SSPCR1_MS;		/* Enable slave bit first */
  LPC_SSP->CR1 |= SSPCR1_SSE;	/* Enable SSP */
#else
  /* Master mode */
  LPC_SSP->CR1 = SSPCR1_SSE;
#endif

#endif
  /* Set SSPINMS registers to enable interrupts */
  /* enable all error related interrupts */
  LPC_SSP->IMSC = SSPIMSC_RORIM | SSPIMSC_RTIM;
  return;
}
                    

In lcdPinSetup() that can be found in qvga_lcd.c, the output pins for the display are configured. The following pins must be set as outputs: RS/PS0/PS1/PS2/PS3/CS/Backlight. The default value for CS and backlight control is high. It's possible to dim the backlight by connecting it to a PWM signal instead and alter the duty cycle. This functionality is not demonstrated in this project. The PS0/PS1/PS2/PS3 configurations pins are set to 0/1/1/1. This sets the display controller interface to 4-wire SPI.

 
void lcdPinSetup(void) {
    // Set RS as output
    GPIOSetDir(PORT2, 8, 1);

    //PS0
    GPIOSetDir(PORT2, 9, 1);
    GPIOSetValue(PORT2, 9, 0);
    //PS1
    GPIOSetDir(PORT2, 6, 1);
    GPIOSetValue(PORT2, 6, 1);
    //PS3
    GPIOSetDir(PORT2, 7, 1);
    GPIOSetValue(PORT2, 7, 1);
    //PS2
    GPIOSetDir(PORT2, 4, 1);
    GPIOSetValue(PORT2, 4, 1);

    //Backlight
    GPIOSetDir(PORT2, 5, 1);
    GPIOSetValue(PORT2, 5, 1);

    //set CS as output
    GPIOSetDir(PORT1, 11, 1);
    GPIOSetValue(PORT1, 11, 1);
}
void initLCD(void) {
    lcdPinSetup();

    //Setup to use 4-wire SPI
    lcdWriteToRegister(0x07, 0x0021);
    lcdWriteToRegister(0x00, 0x0001);
    lcdWriteToRegister(0x07, 0x0723);
    lcdWriteToRegister(0x10, 0x0000);
    delay32Ms(0, 200);
    lcdWriteToRegister(0x07, 0x0033);
    lcdWriteToRegister(0x11, 0x6830);
    lcdWriteToRegister(0x02, 0x0600);
    lcdWriteToRegister(0x0f, 0x0000);
    lcdWriteToRegister(0x01, 0x2b3f);
    lcdWriteToRegister(0x0b, 0x5308);
    lcdWriteToRegister(0x25, 0xa000);
}
                    
                    

When using 9 MHz SPI clock frequency it takes approximately 731 ms to fill the screen (240*320 pixels). It is relatively slow in other words. Full screen video is out of the question. However, there are many embedded applications that just need a simple and relatively static graphical interface. When an update is done, it is typically not the entire screen that is updated. Only a smaller area is updated. To update for example of a 50x50 pixel area only takes 26 ms. That is fast and is not perceived as annoying by the user.

To measure the time the system tick timer is configured to call it's interrupt routine every millisecond. The number of ticks is retrieved before and after the task and calculates the difference to retrieve the actual number of milliseconds it took to execute the task.

 
static uint32_t msTicks = 0;

void SysTick_Handler(void) {
    msTicks++;
}

static uint32_t getTicks(void) {
    return msTicks;
}

void measureTimeToWrite(void) {
    /* setup sys Tick. */
    SysTick_Config(SystemCoreClock / 1000);
    if (!(SysTick->CTRL & (1 << SysTick_CTRL_CLKSOURCE_Msk))) {
        /* When external reference clock is used(CLKSOURCE in
         Systick Control and register bit 2 is set to 0), the
         SYSTICKCLKDIV must be a non-zero value and 2.5 times
         faster than the reference clock.
         When core clock, or system AHB clock, is used(CLKSOURCE
         in Systick Control and register bit 2 is set to 1), the
         SYSTICKCLKDIV has no effect to the SYSTICK frequency. See
         more on Systick clock and status register in Cortex-M3
         technical Reference Manual. */
        LPC_SYSCON->SYSTICKCLKDIV = 0x08;
    }

    uint32_t t1 = 0;
    uint32_t t2 = 0;

    t1 = getTicks();

    fillScreen(YELLOW);

    t2 = getTicks();

    if (t2 > t1) {
        t2 = t2 - t1;
    } else {
        t2 = (0xFFFFFFFF - t1 + 1) + t2;
    }

    printf("\r\nTime to fill the screen = %d ms", t2);

    uint16_t i = 0;
    uint16_t j = 0;

    t1 = getTicks();
    
    for (i = 0; i < 50; i++) {
        movePen(0, i);
        for (j = 0; j < 50; j++) {
            writeToDisplay(RED);
        }
    }

    t2 = getTicks();

    if (t2 > t1) {
        t2 = t2 - t1;
    } else {
        t2 = (0xFFFFFFFF - t1 + 1) + t2;
    }

    printf("\r\nTime to write 50*50 pixels to the screen = %d ms", t2);

}                    
                    

A small program has been created. In the main() function the display is initiated. Available from qvga_lcd.h are functions for drawing, rectangles, vertical lines, horizontal lines, oblique lines, points, circles, fill the screen and triangles. In this small example it is demonstrated how to draw lines, circles, triangles and fractals.

 
int main(void) {
    GPIOInit();
    SSPInit();
    init_timer32(0, 10);

    initLCD();

    measureTimeToWrite();

    fillScreen(BLACK);

    while (1) {
        uint8_t i = 0;

        for (i = 0; i < 10; ++i) {
            lcd_color_t color = rand();
            drawLines(color);
        }

        delay32Ms(0, 3000);
        fillScreen(BLACK);

        for (i = 0; i < 20; ++i) {
            drawCircles();
        }

        fillScreen(BLACK);

        for (i = 0; i < 20; ++i) {
            lcd_color_t color = rand();
            drawCubeLines(color);
            delay32Ms(0, 100);
        }

        fillScreen(BLACK);

        drawCubeTriangles();
        delay32Ms(0, 3000);
        fillScreen(BLACK);

        drawJuliaFractal();
        delay32Ms(0, 3000);
        fillScreen(BLACK);

        drawMandelbrotFractal();
        delay32Ms(0, 3000);
        fillScreen(BLACK);
    }

    return 0;
}
                    
                    


Cube drawn on QVGA display
Figure 4 - Cube drawn on QVGA display

Source code

Import project into LPCXpresso IDE

Begin by importing the driver and sample application bundle (zip file) for the LPC1343 LPCXpresso Board found at the Embedded Artists support site.

  1. In the LPCXpresso IDE, Quickstart tab, choose Import Example project(s)
  2. Browse to the downloaded zip file (for example lpc1343_xpr_bb_100222.zip)
  3. Click finish to import the drivers and sample applications.

The second step is to import the QVGA project. Do the same as when importing the drivers, but choose qvga_example.zip in step 2. To build or debug the example just click build/debug in the QuickStart tab

More information on how to work with the LPCXpresso Base Board as well as the LPCXpresso IDE can be found in the User's Manual for the LPCXpresso Base Board. This User's Manual can be found at the Embedded Artists support site.