Interfacing HY28A LCD module with ILI9320 controller and XPT2046 resistive touch panel to PIC microcontroller
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.
Physical connections
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
C30 code for the ILI9320
C30 code for the XPT2046
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.
Hi again.
Spent some time today reading some of your other topics.
I love doing some programming but for me its only a hobby. Your documentation for me is very comprehendible. You mentioned play super Mario on a 80386,
Giving away age, So here is my confession. I cut my teeth on systems like Tandy TRS 80 Apple 2E, commodore omega, CPUs like 6800 68000 8088 Z80.
I really disliked 8088 offset addressing.
I was hand assembling Z80 Code in binary input via 8 bit dip switches. Built my first R2D2 look alike robot that was self navigating based on 2 Z80 processors.
I remember the cost of a static Ram chip 6116 2k S Ram cost 15 dollars calculate what 1 Meg of ram was going to cost. But at the core nothing has changed binary code its still doing what it was always doing. What has changed and is always changing is the terminology, Like when they stopped using the terms directory, root directory and called them folders . now its Apps not program. I still use 7400 series IC. and a truth table actually told the truth, lol. Anyway great stuff here. The reason am browsing your site again is I am going to include the touch screen feature, Now using the SSD1298 displays with SD card support, still reading cheers.
Hi Rodney,
Thanks for your updates and thanks for sharing your knowledge too!
Super Mario was my favorite game back then and I still played it once in a while nowadays – on my Windows 8 computer running DOSBox! Too bad support for full-screen DOS applications was removed by Microsoft after Windows XP, and there is no support for 16-bit applications in 64-bit versions of Windows, so DOSBox is the only recourse nowadays for me to play Mario, and other DOS games e.g. Prince of Persia, Dyna, etc.
I love old technology too. Used to collect some old computers (like the Casio PB-700 calculator and the Casio FA-11 printer which I demonstrated in my other articles) and played around them. They are still very fascinating and useful devices despite the limited processing power of the chips at the time. Yes, I love the Z80 and have been doing some research on it. I have always wanted to get this project working http://searle.hostei.com/grant/cpm/ – a single board Z80 computer with support serial console and composite video output. The project seems well documented, but I procrastinated a few times after seeing the massive number of connections between the 74-series logic chip that I have to make. Perhaps I can design a PCB and make it easier to work with…
I am glad you find my site useful – I will continue to update it with other interesting projects which I have achieved. There are several in queue, for example, doing English text to speech and Chinese speech recognition on a PIC, but I have yet to have the time to document them. My full time job as a software developer takes away most of my time …
For the touch screen, this article demonstrates the usage of a resistive touch screen in your project. As you can see from the article, it was not good for continuous drawing on my PIC24 running at 32 MHz, but I am sure quality will be much improved and the drawing will be smoother if a faster PIC32 is to be used. Do keep me updated on your progress and other interesting findings you may come across
Wow this is extremely detailed stuff. Have only just started out in programming and hardware recently and it is only just a hobby for me. Read some of your previous posts and I must say it inspires me to keep going and learn more, I have learnt a lot from your posts, keep up the great work. Thank you again.
Brian Hopkins @ Microtips USA
Hi Brian,
Glad you find the information useful. Being a hobbist embedded programmer is tough but brings much fun too. Enjoy programming with microcontrollers and feel free to comment here if you have any questions.
Cheers
The Interfacing HY28A LCD module with ILI9320 controller and XPT2046 resistive touch panel to PIC microcontroller is a fantastic board to experiment with for inexperienced programmers. For instance follow the simple programming steps and within minutes the screen will display incredibly colourful fonts and numerics of varying shapes and sizes. have fun exploring the basic features of this quirky board.
Raymond @ CKS Global Solutions LTD
Hello, I am using a PIC32 MCU to control a touch LCD panel. The touch controller is XPT2046. Therefore, I had downloaded your spi_lib_mode_change.zip and xpt2046.zip files for reference. As PIC24 is very similar to PIC32, the codes are easy to understand, and I made modifications according to my requirements.
However, I realised that the C functions written in xpt2046.c are incomplete. I am expecting some functions like reading the x & y coordinates from the screen, and a calibration function. The x & y coordinates should be stored in a structure isn’t it? Do you have an example like your main.c that demonstrates the use of the functions in xpt2046.c for reference?
Hi,
Thanks for your comment. The spi_lib_mode_change.zip is only used for SPI initialization (in particular mode 3, used by this touch controller) and is specific to the PIC24 which I experiemented on. You can refer to it and adapt it to your PIC32, but some changes (registers, etc) are probably required for it to work.
The xpt2046.h contains only two functions required for this touch controller. First you need to call touch_Init (after SPI initialization and after adjusting the pin configuration). After that, monitor the TP_IRQ (active low) pin. When this pin is 0V, a touch is detected, and you should immediately call touch_GetCoordinates to get the coordinates of the touch point which was pressed.
As for calibration there are no calibration routines provided. It is done by measuring the 4 coordinates of the 4 corners of the touch panel (see constants TOUCH_X0, TOUCH_Y0, TOUCH_X1, TOUCH_Y1) and then applying a linear conversion formula when calculating the touch coordinates. This should be working fine for most purposes.
You may find the full MPLAB project source code here
http://toughdev.com/public/pic24f_touchlcd.zip – it demonstrates how to use the touch controller and several other peripherals as well.
Let me know if this helps.