3.5 in display - dwilson2547/wiki_demo GitHub Wiki

The ST7796U is a popular TFT LCD controller often found in 3.5" and 4" touch screens. Here's how to interface it with your Raspberry Pi:

Hardware Connection

The ST7796U displays typically connect via SPI. You'll need to connect these pins:

Display to Raspberry Pi GPIO:

  • VCC → 5V (Pin 2) or 3.3V (Pin 1) - check your display specs
  • GND → Ground (Pin 6)
  • CS → GPIO 8 (Pin 24) - SPI CE0
  • RESET → GPIO 25 (Pin 22)
  • DC/RS → GPIO 24 (Pin 18) - Data/Command
  • SDI/MOSI → GPIO 10 (Pin 19) - SPI MOSI
  • SCK → GPIO 11 (Pin 23) - SPI Clock
  • LED → 3.3V (Pin 1) - Backlight
  • SDO/MISO → GPIO 9 (Pin 21) - SPI MISO (if available)

For touch functionality:

  • T_CLK → GPIO 11 (Pin 23)
  • T_CS → GPIO 7 (Pin 26)
  • T_DIN → GPIO 10 (Pin 19)
  • T_DO → GPIO 9 (Pin 21)
  • T_IRQ → GPIO 22 (Pin 15)

Software Setup

Enable SPI

sudo raspi-config

Navigate to "Interfacing Options" → "SPI" → "Yes"

Install Required Packages

sudo apt update
sudo apt install python3-pip python3-pil python3-numpy
pip3 install spidev RPi.GPIO pillow

Configure the Display

Create or edit /boot/config.txt:

sudo nano /boot/config.txt

Add these lines:

# Enable SPI
dtparam=spi=on

# ST7796U Display Configuration
dtoverlay=spi1-3cs
dtoverlay=st7796u,rotate=90,speed=32000000,fps=60

For some displays, you might need a custom device tree overlay. Create /boot/overlays/st7796u-overlay.dts:

/dts-v1/;
/plugin/;

/ {
    compatible = "brcm,bcm2835";
    
    fragment@0 {
        target = <&spi0>;
        __overlay__ {
            status = "okay";
            
            st7796u@0{
                compatible = "sitronix,st7796u";
                reg = <0>;
                pinctrl-names = "default";
                
                spi-max-frequency = <32000000>;
                rotate = <90>;
                fps = <60>;
                buswidth = <8>;
                reset-gpios = <&gpio 25 0>;
                dc-gpios = <&gpio 24 0>;
                debug = <0>;
            };
        };
    };
};

Python Control Example

Here's basic Python code to control the display:

import spidev
import RPi.GPIO as GPIO
import time
from PIL import Image, ImageDraw, ImageFont
import numpy as np

class ST7796U:
    def __init__(self):
        # GPIO pins
        self.RST_PIN = 25
        self.DC_PIN = 24
        self.CS_PIN = 8
        
        # Display dimensions
        self.width = 480
        self.height = 320
        
        # Initialize SPI
        self.spi = spidev.SpiDev()
        self.spi.open(0, 0)  # Bus 0, Device 0
        self.spi.max_speed_hz = 32000000
        self.spi.mode = 0
        
        # Initialize GPIO
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(self.RST_PIN, GPIO.OUT)
        GPIO.setup(self.DC_PIN, GPIO.OUT)
        GPIO.setup(self.CS_PIN, GPIO.OUT)
        
        self.reset()
        self.init_display()
    
    def reset(self):
        """Hardware reset"""
        GPIO.output(self.RST_PIN, GPIO.HIGH)
        time.sleep(0.1)
        GPIO.output(self.RST_PIN, GPIO.LOW)
        time.sleep(0.1)
        GPIO.output(self.RST_PIN, GPIO.HIGH)
        time.sleep(0.1)
    
    def write_command(self, cmd):
        """Send command to display"""
        GPIO.output(self.DC_PIN, GPIO.LOW)  # Command mode
        GPIO.output(self.CS_PIN, GPIO.LOW)
        self.spi.xfer2([cmd])
        GPIO.output(self.CS_PIN, GPIO.HIGH)
    
    def write_data(self, data):
        """Send data to display"""
        GPIO.output(self.DC_PIN, GPIO.HIGH)  # Data mode
        GPIO.output(self.CS_PIN, GPIO.LOW)
        if isinstance(data, int):
            self.spi.xfer2([data])
        else:
            self.spi.xfer2(data)
        GPIO.output(self.CS_PIN, GPIO.HIGH)
    
    def init_display(self):
        """Initialize ST7796U display"""
        # Basic initialization sequence
        self.write_command(0x01)  # Software reset
        time.sleep(0.15)
        
        self.write_command(0x11)  # Sleep out
        time.sleep(0.15)
        
        self.write_command(0x3A)  # Pixel format
        self.write_data(0x55)     # 16-bit color
        
        self.write_command(0x36)  # Memory access control
        self.write_data(0x48)     # Rotation setting
        
        self.write_command(0x29)  # Display on
        time.sleep(0.15)
    
    def set_window(self, x0, y0, x1, y1):
        """Set drawing window"""
        self.write_command(0x2A)  # Column address set
        self.write_data([x0 >> 8, x0 & 0xFF, x1 >> 8, x1 & 0xFF])
        
        self.write_command(0x2B)  # Row address set
        self.write_data([y0 >> 8, y0 & 0xFF, y1 >> 8, y1 & 0xFF])
        
        self.write_command(0x2C)  # Memory write
    
    def display_image(self, image):
        """Display PIL image"""
        if image.size != (self.width, self.height):
            image = image.resize((self.width, self.height))
        
        # Convert to RGB565
        rgb_image = image.convert('RGB')
        pixels = np.array(rgb_image)
        
        # Convert RGB888 to RGB565
        r = (pixels[:,:,0] >> 3) << 11
        g = (pixels[:,:,1] >> 2) << 5
        b = pixels[:,:,2] >> 3
        rgb565 = r | g | b
        
        # Convert to bytes
        data = []
        for row in rgb565:
            for pixel in row:
                data.extend([pixel >> 8, pixel & 0xFF])
        
        self.set_window(0, 0, self.width-1, self.height-1)
        self.write_data(data)
    
    def fill_screen(self, color):
        """Fill screen with solid color (RGB565)"""
        data = [color >> 8, color & 0xFF] * (self.width * self.height)
        self.set_window(0, 0, self.width-1, self.height-1)
        self.write_data(data)
    
    def cleanup(self):
        """Clean up GPIO"""
        GPIO.cleanup()

# Usage example
if __name__ == "__main__":
    try:
        display = ST7796U()
        
        # Fill screen with red
        display.fill_screen(0xF800)  # Red in RGB565
        time.sleep(2)
        
        # Display an image
        img = Image.new('RGB', (480, 320), color='blue')
        draw = ImageDraw.Draw(img)
        draw.text((50, 50), "Hello Raspberry Pi!", fill='white')
        display.display_image(img)
        
        time.sleep(5)
        
    except KeyboardInterrupt:
        pass
    finally:
        display.cleanup()

Using Existing Libraries

For easier setup, try using luma.lcd:

pip3 install luma.lcd
from luma.core.interface.serial import spi
from luma.core.render import canvas
from luma.lcd.device import st7796
from PIL import ImageFont

# Create interface and device
serial = spi(port=0, device=0, gpio_DC=24, gpio_RST=25)
device = st7796(serial, width=480, height=320, rotate=1)

# Draw on the display
with canvas(device) as draw:
    draw.rectangle(device.bounding_box, outline="white", fill="black")
    draw.text((50, 50), "Hello World!", fill="white")

Troubleshooting

  • Verify SPI is enabled: lsmod | grep spi
  • Check connections with a multimeter
  • Try different SPI speeds (reduce from 32MHz if unstable)
  • Some displays need 5V power, others work with 3.3V
  • Touch functionality may require additional calibration

The exact initialization sequence might vary depending on your specific ST7796U display module manufacturer, so you may need to adjust the commands based on your display's datasheet.

⚠️ **GitHub.com Fallback** ⚠️