Introduction
In the article Installing the tinydrm driver for the ILI9341 TFT Display on a Raspberry Pi on a Raspberry Pi, I mentioned that we still needed to set up the touchscreen. In this tutorial, I will explain how to configure the XPT2046 touchscreen chip on a Raspberry Pi 4 to use alongside the TFT ILI9341 display.
I'm using the following hardware and software setup for this tutorial::
- Hardware: Raspberry Pi 4
- Kernel Version: 6.12.47+rpt-rpi-v8
- Display: TFT ILI9341 3.2" with touchscreen
- libdrm-dev: 2.4.123-1
- lvgl: v9.4.0
- lv_port_linux
Wiring the touchscreen
The table below shows the wiring connections for the touchscreen chip. Because the TFT display is already using the SPI0 bus, we need to use SPI1 for the touchscreen communication.
| XPT2046 | GPIO | Pin |
|---|---|---|
| T_CLK (SCLK) | GPIO 21 | 40 |
| T_CS (CS) | GPIO 18 | 12, |
| T_DIN (MOSI) | GPIO 20 | 38 |
| T_DO (MISO) | GPIO 19 | 35 |
| T_IRQ | GPIO 23 | 16 |
XPT2046 driver setup
For this step, you need the Device Tree Compiler, which you can install using the following command:
sudo apt-get install -y device-tree-compiler
Write and compile devicetree overlay
write the following Device Tree Overlay to set up the pins for the driver.
// Enable XPT2046 touchscreen driver
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2835";
// Configure the GPIO 23 as input for the pendown interrupt pin:
fragment@0 {
target = <&gpio>;
__overlay__ {
penirq_pin: penirq_pin {
brcm,pins = <23>;
brcm,function = <0>;
brcm,pull = <2>;
};
};
};
fragment@1 {
target = <&spi1>;
__overlay__ {
/* needed to avoid dtc warning */
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
xpt2046@0 {
compatible = "ti,tsc2046";
reg = <0>; /* CS0 */
spi-max-frequency = <1000000>;
pinctrl-names = "default";
pinctrl-0 = <&penirq_pin>;
interrupts = <23 2>;
interrupt-parent = <&gpio>;
pendown-gpio = <&gpio 23 1>;
ti,x-min = /bits/ 16 <0>;
ti,y-min = /bits/ 16 <0>;
ti,x-max = /bits/ 16 <3900>;
ti,y-max = /bits/ 16 <3900>;
ti,x-plate-ohms = /bits/ 16 <60>;
ti,pressure-max = /bits/ 16 <255>;
touchscreen-inverted-x;
touchscreen-inverted-y;
touchscreen-swapped-x-y;
wakeup-source;
};
};
};
};
Install devicetree overlay
Execute the following command to compile your Device Tree Overlay.
dtc -@ -Hepapr -I dts -O dtb -o xpt2046-spi.dtbo xpt2046-spi-overlay.dts
Move the .dtbo file to the /boot/firmware/overlays directory. Then, modify /boot/firmware/config.txt to enable the Device Tree Overlay, which will load the driver.
...
# Uncomment some or all of these to enable the optional hardware interfaces
#dtparam=i2c_arm=on
#dtparam=i2s=on
dtparam=spi=on
dtoverlay=spi0-cs
dtoverlay=spi1-1cs
...
# Additional overlays and parameters are documented
# /boot/firmware/overlays/README
dtoverlay=ili9341-spi
dtoverlay=xpt2046-spi
It is important to note that we enabled CS for SPI1. After saving the changes, you must reboot the Raspberry Pi to load the driver.
Testing the driver with evtest
To receive input events from the touchscreen, we will install the evtest tool using the following command:
sudo apt install libevdev-dev
Run evtest to find the device path for the touchscreen:
sudo evtest
The tool lists the available input devices and prompts you to select one. In my case, the touchscreen is option 0, but keep in mind this number might change after rebooting the Raspberry Pi:
No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event0: ADS7846 Touchscreen
/dev/input/event1: vc4-hdmi-0
/dev/input/event2: vc4-hdmi-0 HDMI Jack
/dev/input/event3: vc4-hdmi-1
/dev/input/event4: vc4-hdmi-1 HDMI Jack
Select the device event number [0-4]:
Once you select the correct option for the touchscreen, tap the TFT display to see the input events on your terminal:
Input driver version is 1.0.1
Input device ID: bus 0x1c vendor 0x0 product 0x1ea6 version 0x0
Input device name: "ADS7846 Touchscreen"
Supported events:
Event type 0 (EV_SYN)
Event type 1 (EV_KEY)
Event code 330 (BTN_TOUCH)
Event type 3 (EV_ABS)
Event code 0 (ABS_X)
Value 628
Min 0
Max 3900
Event code 1 (ABS_Y)
Value 1049
Min 0
Max 3900
Event code 24 (ABS_PRESSURE)
Value 0
Min 0
Max 255
Properties:
Testing ... (interrupt to exit)
Event: time 1774283976.142917, type 1 (EV_KEY), code 330 (BTN_TOUCH), value 1
Event: time 1774283976.142917, type 3 (EV_ABS), code 0 (ABS_X), value 2656
Event: time 1774283976.142917, type 3 (EV_ABS), code 1 (ABS_Y), value 1516
Event: time 1774283976.142917, type 3 (EV_ABS), code 24 (ABS_PRESSURE), value 121
Event: time 1774283976.142917, -------------- SYN_REPORT ------------
Event: time 1774283976.154913, type 3 (EV_ABS), code 0 (ABS_X), value 2645
Event: time 1774283976.154913, type 3 (EV_ABS), code 1 (ABS_Y), value 1497
Event: time 1774283976.154913, -------------- SYN_REPORT ------------
Configuring the touchscreen in LVGL
Here is how to set up the touchscreen device in LVGL. For this example, I am using the same demo from my previous article, Running an LVGL GUI with the DRM Driver on Raspberry Pi.
Confirm that the evdev driver is enabled in the lv_conf.defaults file:
LV_USE_EVDEV 1
The LVGL demo enables widget scrolling by default. You can disable this behavior in the main.c file:
/*Create a Demo*/
lv_demo_widgets();
//lv_demo_widgets_start_slideshow();
The following video shows the LVGL demo running on the display with touchscreen support: