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

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.


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;
}

Begin by importing the driver and sample application bundle (zip file) for the LPC1343 LPCXpresso Board found at the Embedded Artists support site.
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.