This is a cheap 320×240 2.8″ TFT LCD module that uses the ILI9320 controller for the display and the XPT2046 controller for the resistive touch panel. I purchased the module over a year ago but only had the opportunity to try it out recently. The module has since been phased out by the manufacturer and replaced with the HY28B that uses the ILI9325C controller.
The module has two 20-pin connectors on each side:
To my disappointment, the connectors on this module use 2mm pitch, and not the standard 2.54mm (0.1″) pitch used by most hobbyist breadboards, sockets and connectors. I also tried to search eBay and could not find anything that might be useful, other than a few 6-pin connectors for the Zigbee module which also happens to use 2mm pitch, although I did find a photo here taken by someone who has jumper cables with small headers fitting the 2mm pin pitch of this module.
This is the time when some creativity is needed. Luckily since many of the parallel communication pins on the two 20-pin connectors on both sides of the module are not used in SPI mode, I am able to break some of the unused pins, leaving space for me to bend other pins and solder them to standard 2.54mm male connectors in order to fit a breadboard:
Interfacing the LCD module
Although the ILI9320 supports both parallel and serial communications, the HY28A module is configured to only use SPI. Using the example source code provided by the seller, I was quickly able to make this LCD show some text and graphics:
One interesting thing to note about the ILI9320 is that it uses SPI mode 3, not SPI mode 0 like many other SPI devices. This Wikipedia article has a good description on the different clock polarities and phases used by each SPI mode. From a PIC point of view, this means setting the correct value for bit 8 (CKE – Clock Edge) and bit 6 (CKP – Clock Polarity) in the SPI1CON1/SPI2CON1 register according to the datasheet:
bit 8 CKE: SPIx Clock Edge Select bit
1= Serial output data changes on transition from active clock state to idle clock state (see bit 6)
0= Serial output data changes on transition from Idle clock state to active clock state (see bit 6)
bit 6 CKP:Clock Polarity Select bit
1= Idle state for clock is a high level; active state is a low level
0= Idle state for clock is a low level; active state is a high level
For mode 0 (most SPI devices), you will need to set CKP = 0 and CKE = 1. For mode 3 (the ILI9320), CKP = 1 and CKE = 0.
Interfacing the touch screen
There are two variants of the HY28A module, one with the ADS7843 controller for the resistive touch screen and the other with the XPT2046 touch controller. The main difference is that the ADS7843 outputs analog voltages while the XPT2046 uses an SPI interface for the touch controller. My module uses the XPT2406 and has 5 pins:
TP_IRQ – Interrupt Request. Low when a press is detected.
TP_CS – SPI Chip Select
TP_SDO – SPI Data Input
TP_SDI – SPI Data Output
TP_SCK – SPI Clock
Like most other resistive touch controllers, the XPT2046 will return a raw coordinate value when a press is detected on the panel. For the coordinate to be useful, the code must convert it to a coordinate within the LCD resolution. To make things simple, the code can just look at the maximum and the minimum values that are returned when a press is detected on each corner of the panel and perform a linear conversion of the values to LCD coordinates:
// height and width of LCD
#define MAX_X 240UL
#define MAX_Y 320UL
// coordinates of sample touch points at 4 corners of touch panel
#define TOUCH_X0 255
#define TOUCH_Y0 200
#define TOUCH_X1 3968
#define TOUCH_Y1 3775
// calibration constants
cal_x = (TOUCH_X1 - TOUCH_X0) / MAX_X;
cal_y = (TOUCH_Y1 - TOUCH_Y0) / MAX_Y;
// get the raw touch coordinates
xpt2046GetAverageCoordinates(&tX, &tY, 5);
// convert to LCD coordinates
pX = (tX - TOUCH_X0) / cal_x;
pY = (tY - TOUCH_Y0) / cal_y;
Reading of the touch points can be done when TP_IRQ is low, indicating that a touch is detected. To reduce noises and achieve better accuracy, it will be better to perform several reads (5~10) for every press and calculate the average coordinate of the touched points. TP_CS must remain low when reading is performed, and set to high when reading is done. Coordinates reading must be stopped as soon as TP_IRQ is high, indicating that touch presses are no longer detected.
I was quickly able to prototype a program that allows me to draw on this resistive touch panel:
If you can’t see it, the text reads “PPDS STMJ” and “BAHX MBA”. The isolated drawing points are from the noises due to breadboard stray capacitance. A median filter can probably be used to remove these isolated points for better accuracy.
I also tried to connect the drawn points and achieved a better output:
The text reads “Hello ABC” in the first picture and “123” in the second picture. Ignoring the inappropriate connections between two adjacent characters (due to the inability to detect when the stylus is released from the screen to stop connecting points), the other problem is the zig-zag and not smooth shape of the drawing. This is probably because of the slow speed of the PIC24. At 16MHz SPI speed and 32MHz clock speed on my PIC24FJ64GA002, some precious time is wasted communicating with the touch controller, calculating the touched coordinates and plotting them. During this time, other points drawn by the user were lost and not plotted on the screen.
As it takes considerable time to read the touch points and plot them, it is not possible to migrate the entire process into an interrupt to increase touch sensitivity as an interrupt routine also needs to finish executing as fast as possible. The only solution for a smooth drawing would be a much faster clock speed, or perhaps to use Direct Memory Accessing (DMA), supported by this PIC. However, at this moment I do not yet have the time to explore either option.
Sample code download
Codes for both the LCD and the touch panel make use of my custom SPI library for the PIC24FJ64GA002 to facilitate SPI communication. Before working with the LCD or the touch screen, you will need to initialize the SPI modules using the spiInit method from my SPI library as shown below:
spiInit(1, 0b00011011, 0); // SPI Module 1 for Touch Screen, secondary prescale 2:1, primary prescale 1:1, SPI mode 0
spiInit(2, 0b00011011, 3); // SPI Module 2 for LCD, secondary prescale 2:1, primary prescale 1:1, SPI mode 3
Assuming that the PIC is running at 32MHz, the above code will set the SPI clock at 16MHz, fast enough for many purposes.