简介:面向嵌入式开发者的轻量级ILI9341驱动源码包,配合芯片技术详解,帮助读者解决TFT液晶屏初始化、绘图、显示文本等驱动开发难题。适用于电子竞赛、课程设计、智能设备界面开发等场景,也适合正在从基础模块向寄存器级编程进阶的嵌入式学习者。压缩包内含1个ili9341.c源文件,整体大小约2KB,代码精简,便于快速移植到Arduino、Raspberry Pi等常用平台,可单独阅读也可作为配套工程的一部分。ILI9341是一颗支持800x480分辨率、16.7M色、SPI与8/16位并行接口的TFT液晶控制器,在智能手机、导航仪、工业控制显示面板中都有广泛应用。驱动源码覆盖芯片复位、初始化序列、写命令/写数据、清屏、绘制基本图形和文本显示等关键环节,并预留扩展接口,读者可在此基础上进一步实现触摸交互、图像显示等功能,有效缩短显示模块的调试周期。目前已有233人学习下载,适合对LCD底层驱动原理感兴趣,并希望在实际项目里快速应用ILI9341的开发者。
1. 为什么你的 ili9341 插上树莓派只亮背光
ili9341_这个标题像随手起的工程名,但至少见过这块屏的人都会把它补完。ILI9341 是一颗几乎统治了 2.4/2.8 寸 SPI 液晶模组的驱动 IC,从 Arduino 到树莓派都能遇到它。难点不在焊接,而在树莓派上“点亮后没法好好用”:默认 HDMI 占着 fb0,屏幕却自己注册成 fb1,背光亮着却在跑雪花,旋转方向、触摸坐标、刷新率全是坑。这篇文章会从树莓派官方内核里已有的 fbtft 驱动出发,给出最小可复现的 config.txt 配置、设备树参数和排错手段,适合第一次碰 SPI LCD,或想把这块小屏做成监控面板的工程师。
2. ILI9341 的官方驱动在哪:fbtft、overlay 与设备树
2.1 没有“安装包”,驱动早就编进树莓派内核了
不少人搜索“ili9341 树莓派官方驱动”,期望在 GitHub 上下载一个驱动源码包编译安装。实际情况是:树莓派 OS 的内核里已经把上游 fbtft 子系统的ili9341驱动编译进去了,树莓派官方固件又额外提供了一个名为rpi-display的设备树 overlay,把驱动实例挂到 SPI0 的 CE0 上。也就是说,官方驱动的形态是“内核模块 + 设备树 overlay”,不是一个独立安装包。
在这种架构下,点亮屏幕要做的不是装驱动,而是让内核在启动阶段创建一个名为ili9341的 SPI 设备,并把它注册成/dev/fb1。fbtft 是一个把液晶控制器抽象成 Linux 帧缓冲(framebuffer)的框架,它负责类型无关的那部分:内存映射、刷新机制、颜色格式转换;具体的 ILI9341 寄存器序列则写在drivers/staging/fbtft/fb_ili9341.c里,包括 INITGAMMA、MADCTL、COLOR 等初始化命令。
2.2 三种点亮 ILI9341 的方式与选型
在树莓派上接入 ILI9341,常见的路径大致有三条,它们之间的选择直接决定你后面能不能顺利叠加触摸、旋转和终端。
| 接入方式 | 配置入口 | 适合谁 | 与官方内核的关系 |
|---|---|---|---|
dtoverlay=rpi-display | /boot/config.txt | 绝大多数串口 SPI 屏用户 | 官方固件自带,推荐首选 |
dtoverlay=fbtft+ 参数动态指定 name | /boot/config.txt | 需要调整片选、GPIO 复用的高级用户 | 基于内核 fbtft 模块 |
| DRM/KMS 面板驱动 | 内核配置 + 设备树 | 需要硬解旋转、合成画面 | 主线新方案,树莓派固件覆盖还不完整 |
rpi-display这个名字容易让人以为它只适配某种特定型号的屏,实际上它正是针对 ILI9341 这类 320x240 面板定制的 overlay,把 compatible 指向ilitek,ili9341。我一般会直接用这个 overlay,只有需要把屏幕挂到 SPI1、SPI2 或者换成非标引脚时,才退回fbtft参数化方式。DRM 方案更现代,但在树莓派上还没有成为开箱即用的默认路线,暂不作为新手首选。
2.3 从设备树反编译确认 overlay 的真实参数
官方固件里的 overlay 文件是编译过的二进制 DTB,想看它到底暴露了哪些参数,可以用设备树编译器解包,不用去猜测 README:
sudo apt install device-tree-compiler dtc -I dtb -O dts /boot/overlays/rpi-display.dtbo 2>/dev/null | grep -E "compatible|rotate|speed|fps"这个命令把rpi-display.dtbo反编译成可读的 DTS 文本,然后过滤出驱动绑定和可调参数。逻辑上,dtc -I dtb告诉工具输入是二进制设备树,-O dts要求输出明文源码;2>/dev/null丢弃反编译过程中的警告噪音。看到输出里有ilitek,ili9341以及rotate、speed、fps这些属性名,说明这块 overlay 就是可配置的 ILI9341 驱动入口,后面所有调参都是往这里走。
3. 点亮 ILI9341 的最小步骤:SPI、config.txt 与帧缓冲验证
3.1 开启 SPI 总线并装载 rpi-display overlay
第一步先把树莓派的 SPI 主控制器打开,否则设备树里的spi0.0节点不会被挂载驱动。命令行方式比进入raspi-config逐层点更快:
sudo raspi-config nonint do_spi 0 echo "dtoverlay=rpi-display" | sudo tee -a /boot/config.txt sudo rebootraspi-config nonint是它在非交互模式下的接口,do_spi 0表示把 SPI 设为启用;tee -a把 overlay 配置追加到/boot/config.txt末尾。重启后可以检查两处:/dev/fb*里是不是多了 fb1,以及dmesg里是否出现 fbtft 与 ILI9341 的注册信息。注意如果 config.txt 里已有过dtoverlay=rpi-display,先删掉旧行再追加,重复覆盖不会叠加生效,反而可能因参数冲突启动失败。
3.2 确认 /dev/fb1 与内核日志
重启后依次执行下面三条命令:
ls -l /dev/fb* dmesg | grep -iE "fbtft|ili9341" sudo fbi -d /dev/fb1 -T 1 ./test.png第一行确认 fb 设备数量;第二行看内核日志里驱动实例名和 SPI 速率,如果看到fb1: fbtft相关的注册行,说明驱动已经跑起来了;第三行用fbi把一个测试图片直接画到 fb1 上,-d指定设备,-T指定虚拟终端。fbi 需要 root 权限,因为/dev/fb1默认属主是 root:video。如果屏幕上出现完整画面,说明 SPI 接线、供电、驱动链路全部正常,接下来的旋转和触摸才有意义。
3.3 用 numpy 往帧缓冲写一张渐变图
没有现成图片时,可以直接往/dev/fb1的映射内存里写像素,这是验证帧缓冲最干净的手段,不依赖任何图形库:
import mmap import time import numpy as np # 从 sysfs 读取 fb1 的实际分辨率,避免手动猜横竖方向 with open("/sys/class/graphics/fb1/virtual_size") as f: w, h = map(int, f.read().strip().split(",")) with open("/dev/fb1", "r+b") as fb: fb_map = mmap.mmap(fb.fileno(), w * h * 2, access=mmap.ACCESS_WRITE) # 构造 RGB565 渐变帧,R 随 x 变化,G 随 x 一次方变化,B 随 y 变化 y, x = np.mgrid[0:h, 0:w] r = (x * 31 // (w - 1)).astype(np.uint16) << 11 g = (x * 63 // (w - 1)).astype(np.uint16) << 5 b = (y * 31 // (h - 1)).astype(np.uint16) frame = (r | g | b).astype("<u2") # 小端 RGB565 # 连续写 120 帧,模拟 60fps 的写入节奏 for _ in range(120): fb_map[:] = frame.tobytes() time.sleep(0.016) fb_map.close()这段代码的关键在virtual_size的读取:fb1 在旋转后可能是 320x240,也可能是 240x320,直接读 sysfs 比硬编码安全。RGB565 的位布局是高 5 位红、中间 6 位绿、低 5 位蓝,所以r左移 11 位、g左移 5 位、b直接放低位,最后转成小端字节序。平滑渐变不花屏,说明像素格式和内存长度都正确。
4. 把 ILI9341 调到能用的状态:旋转、分辨率与触摸参数
4.1 三个最常改的 overlay 参数:rotate、speed、fps
点亮只是一个开始,绝大多数用户卡在“画面方向不对”和“刷新慢”上。rpi-display暴露的参数里,最有价值的三个是:
| 参数 | 常见取值 | 作用与踩坑 |
|---|---|---|
rotate | 0 / 90 / 180 / 270 | 顺时针旋转画面;0 是面板物理正向,排线在侧面的模组往往要用 90 或 270 |
speed | 16000000 / 32000000 | SPI 通信时钟,单位 Hz;跳线过长时 32MHz 容易花屏,降到 16MHz |
fps | 20 / 25 / 30 | fbtft 的内置刷新上限;设得太高会让 CPU 花费大量时间在刷屏上 |
设置方式是在/boot/config.txt的dtoverlay后面追加参数:
dtoverlay=rpi-display,speed=32000000,rotate=90,fps=30rotate=90在 fbtft 里是软旋转,驱动会交换画面的宽高并改写 MADCTL 寄存器,比在应用层做坐标变换省事得多。speed是 SPI 时钟,不是帧率,两回事;如果屏幕出现雪花状噪声,直接看这一项。fps适合作为上限保护,实际吞吐还受总线带宽限制,所以不要指望调到 60 帧,2.4 寸串口屏能稳定 25 帧已经够看了。
4.2 背面触摸屏 XPT2046 的设备树接入与校准
大部分 3.5 寸的 ILI9341 模组背面还带着一块 XPT2046 触摸芯片,它与 TI 的 ADS7846 兼容,树莓派官方固件里同样有 overlay。触摸屏和显示共用 SPI0,但片选要错开:显示用 CE0,触摸用 CE1。
dtoverlay=ads7846,cs=1,penirq=25,penirq_pull=2,speed=2000000,swapxy=1cs=1表示使用 SPI0 的 CE1,避免与屏幕的 CE0 冲突;penirq指定触摸中断引脚,25 是一个对外设冲突较少的 GPIO;penirq_pull=2启用内部上拉,防止浮空误触发;speed=2000000是触摸芯片的 SPI 采样时钟,不需要跟着屏幕跑到 32MHz,保持低速率更抗干扰。校准方式是在 X 环境下安装并运行校准器:
sudo apt install xinput-calibrator DISPLAY=:0 xinput_calibrator --device "$(evtest --query /dev/input/event* 2>&1 | sed -n 's/.*\(ADS7846[^"]*\).*/\1/p')"先重启一次让ads7846自动生成 input 设备,再用evtest逐个确认设备名,最后用校准器生成一组五点的校准矩阵。走过这个流程后,触控坐标就会和显示像素对齐,不用在应用层再写坐标换算。
4.3 一份可直接抄的 config.txt 完整片段
把前面内容合成一份可复制的配置,按顺序写在/boot/config.txt即可:
dtparam=spi=on dtoverlay=rpi-display,speed=32000000,rotate=90,fps=30 dtoverlay=ads7846,cs=1,penirq=25,penirq_pull=2,speed=2000000,swapxy=1dtparam=spi=on和raspi-config nonint do_spi 0等价,写进文件里更不容易被后续操作覆盖。第一行和第二行的顺序有讲究:必须先保证 SPI 控制器存在,overlay 才能把子设备挂上去;第三行与第二行互不干扰,可以并行存在。swapxy 是否保留取决于触摸屏模组,若校准后 Y 轴反向,就把它去掉。
提示:如果你用的是只有 4 根 SPI 线的裸屏没有触摸芯片,第三行不要加,否则 dmesg 里会出现 CS1 设备挂载失败的错误,虽然不影响显示,但会污染日志排查视线。
5. ILI9341 花屏排查与把它变成串口日志终端
5.1 白屏、花屏、颜色错误分别是什么原因
亮度正常但画面乱,是串口屏最常见的失败模式,先按症状分类再动手。
| 现象 | 优先检查项 |
|---|---|
| 背光亮、全白 | VCC 电压不足、RESET 引脚被拉低、DC 引脚接错 GPIO |
| 斜向雪花/横纹 | SPI 时钟过高,speed降到 16000000;杜邦线长度超过 15cm |
| 颜色反相 | dtoverlay=rpi-display追加invert=1 |
| 屏幕偏暗 | LED 背光引脚供电不够,需要单独接 3.3V 或 5V(看模组标识) |
花屏问题里,SPI 速率过高的比例最大。树莓派的 SPI 主控能跑到 100MHz 不代表模块能跟上,fbtft 的speed参数是直接写给 SPI 驱动的请求速率,出错时优先减半。还有一个隐蔽问题:DC(RS)引脚在很多模块上标为DC,但某些厂商会标成RS或A0,接错时初始化指令全部错位,表现就是白屏或纯色屏,不一定是驱动问题。
5.2 让 ILI9341 常驻滚动日志:con2fbmap 与 fbterm 组合
屏幕大部分时间不用来跑 GUI,而是当作服务器监控副屏,最省资源的做法是把 Linux 控制台直接映射到 fb1 上。con2fbmap可以把指定虚拟终端绑定到 fb1,再用fbterm提供不依赖 X 的文字渲染:
sudo con2fbmap 1 1 sudo chvt 1 sudo fbterm -s 16 -- /bin/bashcon2fbmap的第一个参数是 tty 编号,第二个参数是 fb 编号,合起来的意思是把 tty1 从 HDMI 的 fb0 移动到 ILI9341 的 fb1 上。之后chvt 1切换到该终端,fbterm 就会以 16 号字体渲染文本。这时在 fbterm 里面执行:
sudo fbterm -s 16 -- tail -f /var/log/syslogfbterm 是把终端输出画到帧缓冲,而不是走显卡合成,因此屏幕小也不卡。如果想要日志自动带时间过滤,可以把它包到/etc/rc.local里,配合systemctl把 tty1 的登录 shell 替换成 fbterm,整个 ILI9341 就变成一块常年滚动的 syslog 终端屏,不再依赖任何桌面环境,占的内存比一张浏览器标签页还小。
本文还有配套的精品资源,点击获取