XPT2046 / ILI9341 SPI LCD (Raspberry Pi style) on Orange Pi Zero (Allwinner H3), Armbian OS

2023/11/20

I have a touchscreen SPI LCD that I got for a Raspberry Pi but don’t use for much. I got it working on an Orange Pi Zero (Allwinner H3) SBC. Yay!

This is on the mainline kernel version, 6.1.53. Most complete guides that I find out there for getting these SPI LCDs working on Armbian will use older mechanisms that don’t work with more current kernel versions, AFAIK.

This was adapted from this person’s device tree for a NanoPi SBC. The pinouts are significantly different, and the SPI0 bus is not exposed, so many changes were necessary, but the basic structure is the same.

This may require the spi-add-cs1 device tree overlay included with Armbian in order to work - it uses an emulated second chip select on the same SPI bus. The init stanza may also be different for your LCD - I’m not sure.

For reference, my specific LCD seems to be an unbranded clone of the 3.5 inch Waveshare SPI touchscreen.

/dts-v1/;
/plugin/;
 
/ {
    compatible = "allwinner,sun8i-h3";
 
    fragment@0 {
        target = <&pio>;
        __overlay__ {
            spi1_cs0: spi1_cs0 {
                pins = "PA13"; 
                function = "gpio_out";
                output-high;
            };
 
            spi1_cs1: spi1_cs1 {
                pins = "PA10"; 
                function = "gpio_out";
                output-high;
            };
 
            opiz_display_pins: opiz_display_pins {
                pins = "PA18", "PA2", "PA6";
                function = "gpio_out";
            };
 
            ads7846_pins: ads7846_pins {
                pins = "PA1";
                function = "irq";
            };
 
 
        };
    };
 
    fragment@1 {
        target = <&spi1>;
        __overlay__ {
            pinctrl-names = "default", "default";
            pinctrl-1 = <&spi1_cs1>;
            cs-gpios = <0>, <&pio 0 10 0>; /* PA10 */
        };
    };
 
    fragment@2 {
        target = <&spi1>;
        __overlay__ {
            #address-cells = <1>;
            #size-cells = <0>;
            status = "okay";
 
            pinctrl-names = "default", "default";
            cs-gpios= <&pio 0 13 0>, <&pio 0 10 1>;
 
            opizdisplay: opiz-display@1 {
                pinctrl-1 = <&spi1_cs0>;
                reg = <0>; /* Chip Select 0 */
                compatible = "ilitek,ili9341";
                spi-max-frequency = <16000000>;
                status = "okay";
                pinctrl-names = "default";
                pinctrl-0 = <&opiz_display_pins>;
                rotate = <270>;
                bgr = <0>;
                fps = <30>;
		width = <320>;
		height = <480>;
                buswidth = <8>;
		regwidth = <16>;
                dc-gpios = <&pio 0 18 0>;      /* PIN_22  GPIO A18 > */
                reset-gpios = <&pio 0 2 1 >; /*    GPIO A2> */
                debug=<4>;
                init = <0x10000b0 0x00
                        0x1000011
			0x20000ff
			0x100003a 0x55
			0x1000036 0x28
			0x10000c2 0x44
			0x10000c5 0x00 0x00 0x00 0x00
			0x10000e0 0x0f 0x1f 0x1c 0x0c 0x0f 0x08 0x48 0x98 0x37 0x0a 0x13 0x04 0x11 0x0d 0x00
			0x10000e1 0x0f 0x32 0x2e 0x0b 0x0d 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00
			0x10000e2 0x0f 0x32 0x2e 0x0b 0x0d 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00
			0x1000036 0x28
			0x1000011
			0x1000029>;
 
            };
 
            ads7846: ads7846@1 {
                reg = <1>; /* Chip Select 1 */
                compatible = "ti,ads7846";
                spi-max-frequency = <1000000>;
                status = "okay";
                pinctrl-2=<&spi1_cs1 &spi1_cs1>;
                pinctrl-names = "default";
                pinctrl-3 = <&ads7846_pins>;
                interrupt-parent = <&pio>;
                interrupts = <0 1 2>; /* PA1 IRQ_TYPE_EDGE_FALLING */
                pendown-gpio = <&pio 0 1 0>; /* PA1 */
                /* driver defaults, optional */
                ti,x-min = /bits/ 16 <0>;
                ti,y-min = /bits/ 16 <0>;
                ti,x-max = /bits/ 16 <0x0FFF>;
                ti,y-max = /bits/ 16 <0x0FFF>;
                ti,pressure-min = /bits/ 16 <0>;
                ti,pressure-max = /bits/ 16 <0xFFFF>;
                ti,x-plate-ohms = /bits/ 16 <400>;
            };
 
        };
 
    };
};

This basic device tree structure will likely work for any other Allwinner H2+ or H3 board with an SPI bus exposed.

Here is the reference I made for properly mapping the pins - most of them were pretty straightforward, though I did have to rearrange a couple of GPIOs to match the pinout of the LCD, so it would work when mounted directly to the Orange Pi, and I didn’t have to do any weird jumper wire business.

NanoPi NEO wiring - 
   PC2  <-->  SCK       & T_CLK
   PC1  <-->  SDO<MISO> & T_DO
   PC0  <-->  SDI<MOSI> & T_DIN
   PA1  <-->  DC
   PG8  <-->  RESET
   PC3  <-->  CS
   PA3  <-->  T_CS
   PG9  <-->  T_IRG
 
OPi Zero wiring:
   PA14  <-->  SCK       & T_CLK
   PA16  <-->  SDO<MISO> & T_DO
   PA15  <-->  SDI<MOSI> & T_DIN
   PA18  <-->  DC
   PA2  <-->  RESET
   PA13  <-->  CS
   PA10  <-->  T_CS
   PA1  <-->  T_IRG
 */
>> Home