Installing the driver for the XPT2046 touchscreen chip on a Raspberry Pi

Posted by Felipe Arturo López Bonilla on

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: