🏠 返回首页

🔧 ILI9486 自定义驱动开发指南

适用于 Orange Pi PC (全志H3) 的完整驱动开发方案

ILI9486 自定义驱动 fbtft框架 Linux内核

🏗️ 驱动架构

用户空间 (App)
read/write/mmap
Linux Framebuffer (/dev/fb0)
fb_ops 回调
ILI9486 驱动 (ili9486.c)
SPI传输
ILI9486 LCD 面板

三种驱动实现方式

方式 代码量 优点 缺点
fbtft框架 200-300行 简单,内核已有框架 功能有限
DRM/KMS 1000+行 现代标准,硬件加速 复杂,学习曲线陡
字符设备 500+行 完全控制,灵活 需要自己处理所有细节
💡 推荐方案 使用fbtft框架,只需实现init函数和少量回调,代码量最少,可以快速上手。驱动文件已创建在 /root/work_materials/ili9486_driver.c

📡 SPI通信协议详解

时序图

Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 CS ← 片选拉低 开始传输 SCK ← 上升沿采样 DC 命令 数据 MOSI D7 D6 D5 D4 D3 D2 D1 D0 ← MSB先发 SPI Mode 0 · CPOL=0 · CPHA=0 · 空闲SCK低 · 上升沿采样

SPI模式

模式 CPOL CPHA 说明
Mode 0 0 0 空闲SCK低,上升沿采样(推荐)
Mode 3 1 1 空闲SCK高,上升沿采样

关键代码

/* 发送命令 - DC引脚拉低 */ static int ili9486_write_cmd(struct ili9486_priv *priv, u8 cmd) { gpiod_set_value(priv->dc, 0); // DC=0 表示命令 return spi_write(priv->spi, &cmd, 1); } /* 发送数据 - DC引脚拉高 */ static int ili9486_write_data(struct ili9486_priv *priv, u8 data) { gpiod_set_value(priv->dc, 1); // DC=1 表示数据 return spi_write(priv->spi, &data, 1); } /* 发送16位像素数据(大端序) */ static int ili9486_write_data16(struct ili9486_priv *priv, u16 data) { u8 buf[2] = { data >> 8, data & 0xFF }; return ili9486_write_data_buf(priv, buf, 2); }

🎬 ILI9486 初始化序列

初始化流程图

上电
硬件复位 (RST)
软复位 (0x01)
退出睡眠 (0x11)
配置寄存器
开显示 (0x29)

MADCTL 旋转控制 (0x36)

旋转角度 MADCTL值 说明
0x48 竖屏,正常方向
90° 0x28 横屏,向右旋转
180° 0x88 竖屏,上下翻转
270° 0xE8 横屏,向左旋转(默认)

MADCTL位定义

Bit 7 (MY): 行地址顺序 0=自顶向下 1=自底向上 Bit 6 (MX): 列地址顺序 0=自左向右 1=自右向左 Bit 5 (MV): 行/列交换 0=正常 1=交换 Bit 4 (ML): 垂直刷新顺序 0=自顶向下 1=自底向上 Bit 3 (RGB): RGB/BGR顺序 0=RGB 1=BGR

像素格式 (0x3A)

格式 说明
0x55 RGB565 16位(常用)
0x66 RGB666 18位
0x77 RGB888 24位

💻 完整驱动代码

📁 文件位置 驱动源码已保存到:/root/work_materials/ili9486_driver.c

项目文件结构

ili9486-driver/
├── ili9486.c ← 主驱动文件(完整代码)
├── Makefile ← 编译脚本
├── Kconfig ← 内核配置菜单
└── README.md ← 说明文档

驱动核心结构

/* 驱动私有数据结构 */ struct ili9486_priv { struct spi_device *spi; struct gpio_desc *dc; // 数据/命令选择 struct gpio_desc *reset; // 复位 struct gpio_desc *led; // 背光 u16 *fbmem; // 帧缓冲内存 int rotate; // 旋转角度 int width; // 当前宽度 int height; // 当前高度 }; /* 设备树匹配表 */ static const struct of_device_id ili9486_of_match[] = { { .compatible = "ilitek,ili9486" }, { } };

关键函数

  1. ili9486_write_cmd() - 发送命令
    DC引脚拉低,发送命令字节
  2. ili9486_write_data() - 发送数据
    DC引脚拉高,发送参数或像素数据
  3. ili9486_init_sequence() - 初始化序列
    完整的ILI9486寄存器配置
  4. ili9486_set_window() - 设置显示窗口
    设置像素操作的区域
  5. ili9486_fb_write() - 写屏回调
    当framebuffer改变时调用
  6. ili9486_probe() - 驱动入口
    初始化GPIO、SPI、分配framebuffer

🌳 设备树配置

Orange Pi PC 设备树节点

/* 在 &spi0 节点下添加 */ &spi0 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&spi0_pins &spi0_cs_pin>; cs-gpios = <&pio 0 2 GPIO_ACTIVE_LOW>; // CE0 ili9486@0 { compatible = "ilitek,ili9486"; reg = <0>; spi-max-frequency = <32000000>; // 32MHz rotate = <270>; // 横屏显示 bgr; // BGR颜色顺序 fps = <30>; dc-gpios = <&pio 1 8 GPIO_ACTIVE_LOW>; // RS/DC引脚 reset-gpios = <&pio 1 9 GPIO_ACTIVE_LOW>; // RST引脚 led-gpios = <&pio 1 7 GPIO_ACTIVE_HIGH>; // 背光引脚 }; };
⚠️ 引脚映射 Orange Pi PC的GPIO编号与树莓派不同。需要参考全志H3的GPIO映射表,将LCD引脚正确连接到SPI和GPIO。

GPIO引脚参考

LCD引脚 Orange Pi GPIO 说明
LCD_CS PA2 (SPI0_CS0) 片选
LCD_RS/DC PH8 数据/命令选择
LCD_SCK PA5 (SPI0_SCK) SPI时钟
LCD_SI/MOSI PA6 (SPI0_MOSI) SPI数据
RST PH9 复位
LED PH7 背光控制

LCD 3.5寸 SPI模块引脚图

LCD 3.5" TFT · ILI9486
26Pin GPIO 排针 (RPi直插)
13.3V25V
3NC45V
5NC6GND
7NC8NC
9GND10NC
11TP_IRQ12NC
13NC14GND
15NC16NC
173.3V18LCD_RS
19LCD_SI20GND
21TP_SO22RST
23LCD_SCK24LCD_CS
25GND26TP_CS
MPI3501 3.5inch RPi Display
MPI3501 模块背面 · 2×20排针接口
功能引脚说明
Pin 24 LCD_CS — LCD片选(低有效)
Pin 18 LCD_RS — 命令/数据选择
Pin 23 LCD_SCK — SPI时钟
Pin 19 LCD_SI — SPI数据输入(MOSI)
Pin 22 RST — 复位引脚
Pin 11 TP_IRQ — 触摸中断(按下拉低)
Pin 21 TP_SO — 触摸SPI数据输出(MISO)
Pin 26 TP_CS — 触摸芯片片选(低有效)
电源引脚
Pin 1, 17 — 3.3V 电源
Pin 2, 4 — 5V 电源
Pin 6, 9, 14, 20, 25 — GND
⚠️ 注意事项
  • 此模块为RPi直插设计,26Pin排针直接插到树莓派GPIO
  • LCD和触摸屏共用SPI MOSI/SCK,片选分开(LCD_CS/TP_CS)
  • 使用前需安装LCD驱动(fbcp或自定义驱动)
  • XPT2046触摸控制器需要独立的SPI MISO(TP_SO)

⚙️ 编译与测试

Makefile

# ILI9486驱动 Makefile obj-m := ili9486.o KDIR := /path/to/linux-orangepi CROSS_COMPILE := arm-linux-gnueabihf- ARCH := arm all: $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) \ -C $(KDIR) M=$(PWD) modules clean: $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) \ -C $(KDIR) M=$(PWD) clean

编译测试步骤

  1. 安装交叉编译工具链
    sudo apt-get install gcc-arm-linux-gnueabihf
  2. 编译驱动
    make
  3. 传输到Orange Pi
    scp ili9486.ko root@orangepi:~/
  4. 加载驱动
    ssh root@orangepi insmod ili9486.ko
  5. 检查framebuffer设备
    ls -la /dev/fb* dmesg | tail -20

测试显示

# 测试framebuffer echo -n "Hello LCD!" > /dev/fb0 # 安装fbset工具 apt-get install fbset fbset -a # 截图 fbgrab /tmp/screenshot.png

🔍 调试技巧

SPI通信调试

# 启用SPI调试日志 echo 8 > /proc/sys/kernel/printk # 检查SPI设备 ls -la /dev/spidev* cat /sys/class/spi_master/spi0/spi0.0/modalias

GPIO调试

# 查看GPIO状态 cat /sys/kernel/debug/gpio # 手动测试GPIO echo 8 > /sys/class/gpio/export echo out > /sys/class/gpio/gpio8/direction echo 1 > /sys/class/gpio/gpio8/value

常见问题排查

问题 可能原因 解决方法
白屏 未初始化或SPI通信失败 检查接线、SPI配置、初始化序列
花屏 时序问题或数据错误 降低SPI频率、检查时序
无显示 背光未开启 检查LED引脚
颜色错误 BGR/RGB顺序错误 修改MADCTL的RGB位

逻辑分析仪

🔧 推荐工具 使用Saleae或DSLogic抓取SPI波形,检查CS、SCK、MOSI、DC四根线的时序和数据是否正确。

🚀 优化方向

DMA传输

/* 使用DMA传输提高效率 */ static void ili9486_fb_write_dma(struct fb_info *info, int x, int y, int w, int h) { struct ili9486_priv *priv = info->par; struct spi_transfer t = { .tx_buf = priv->txbuf, .len = w * h * 2, .speed_hz = priv->spi->max_speed_hz, }; struct spi_message m; spi_message_init(&m); spi_message_add_tail(&t, &m); spi_sync(priv->spi, &m); }

性能对比

方案 刷新率 CPU占用 说明
基础写屏 15 FPS 80% 简单实现
DMA传输 30 FPS 30% 使用DMA
部分刷新 60 FPS 10% 只更新变化区域
双缓冲 60 FPS 5% 前后台缓冲

更多优化

✅ 优化建议
  • 使用ILI9486内置的硬件滚动功能
  • 实现脏矩形检测,只刷新变化区域
  • 利用双缓冲减少撕裂
  • 优化SPI传输批量数据

🔍 LCD-show 代码分析

goodtft/LCD-show 项目代码架构分析,理解SPI LCD驱动的实现原理

整体架构

用户空间 App
read/write/mmap
Linux Framebuffer (/dev/fb0)
fbcp 像素搬运
fbcp-ili9341 用户态程序
直接操作BCM2835寄存器
ILI9486 SPI LCD
💡 核心思路 fbcp 不是内核驱动,是用户态程序。读 /dev/fb0(HDMI帧缓冲)→ 对比前后帧差异 → 通过SPI写到LCD。绕过内核,直接mmap操作BCM2835的SPI寄存器。

目录结构

fbcp-ili9341/
├── fbcp-ili9341.cpp ← 主循环:读fb0 → diff对比 → 推送SPI
├── spi.cpp ← SPI通信层:mmap操作BCM2835寄存器
├── gpu.cpp ← GPU帧缓冲获取
├── diff.cpp ← 差分算法:只更新变化的像素
├── ili9486.cpp ← ILI9486初始化序列 ★你要的部分
├── ili9341.cpp ← ILI9341初始化
├── mpi3501.cpp ← MPI3501初始化
├── display.h ← 编译时选择屏幕型号
└── config.h ← GPIO引脚、SPI频率等配置

ILI9486 初始化序列(ili9486.cpp)

void InitILI9486() { // 1. 硬件复位:RST引脚 高→低→高 SET_GPIO(TFT_RESET_PIN); usleep(120000); CLEAR_GPIO(TFT_RESET_PIN); usleep(120000); SET_GPIO(TFT_RESET_PIN); usleep(120000); // 2. 低速初始化(SPI分频34) spi->clk = 34; // 3. 命令序列 SPI_TRANSFER(0xB0, 0x00, 0x00); // 接口模式 SPI_TRANSFER(0x11); // Sleep OUT usleep(120000); SPI_TRANSFER(0x3A, 0x00, 0x55); // 像素格式 RGB565 SPI_TRANSFER(0x20); // 显示反转 OFF SPI_TRANSFER(0xC0, 0x00, 0x09, 0x00, 0x09); // 电源控制1 SPI_TRANSFER(0xC1, 0x00, 0x41, 0x00, 0x00); // 电源控制2 SPI_TRANSFER(0xC2, 0x00, 0x33); // 电源控制3 SPI_TRANSFER(0xC5, 0x00, 0x00, 0x00, 0x36); // VCOM控制 SPI_TRANSFER(0x36, 0x00, madctl); // ★ MADCTL旋转控制 SPI_TRANSFER(0xE0, ...); // 正向Gamma SPI_TRANSFER(0xE1, ...); // 负向Gamma SPI_TRANSFER(0xB6, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3B); // 显示功能 SPI_TRANSFER(0x11); // 再次Sleep OUT usleep(120000); SPI_TRANSFER(0x29); // ★ 开显示 SPI_TRANSFER(0x38); // 关闭空闲模式 SPI_TRANSFER(0x13); // 正常显示模式 // 4. 恢复高速SPI spi->clk = SPI_BUS_CLOCK_DIVISOR; }

SPI通信方式对比

项目 fbcp(用户态) fbtft(内核态)
SPI访问 mmap直接操作BCM2835寄存器 spi_write() 内核API
帧缓冲 读/dev/fb0,自己搬运 注册fb_device,内核管理
性能 高(绕过内核) 中(经过内核栈)
复杂度 中等(~360行) 简单(~200行)
适用平台 仅BCM2835(RPi) 任意Linux(含全志H3)
初始化序列 完全相同 完全相同
⚠️ 你写Orange Pi PC驱动的要点
  • ILI9486初始化序列直接从ili9486.cpp抄,一字不差
  • SPI传输换成内核API:spi_write(spi, buf, len)
  • fbcp的mmap方式不适用全志H3,必须走内核fbtft框架
  • GPIO操作换成全志的gpiod或sunxi-pinctrl
  • 关键区别:fbcp用16位SPI总线宽度,fbtft默认8位

MADCTL 旋转控制详解

// ILI9486 0x36 MADCTL 位定义 // Bit 7 (MY): 行地址顺序 0=自顶向下 1=自底向上 // Bit 6 (MX): 列地址顺序 0=自左向右 1=自右向左 // Bit 5 (MV): 行/列交换 0=正常 1=交换 // Bit 4 (ML): 垂直刷新顺序 0=自顶向下 1=自底向上 // Bit 3 (RGB): RGB/BGR顺序 0=RGB 1=BGR // 常用旋转值 0x48 // 0° 竖屏 0x28 // 90° 横屏(向右旋转) 0x88 // 180° 竖屏(上下翻转) 0xE8 // 270° 横屏(向左旋转,默认) // fbcp中的MADCTL计算 uint8_t madctl = 0; madctl |= MADCTL_BGR_PIXEL_ORDER; // 通常ILI9486是BGR madctl |= MADCTL_ROW_COLUMN_EXCHANGE; // 横屏模式 // 如果要旋转180度: madctl ^= MADCTL_COLUMN_ADDRESS_ORDER_SWAP | MADCTL_ROW_ADDRESS_ORDER_SWAP;

📥 资源下载

驱动文件

参考资源