显示设备 #

一、显示设备概述 #

1.1 显示设备分类 #

text
┌─────────────────────────────────────────────────────────┐
│                    显示设备分类                          │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  按技术分类:                                            │
│  ├── LCD:液晶显示,成本低,功耗低                       │
│  ├── OLED:有机发光,对比度高,响应快                    │
│  ├── E-Ink:电子墨水,类纸显示,超低功耗                 │
│  └── TFT:薄膜晶体管,色彩丰富,刷新快                   │
│                                                         │
│  按接口分类:                                            │
│  ├── 并行接口:数据线多,速度快                          │
│  ├── I2C接口:线少,速度慢                               │
│  ├── SPI接口:速度适中,常用                             │
│  └── UART接口:简单,适合字符显示                        │
│                                                         │
└─────────────────────────────────────────────────────────┘

1.2 常见显示设备参数 #

设备 分辨率 接口 特点
LCD1602 16x2字符 I2C/并行 字符显示,简单
LCD2004 20x4字符 I2C/并行 字符显示,更大
OLED 0.96" 128x64 I2C/SPI 小巧,高对比度
OLED 1.3" 128x64 I2C/SPI SH1106驱动
TFT 2.4" 240x320 SPI 彩色,触摸
TFT 3.5" 480x320 SPI 彩色,触摸

二、LCD1602字符显示 #

2.1 I2C接口LCD1602 #

java
package com.example.display;

import com.pi4j.Pi4J;
import com.pi4j.io.i2c.I2C;
import com.pi4j.io.i2c.I2CConfig;

public class LCD1602 {

    private static final int LCD_CLEARDISPLAY = 0x01;
    private static final int LCD_RETURNHOME = 0x02;
    private static final int LCD_ENTRYMODESET = 0x04;
    private static final int LCD_DISPLAYCONTROL = 0x08;
    private static final int LCD_CURSORSHIFT = 0x10;
    private static final int LCD_FUNCTIONSET = 0x20;
    private static final int LCD_SETCGRAMADDR = 0x40;
    private static final int LCD_SETDDRAMADDR = 0x80;
    
    private static final int LCD_ENTRYLEFT = 0x02;
    private static final int LCD_ENTRYSHIFTINCREMENT = 0x01;
    
    private static final int LCD_DISPLAYON = 0x04;
    private static final int LCD_CURSOROFF = 0x00;
    private static final int LCD_BLINKOFF = 0x00;
    
    private static final int LCD_4BITMODE = 0x00;
    private static final int LCD_2LINE = 0x08;
    private static final int LCD_5x8DOTS = 0x00;
    
    private static final int LCD_BACKLIGHT = 0x08;
    private static final int LCD_NOBACKLIGHT = 0x00;
    
    private static final int ENABLE = 0x04;
    private static final int Rs = 0x01;
    
    private final I2C device;
    private int backlight = LCD_BACKLIGHT;
    
    public LCD1602(var pi4j, int bus, int address) {
        this.device = pi4j.create(I2C.newConfigBuilder(pi4j)
                .id("lcd1602")
                .name("LCD1602 Display")
                .bus(bus)
                .device(address)
                .build());
        
        initialize();
    }
    
    private void initialize() {
        try { Thread.sleep(50); } catch (InterruptedException e) {}
        
        write4Bits(0x03 << 4);
        try { Thread.sleep(5); } catch (InterruptedException e) {}
        
        write4Bits(0x03 << 4);
        try { Thread.sleep(1); } catch (InterruptedException e) {}
        
        write4Bits(0x03 << 4);
        write4Bits(0x02 << 4);
        
        sendCommand(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE);
        sendCommand(LCD_DISPLAYCONTROL | LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF);
        clear();
        sendCommand(LCD_ENTRYMODESET | LCD_ENTRYLEFT);
    }
    
    public void clear() {
        sendCommand(LCD_CLEARDISPLAY);
        try { Thread.sleep(2); } catch (InterruptedException e) {}
    }
    
    public void home() {
        sendCommand(LCD_RETURNHOME);
        try { Thread.sleep(2); } catch (InterruptedException e) {}
    }
    
    public void setCursor(int col, int row) {
        int[] rowOffsets = {0x00, 0x40, 0x14, 0x54};
        sendCommand(LCD_SETDDRAMADDR | (col + rowOffsets[row]));
    }
    
    public void display(boolean on) {
        if (on) {
            sendCommand(LCD_DISPLAYCONTROL | LCD_DISPLAYON);
        } else {
            sendCommand(LCD_DISPLAYCONTROL);
        }
    }
    
    public void cursor(boolean show) {
        if (show) {
            sendCommand(LCD_DISPLAYCONTROL | LCD_DISPLAYON | 0x02);
        } else {
            sendCommand(LCD_DISPLAYCONTROL | LCD_DISPLAYON);
        }
    }
    
    public void blink(boolean on) {
        if (on) {
            sendCommand(LCD_DISPLAYCONTROL | LCD_DISPLAYON | 0x01);
        } else {
            sendCommand(LCD_DISPLAYCONTROL | LCD_DISPLAYON);
        }
    }
    
    public void scrollDisplayLeft() {
        sendCommand(LCD_CURSORSHIFT | 0x08);
    }
    
    public void scrollDisplayRight() {
        sendCommand(LCD_CURSORSHIFT | 0x08 | 0x04);
    }
    
    public void leftToRight() {
        sendCommand(LCD_ENTRYMODESET | LCD_ENTRYLEFT);
    }
    
    public void rightToLeft() {
        sendCommand(LCD_ENTRYMODESET);
    }
    
    public void backlight(boolean on) {
        backlight = on ? LCD_BACKLIGHT : LCD_NOBACKLIGHT;
        device.write(backlight);
    }
    
    public void write(String text) {
        for (char c : text.toCharArray()) {
            sendChar(c);
        }
    }
    
    public void writeLine(int line, String text) {
        setCursor(0, line);
        
        int maxLen = 16;
        String padded = String.format("%-" + maxLen + "s", 
            text.length() > maxLen ? text.substring(0, maxLen) : text);
        
        write(padded);
    }
    
    public void createChar(int location, byte[] charmap) {
        location &= 0x07;
        sendCommand(LCD_SETCGRAMADDR | (location << 3));
        
        for (int i = 0; i < 8; i++) {
            sendChar((char) (charmap[i] & 0xFF));
        }
    }
    
    private void sendCommand(int cmd) {
        write4Bits(cmd & 0xF0);
        write4Bits((cmd << 4) & 0xF0);
    }
    
    private void sendChar(char c) {
        write4Bits((c & 0xF0) | Rs);
        write4Bits(((c << 4) & 0xF0) | Rs);
    }
    
    private void write4Bits(int value) {
        device.write(value | backlight);
        pulseEnable(value | backlight);
    }
    
    private void pulseEnable(int data) {
        device.write(data | ENABLE | backlight);
        try { Thread.sleep(0, 50000); } catch (InterruptedException e) {}
        device.write((data & ~ENABLE) | backlight);
        try { Thread.sleep(0, 50000); } catch (InterruptedException e) {}
    }
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        LCD1602 lcd = new LCD1602(pi4j, 1, 0x27);
        
        System.out.println("LCD1602显示测试");
        
        lcd.clear();
        lcd.writeLine(0, "Hello Java!");
        lcd.writeLine(1, "Embedded Dev");
        
        Thread.sleep(3000);
        
        lcd.scrollDisplayRight();
        Thread.sleep(500);
        lcd.scrollDisplayRight();
        Thread.sleep(500);
        
        lcd.clear();
        lcd.writeLine(0, "Counter:");
        
        for (int i = 0; i < 100; i++) {
            lcd.setCursor(0, 1);
            lcd.write(String.format("Count: %d", i));
            Thread.sleep(100);
        }
        
        lcd.clear();
        pi4j.shutdown();
    }
}

三、OLED显示 #

3.1 SSD1306 OLED驱动 #

java
package com.example.display;

import com.pi4j.Pi4J;
import com.pi4j.io.i2c.I2C;
import com.pi4j.io.i2c.I2CConfig;

public class SSD1306OLED {

    private static final int WIDTH = 128;
    private static final int HEIGHT = 64;
    private static final int PAGES = HEIGHT / 8;
    
    private static final int ADDRESS = 0x3C;
    
    private static final int COMMAND_MODE = 0x00;
    private static final int DATA_MODE = 0x40;
    
    private static final int DISPLAY_OFF = 0xAE;
    private static final int DISPLAY_ON = 0xAF;
    private static final int SET_DISPLAY_OFF = 0xA4;
    private static final int SET_DISPLAY_ON = 0xA5;
    private static final int NORMAL_DISPLAY = 0xA6;
    private static final int INVERT_DISPLAY = 0xA7;
    private static final int SET_CONTRAST = 0x81;
    private static final int SET_MULTIPLEX = 0xA8;
    private static final int SET_DISPLAY_OFFSET = 0xD3;
    private static final int SET_START_LINE = 0x40;
    private static final int SET_SEGMENT_REMAP = 0xA0;
    private static final int SET_COM_SCAN_DIR = 0xC0;
    private static final int SET_COM_PINS = 0xDA;
    private static final int SET_VCOM_DESELECT = 0xDB;
    private static final int SET_PRECHARGE = 0xD9;
    private static final int SET_CLK_DIV = 0xD5;
    private static final int SET_CHARGE_PUMP = 0x8D;
    private static final int SET_MEMORY_MODE = 0x20;
    private static final int SET_COLUMN_ADDR = 0x21;
    private static final int SET_PAGE_ADDR = 0x22;
    
    private final I2C device;
    private final byte[][] buffer = new byte[PAGES][WIDTH];
    
    public SSD1306OLED(var pi4j, int bus) {
        this.device = pi4j.create(I2C.newConfigBuilder(pi4j)
                .id("ssd1306")
                .name("SSD1306 OLED Display")
                .bus(bus)
                .device(ADDRESS)
                .build());
        
        initialize();
    }
    
    private void initialize() {
        sendCommand(DISPLAY_OFF);
        sendCommand(SET_CLK_DIV, 0x80);
        sendCommand(SET_MULTIPLEX, 0x3F);
        sendCommand(SET_DISPLAY_OFFSET, 0x00);
        sendCommand(SET_START_LINE | 0x00);
        sendCommand(SET_CHARGE_PUMP, 0x14);
        sendCommand(SET_MEMORY_MODE, 0x00);
        sendCommand(SET_SEGMENT_REMAP | 0x01);
        sendCommand(SET_COM_SCAN_DIR | 0x08);
        sendCommand(SET_COM_PINS, 0x12);
        sendCommand(SET_CONTRAST, 0xCF);
        sendCommand(SET_PRECHARGE, 0xF1);
        sendCommand(SET_VCOM_DESELECT, 0x40);
        sendCommand(SET_DISPLAY_OFF);
        sendCommand(NORMAL_DISPLAY);
        sendCommand(DISPLAY_ON);
        
        clear();
        display();
    }
    
    public void clear() {
        for (int page = 0; page < PAGES; page++) {
            for (int col = 0; col < WIDTH; col++) {
                buffer[page][col] = 0;
            }
        }
    }
    
    public void fill() {
        for (int page = 0; page < PAGES; page++) {
            for (int col = 0; col < WIDTH; col++) {
                buffer[page][col] = (byte) 0xFF;
            }
        }
    }
    
    public void setPixel(int x, int y, boolean on) {
        if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) return;
        
        int page = y / 8;
        int bit = y % 8;
        
        if (on) {
            buffer[page][x] |= (1 << bit);
        } else {
            buffer[page][x] &= ~(1 << bit);
        }
    }
    
    public void drawLine(int x0, int y0, int x1, int y1) {
        int dx = Math.abs(x1 - x0);
        int dy = Math.abs(y1 - y0);
        int sx = x0 < x1 ? 1 : -1;
        int sy = y0 < y1 ? 1 : -1;
        int err = dx - dy;
        
        while (true) {
            setPixel(x0, y0, true);
            
            if (x0 == x1 && y0 == y1) break;
            
            int e2 = 2 * err;
            if (e2 > -dy) {
                err -= dy;
                x0 += sx;
            }
            if (e2 < dx) {
                err += dx;
                y0 += sy;
            }
        }
    }
    
    public void drawRect(int x, int y, int w, int h) {
        drawLine(x, y, x + w - 1, y);
        drawLine(x, y + h - 1, x + w - 1, y + h - 1);
        drawLine(x, y, x, y + h - 1);
        drawLine(x + w - 1, y, x + w - 1, y + h - 1);
    }
    
    public void fillRect(int x, int y, int w, int h) {
        for (int i = x; i < x + w; i++) {
            for (int j = y; j < y + h; j++) {
                setPixel(i, j, true);
            }
        }
    }
    
    public void drawCircle(int x0, int y0, int r) {
        int f = 1 - r;
        int ddF_x = 1;
        int ddF_y = -2 * r;
        int x = 0;
        int y = r;
        
        setPixel(x0, y0 + r, true);
        setPixel(x0, y0 - r, true);
        setPixel(x0 + r, y0, true);
        setPixel(x0 - r, y0, true);
        
        while (x < y) {
            if (f >= 0) {
                y--;
                ddF_y += 2;
                f += ddF_y;
            }
            x++;
            ddF_x += 2;
            f += ddF_x;
            
            setPixel(x0 + x, y0 + y, true);
            setPixel(x0 - x, y0 + y, true);
            setPixel(x0 + x, y0 - y, true);
            setPixel(x0 - x, y0 - y, true);
            setPixel(x0 + y, y0 + x, true);
            setPixel(x0 - y, y0 + x, true);
            setPixel(x0 + y, y0 - x, true);
            setPixel(x0 - y, y0 - x, true);
        }
    }
    
    public void display() {
        sendCommand(SET_COLUMN_ADDR, 0, WIDTH - 1);
        sendCommand(SET_PAGE_ADDR, 0, PAGES - 1);
        
        for (int page = 0; page < PAGES; page++) {
            byte[] data = new byte[WIDTH + 1];
            data[0] = DATA_MODE;
            System.arraycopy(buffer[page], 0, data, 1, WIDTH);
            device.write(data);
        }
    }
    
    public void setContrast(int contrast) {
        sendCommand(SET_CONTRAST, contrast);
    }
    
    public void invert(boolean invert) {
        sendCommand(invert ? INVERT_DISPLAY : NORMAL_DISPLAY);
    }
    
    private void sendCommand(int... commands) {
        byte[] data = new byte[commands.length + 1];
        data[0] = COMMAND_MODE;
        for (int i = 0; i < commands.length; i++) {
            data[i + 1] = (byte) commands[i];
        }
        device.write(data);
    }
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        SSD1306OLED oled = new SSD1306OLED(pi4j, 1);
        
        System.out.println("SSD1306 OLED显示测试");
        
        oled.clear();
        oled.drawLine(0, 0, 127, 63);
        oled.drawLine(127, 0, 0, 63);
        oled.display();
        Thread.sleep(2000);
        
        oled.clear();
        oled.drawRect(10, 10, 50, 30);
        oled.fillCircle(90, 32, 20);
        oled.display();
        Thread.sleep(2000);
        
        oled.clear();
        oled.fillRect(0, 0, 128, 64);
        oled.display();
        Thread.sleep(1000);
        
        oled.clear();
        oled.display();
        
        pi4j.shutdown();
    }
}

3.2 字体渲染 #

java
package com.example.display;

public class Font5x8 {
    
    public static final byte[][][] FONT = {
        {{0x00, 0x00, 0x00, 0x00, 0x00}, 0},
        {{0x00, 0x00, 0x5F, 0x00, 0x00}, 5},
        {{0x00, 0x07, 0x00, 0x07, 0x00}, 6},
        {{0x14, 0x7F, 0x14, 0x7F, 0x14}, 6},
        {{0x24, 0x2A, 0x7F, 0x2A, 0x12}, 6},
        {{0x23, 0x13, 0x08, 0x64, 0x62}, 6},
        {{0x36, 0x49, 0x55, 0x22, 0x50}, 6},
        {{0x00, 0x05, 0x03, 0x00, 0x00}, 4},
        {{0x00, 0x1C, 0x22, 0x41, 0x00}, 5},
        {{0x00, 0x41, 0x22, 0x1C, 0x00}, 5},
        {{0x08, 0x2A, 0x1C, 0x2A, 0x08}, 6},
        {{0x08, 0x08, 0x3E, 0x08, 0x08}, 6},
        {{0x00, 0x50, 0x30, 0x00, 0x00}, 4},
        {{0x08, 0x08, 0x08, 0x08, 0x08}, 6},
        {{0x00, 0x60, 0x60, 0x00, 0x00}, 3},
        {{0x20, 0x10, 0x08, 0x04, 0x02}, 6},
        {{0x3E, 0x51, 0x49, 0x45, 0x3E}, 6},
        {{0x00, 0x42, 0x7F, 0x40, 0x00}, 5},
        {{0x42, 0x61, 0x51, 0x49, 0x46}, 6},
        {{0x21, 0x41, 0x45, 0x4B, 0x31}, 6},
        {{0x18, 0x14, 0x12, 0x7F, 0x10}, 6},
        {{0x27, 0x45, 0x45, 0x45, 0x39}, 6},
        {{0x3C, 0x4A, 0x49, 0x49, 0x30}, 6},
        {{0x01, 0x71, 0x09, 0x05, 0x03}, 6},
        {{0x36, 0x49, 0x49, 0x49, 0x36}, 6},
        {{0x06, 0x49, 0x49, 0x29, 0x1E}, 6},
        {{0x00, 0x36, 0x36, 0x00, 0x00}, 4},
        {{0x00, 0x56, 0x36, 0x00, 0x00}, 4},
        {{0x00, 0x08, 0x14, 0x22, 0x41}, 6},
        {{0x14, 0x14, 0x14, 0x14, 0x14}, 6},
        {{0x41, 0x22, 0x14, 0x08, 0x00}, 6},
        {{0x02, 0x01, 0x51, 0x09, 0x06}, 6},
        {{0x32, 0x49, 0x79, 0x41, 0x3E}, 6},
        {{0x7E, 0x11, 0x11, 0x11, 0x7E}, 6},
        {{0x7F, 0x49, 0x49, 0x49, 0x36}, 6},
        {{0x3E, 0x41, 0x41, 0x41, 0x22}, 6},
        {{0x7F, 0x41, 0x41, 0x22, 0x1C}, 6},
        {{0x7F, 0x49, 0x49, 0x49, 0x41}, 6},
        {{0x7F, 0x09, 0x09, 0x09, 0x01}, 6},
        {{0x3E, 0x41, 0x41, 0x51, 0x32}, 6},
        {{0x7F, 0x08, 0x08, 0x08, 0x7F}, 6},
        {{0x00, 0x41, 0x7F, 0x41, 0x00}, 5},
        {{0x20, 0x40, 0x41, 0x3F, 0x01}, 6},
        {{0x7F, 0x08, 0x14, 0x22, 0x41}, 6},
        {{0x7F, 0x40, 0x40, 0x40, 0x40}, 5},
        {{0x7F, 0x02, 0x04, 0x02, 0x7F}, 6},
        {{0x7F, 0x04, 0x08, 0x10, 0x7F}, 6},
        {{0x3E, 0x41, 0x41, 0x41, 0x3E}, 6},
        {{0x7F, 0x09, 0x09, 0x09, 0x06}, 6},
        {{0x3E, 0x41, 0x51, 0x21, 0x5E}, 6},
        {{0x7F, 0x09, 0x19, 0x29, 0x46}, 6},
        {{0x26, 0x49, 0x49, 0x49, 0x32}, 6},
        {{0x01, 0x01, 0x7F, 0x01, 0x01}, 5},
        {{0x3F, 0x40, 0x40, 0x40, 0x3F}, 6},
        {{0x1F, 0x20, 0x40, 0x20, 0x1F}, 6},
        {{0x3F, 0x40, 0x38, 0x40, 0x3F}, 6},
        {{0x63, 0x14, 0x08, 0x14, 0x63}, 6},
        {{0x03, 0x04, 0x78, 0x04, 0x03}, 6},
        {{0x61, 0x51, 0x49, 0x45, 0x43}, 6},
        {{0x00, 0x00, 0x7F, 0x41, 0x41}, 5},
        {{0x02, 0x04, 0x08, 0x10, 0x20}, 6},
        {{0x41, 0x41, 0x7F, 0x00, 0x00}, 5},
        {{0x04, 0x02, 0x01, 0x02, 0x04}, 6},
        {{0x40, 0x40, 0x40, 0x40, 0x40}, 5},
        {{0x00, 0x01, 0x02, 0x04, 0x00}, 3},
        {{0x20, 0x54, 0x54, 0x54, 0x78}, 6},
        {{0x7F, 0x48, 0x44, 0x44, 0x38}, 6},
        {{0x38, 0x44, 0x44, 0x44, 0x20}, 6},
        {{0x38, 0x44, 0x44, 0x48, 0x7F}, 6},
        {{0x38, 0x54, 0x54, 0x54, 0x18}, 6},
        {{0x08, 0x7E, 0x09, 0x01, 0x02}, 6},
        {{0x0C, 0x52, 0x52, 0x52, 0x3E}, 6},
        {{0x7F, 0x08, 0x04, 0x04, 0x78}, 6},
        {{0x00, 0x44, 0x7D, 0x40, 0x00}, 5},
        {{0x20, 0x40, 0x44, 0x3D, 0x00}, 5},
        {{0x00, 0x00, 0x00, 0x00, 0x00}, 0}
    };
    
    public static byte[] getChar(char c) {
        int index = c - 32;
        if (index < 0 || index >= FONT.length) {
            index = FONT.length - 1;
        }
        return FONT[index][0];
    }
    
    public static int getWidth(char c) {
        int index = c - 32;
        if (index < 0 || index >= FONT.length) {
            return 0;
        }
        return FONT[index][1];
    }
}

四、TFT彩色显示屏 #

4.1 ILI9341 TFT驱动 #

java
package com.example.display;

import com.pi4j.Pi4J;
import com.pi4j.io.spi.Spi;
import com.pi4j.io.spi.SpiMode;
import com.pi4j.io.gpio.digital.*;

public class ILI9341TFT {

    private static final int WIDTH = 240;
    private static final int HEIGHT = 320;
    
    private static final int NOP = 0x00;
    private static final int SWRESET = 0x01;
    private static final int RDDID = 0x04;
    private static final int RDDST = 0x09;
    
    private static final int SLPIN = 0x10;
    private static final int SLPOUT = 0x11;
    private static final int PTLON = 0x12;
    private static final int NORON = 0x13;
    
    private static final int RDMODE = 0x0A;
    private static final int RDMADCTL = 0x0B;
    private static final int RDPIXFMT = 0x0C;
    private static final int RDIMGFMT = 0x0A;
    private static final int RDSELFDIAG = 0x0F;
    
    private static final int INVOFF = 0x20;
    private static final int INVON = 0x21;
    private static final int GAMMASET = 0x26;
    private static final int DISPOFF = 0x28;
    private static final int DISPON = 0x29;
    
    private static final int CASET = 0x2A;
    private static final int PASET = 0x2B;
    private static final int RAMWR = 0x2C;
    private static final int RAMRD = 0x2E;
    
    private static final int PTLAR = 0x30;
    private static final int VSCRDEF = 0x33;
    private static final int MADCTL = 0x36;
    private static final int VSCRSADD = 0x37;
    private static final int PIXFMT = 0x3A;
    
    private static final int FRMCTR1 = 0xB1;
    private static final int FRMCTR2 = 0xB2;
    private static final int FRMCTR3 = 0xB3;
    private static final int INVCTR = 0xB4;
    private static final int DFUNCTR = 0xB6;
    
    private static final int PWCTR1 = 0xC0;
    private static final int PWCTR2 = 0xC1;
    private static final int PWCTR3 = 0xC2;
    private static final int PWCTR4 = 0xC3;
    private static final int PWCTR5 = 0xC4;
    private static final int VMCTR1 = 0xC5;
    private static final int VMCTR2 = 0xC7;
    
    private static final int RDID1 = 0xDA;
    private static final int RDID2 = 0xDB;
    private static final int RDID3 = 0xDC;
    private static final int RDID4 = 0xDD;
    
    private static final int GMCTRP1 = 0xE0;
    private static final int GMCTRN1 = 0xE1;
    
    private final Spi spi;
    private final DigitalOutput dc;
    private final DigitalOutput rst;
    
    public ILI9341TFT(var pi4j, int spiBus, int spiCs, int dcPin, int rstPin) {
        this.spi = pi4j.create(Spi.newConfigBuilder(pi4j)
                .id("ili9341")
                .name("ILI9341 TFT Display")
                .bus(spiBus)
                .chipSelect(spiCs)
                .mode(SpiMode.MODE_0)
                .baud(32000000)
                .build());
        
        this.dc = pi4j.create(DigitalOutput.newConfigBuilder(pi4j)
                .id("tft-dc")
                .name("TFT DC")
                .address(dcPin)
                .shutdown(DigitalState.LOW)
                .initial(DigitalState.LOW)
                .provider("pigpio-digital-output"));
        
        this.rst = pi4j.create(DigitalOutput.newConfigBuilder(pi4j)
                .id("tft-rst")
                .name("TFT RST")
                .address(rstPin)
                .shutdown(DigitalState.LOW)
                .initial(DigitalState.HIGH)
                .provider("pigpio-digital-output"));
        
        initialize();
    }
    
    private void initialize() {
        rst.low();
        try { Thread.sleep(10); } catch (InterruptedException e) {}
        rst.high();
        try { Thread.sleep(100); } catch (InterruptedException e) {}
        
        writeCommand(SWRESET);
        try { Thread.sleep(150); } catch (InterruptedException e) {}
        
        writeCommand(SLPOUT);
        try { Thread.sleep(500); } catch (InterruptedException e) {}
        
        writeCommand(PIXFMT);
        writeData(0x55);
        
        writeCommand(FRMCTR1);
        writeData(0x00, 0x1B);
        
        writeCommand(DFUNCTR);
        writeData(0x0A, 0xA2);
        
        writeCommand(PWCTR1);
        writeData(0x10, 0x10);
        
        writeCommand(PWCTR2);
        writeData(0x10);
        
        writeCommand(VMCTR1);
        writeData(0x3B, 0x00);
        
        writeCommand(VMCTR2);
        writeData(0x00);
        
        writeCommand(GMCTRP1);
        writeData(0x0F, 0x1F, 0x1C, 0x0C, 0x0F, 0x08, 0x48, 0x98,
                  0x37, 0x0A, 0x13, 0x04, 0x11, 0x0D, 0x00);
        
        writeCommand(GMCTRN1);
        writeData(0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75,
                  0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00);
        
        writeCommand(MADCTL);
        writeData(0x48);
        
        writeCommand(DISPON);
    }
    
    public void setWindow(int x0, int y0, int x1, int y1) {
        writeCommand(CASET);
        writeData((byte) (x0 >> 8), (byte) x0, (byte) (x1 >> 8), (byte) x1);
        
        writeCommand(PASET);
        writeData((byte) (y0 >> 8), (byte) y0, (byte) (y1 >> 8), (byte) y1);
        
        writeCommand(RAMWR);
    }
    
    public void fillScreen(int color) {
        fillRect(0, 0, WIDTH, HEIGHT, color);
    }
    
    public void fillRect(int x, int y, int w, int h, int color) {
        setWindow(x, y, x + w - 1, y + h - 1);
        
        byte hi = (byte) (color >> 8);
        byte lo = (byte) color;
        
        byte[] buffer = new byte[w * h * 2];
        for (int i = 0; i < buffer.length; i += 2) {
            buffer[i] = hi;
            buffer[i + 1] = lo;
        }
        
        dc.high();
        spi.transfer(buffer);
    }
    
    public void drawPixel(int x, int y, int color) {
        setWindow(x, y, x, y);
        dc.high();
        spi.transfer(new byte[]{(byte) (color >> 8), (byte) color});
    }
    
    public void drawLine(int x0, int y0, int x1, int y1, int color) {
        int dx = Math.abs(x1 - x0);
        int dy = Math.abs(y1 - y0);
        int sx = x0 < x1 ? 1 : -1;
        int sy = y0 < y1 ? 1 : -1;
        int err = dx - dy;
        
        while (true) {
            drawPixel(x0, y0, color);
            
            if (x0 == x1 && y0 == y1) break;
            
            int e2 = 2 * err;
            if (e2 > -dy) {
                err -= dy;
                x0 += sx;
            }
            if (e2 < dx) {
                err += dx;
                y0 += sy;
            }
        }
    }
    
    public void drawRect(int x, int y, int w, int h, int color) {
        drawLine(x, y, x + w - 1, y, color);
        drawLine(x, y + h - 1, x + w - 1, y + h - 1, color);
        drawLine(x, y, x, y + h - 1, color);
        drawLine(x + w - 1, y, x + w - 1, y + h - 1, color);
    }
    
    public static int rgb565(int r, int g, int b) {
        return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
    }
    
    private void writeCommand(int cmd) {
        dc.low();
        spi.transfer((byte) cmd);
    }
    
    private void writeData(int... data) {
        dc.high();
        byte[] bytes = new byte[data.length];
        for (int i = 0; i < data.length; i++) {
            bytes[i] = (byte) data[i];
        }
        spi.transfer(bytes);
    }
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        ILI9341TFT tft = new ILI9341TFT(pi4j, 0, 0, 24, 25);
        
        System.out.println("ILI9341 TFT显示测试");
        
        int red = rgb565(255, 0, 0);
        int green = rgb565(0, 255, 0);
        int blue = rgb565(0, 0, 255);
        int white = rgb565(255, 255, 255);
        
        tft.fillScreen(red);
        Thread.sleep(1000);
        
        tft.fillScreen(green);
        Thread.sleep(1000);
        
        tft.fillScreen(blue);
        Thread.sleep(1000);
        
        tft.fillScreen(0);
        tft.drawRect(10, 10, 100, 50, white);
        tft.drawLine(0, 0, 239, 319, white);
        
        pi4j.shutdown();
    }
}

五、总结 #

显示设备驱动要点:

  1. 接口选择:根据速度需求选择I2C或SPI接口
  2. 帧缓冲:使用缓冲区管理显示内容
  3. 图形绘制:实现基本图形绘制算法
  4. 字体支持:集成字体库实现文字显示
  5. 性能优化:批量传输数据提高刷新效率

下一章我们将学习存储设备,实现数据的持久化存储。

最后更新:2026-03-27