显示设备 #
一、显示设备概述 #
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();
}
}
五、总结 #
显示设备驱动要点:
- 接口选择:根据速度需求选择I2C或SPI接口
- 帧缓冲:使用缓冲区管理显示内容
- 图形绘制:实现基本图形绘制算法
- 字体支持:集成字体库实现文字显示
- 性能优化:批量传输数据提高刷新效率
下一章我们将学习存储设备,实现数据的持久化存储。
最后更新:2026-03-27