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:
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)
sudo raspi-config
Navigate to "Interfacing Options" → "SPI" → "Yes"
sudo apt update
sudo apt install python3-pip python3-pil python3-numpy
pip3 install spidev RPi.GPIO pillow
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>;
};
};
};
};
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()
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")
- 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.