ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

RK3568 SPI LCD FrameBuffer驱动开发实战:从设备树到初始化序列

RK3568 SPI LCD FrameBuffer驱动开发实战:从设备树到初始化序列 如果你的项目恰好要在RK3568上用SPI接口点亮一块小尺寸LCD同时又不希望引入复杂的DRM显示链路那这篇文章值得你花十分钟看完。我在实际项目里用FrameBuffer模式给RK3568写过SPI LCD驱动整个过程踩了不少坑——从设备树节点怎么写、SPI时序参数怎么定到预初始化指令序列的排列组合每一步都有讲究。这篇文章就把我调通的完整思路、驱动框架对接经验和排障过程整理出来给准备在RK3568上做SPI LCD驱动开发的工程师一个可以复现的起点。先说清楚这篇文章讲的是什么RK3568平台下用Linux内核的FrameBuffer机制驱动一块SPI接口的TFT LCD屏幕。它解决的是“低成本小屏显示”这一类需求适合做HMI面板、工控显示模块、仪表副屏也适合刚接触嵌入式Linux显示驱动的同学用来理解FrameBuffer和SPI设备驱动的协同工作方式。文中的代码和配置基于Linux内核5.10版本的常见框架fbtft和自研驱动的两套思路我都会讲到你可以按自己的项目情况选。1. 整体技术路线与FrameBuffer方案选型1.1 为什么在RK3568上还要用SPI LCDRK3568是瑞芯微一颗四核Cortex-A55处理器资源不算差通常搭配eDP、MIPI DSI、LVDS这类高速显示接口。但实际项目中SPI LCD依旧有它不可替代的存在价值。第一是成本敏感场景。小尺寸屏1.3寸、1.54寸、2.4寸TFT走SPI接口屏幕本身单价低PCB走线也简单四层板甚至两层板都能把信号拉通。SPI一共就CLK、MOSI、MISO、CS几条线相比MIPI DSI的差分对和RGB并口的二十多根线布局压力小一个量级。第二是低功耗场景。很多SPI LCD在进入Sleep In模式后可以做到极低功耗配合屏幕自带的内部RAM刷新适合常显设备。比如一个温控器面板需要做的只是每隔几秒更新一组数字SPI屏的功耗优势就很明显。第三是布线受限场景。处理器的MIPI DSI或RGB并口被主屏占用时用SPI引出一条副屏是成本最低的扩展方案。我在一个双屏项目里就是这么干的主屏走eDP显示主界面副屏用SPI接口显示状态图标和日志信息两套显示链路互不干扰。选FrameBuffer而不是DRM核心原因是开发效率。fbtft、pwm-backlight这些老牌框架在FrameBuffer模式下已经非常成熟社区资料多踩坑成本低。而RK3568如果完全走DRMSPI LCD要么写一个drm_panel驱动挂到虚拟显示接口上要么走MIPI DSI的command mode配置复杂度会高出一个数量级。对只需要做简单状态显示的设备来说FrameBuffer的mmap、ioctl接口完全够用用户态直接往显存里写像素数据就行。1.2 FrameBuffer、fbtft和DRM的关系梳理来理一下这几个概念它们在嵌入式显示开发里经常被混着说。FrameBuffer是Linux内核里最早期的显示抽象层它把显示设备表达成一个简单的内存区应用程序通过mmap把显存映射到用户空间直接往里写RGB数据然后由驱动负责把显存内容推到屏幕上。它的优势是简单直白劣势是没有做图形合成、多图层叠加这些现代显示功能。DRM是后来取代FrameBuffer的现代显示框架它把显示链路拆成了CRTC、Encoder、Connector、Plane这些对象功能强很多但上手成本也高很多。RK3568主线内核的eDP、HDMI、MIPI DSI基本都是走DRM框架。fbtft是Linux内核里专门为小尺寸TFT屏幕写的一套驱动库它在FrameBuffer框架下把“显存管理、SPI传输、初始化序列下发”这些公共逻辑全部封装好了。ST7789、ILI9341、GC9A01这些常见屏幕都可以直接套用。fbtft的核心思想是你只需要告诉它屏幕的分辨率、像素格式、初始化命令序列它就能帮你把FrameBuffer设备注册好把SPI传输管起来。我在这篇文章里会以fbtft为主线讲因为对绝大多数SPI LCD项目来说这是最短路径。但自研驱动的思路也会单独讲因为总有fbtft覆盖不到的场景。2. 硬件链路与SPI总线参数设计2.1 RK3568 SPI控制器能力与引脚分配RK3568内部有多组SPI控制器一般命名为SPI0到SPI3。每组控制器都支持master和slave模式支持8/16/32位字宽时钟由PLL分频得到。实际项目中我用的是SPI3挂在某个GPIO组上具体引脚以你自己的原理图为准。引脚分配上有一个关键决策使用硬件CS还是用软件GPIO模拟CS。RK3568的SPI控制器自带硬件片选输出在设备树里通过cs-gpios或默认cs引脚配置。但硬件CS有几个让我头疼的问题片选释放时机不好控制某些屏幕在CS拉高瞬间需要额外的保持时间硬件CS不一定满足硬件CS的电平极性如果和屏幕控制器的期望不匹配会出现首字节丢失或偶发误触发。所以我建议直接用普通GPIO手动拉CS尤其在系统里GPIO有复杂mux配置的时候。软件拉CS的代价是每次传输前多几十纳秒的GPIO操作对10MHz以下的SPI速率来说完全不是瓶颈。除了CLK、MOSI、CS三根线SPI LCD一般还需要两个控制引脚DC数据/命令选择和RST复位。部分屏幕还有BL背光或TE撕裂同步引脚。DC这根线非常关键后面驱动设计部分会详细展开。2.2 SPI模式与初始化序列的匹配SPI有四种模式由CPOL时钟极性和CPHA时钟相位组合决定。SPI LCD几乎只用到mode 0和mode 3SPI mode 0CPOL0CPHA0SCLK空闲为低电平数据在第一个时钟沿采样。 SPI mode 3CPOL1CPHA1SCLK空闲为高电平数据同样在第一个时钟沿采样。ST7789、ILI9341、GC9A01这些主流屏幕控制器的规格书里都会写明支持的SPI模式。但我踩过的坑是规格书写的“支持mode 0/3”不代表实际点亮效果一样个别屏在mode 3下会因为时钟沿采样时序的微小差异出现颜色偏移或首像素异常。我的调试习惯是先把逻辑分析仪挂在CLK和MOSI线上按照规格书推荐的模式发一条读ID指令观察返回的8位数据是否和屏厂手册一致。如果一致说明模式正确不一致换另一个模式再试。这个方法比盲调快得多。2.3 SPI速率的选择逻辑SPI时钟不是越快越好。你需要先算一笔带宽账。一块240x320分辨率的RGB565屏每像素16bit每帧数据量是240 x 320 x 16 1,228,800 bit。SPI速率10MHz下理论帧率约8fps实际加上命令传输、CS切换和协议开销约4到6fps。如果屏幕只显示静态内容或低频更新这个帧率完全够用。如果把SPI速率提到40MHz理论帧率到32fps但这时候问题就来了。第一是信号完整性SCLK走线稍长就会出现振铃导致数据采样错误具体表现就是花屏或者随机噪点。第二是屏幕控制器的内部FIFO处理能力很多低端屏控制器在超过20MHz后写显存指令会出现丢字节现象。我用过的一块屏数据手册标称最高支持60MHz实际20MHz以上就偶发花屏。所以我的建议是优先保证稳定先按10MHz调试通再根据实际波形和花屏情况慢慢往上升。SPI LCD的传输速率从来不是瓶颈屏幕控制器的响应能力和初始化序列才是决定显示品质的关键。3. 设备树配置与驱动框架对接3.1 设备树spi节点编写要点RK3568的设备树是标准的DTS格式SPI LCD的节点一般挂在某个SPI控制器下面。下面是一个我在项目中实际使用的设备树节点示例屏幕是ST7789控制器的240x320 IPS屏spi3 { status okay; pinctrl-names default; pinctrl-0 spi3_pins; st7789v0 { compatible fbtft,st7789v; reg 0; spi-max-frequency 10000000; spi-mode 0; dc-gpios gpio1 RK_PA0 GPIO_ACTIVE_HIGH; reset-gpios gpio1 RK_PA1 GPIO_ACTIVE_HIGH; backlight-gpios gpio1 RK_PA2 GPIO_ACTIVE_HIGH; rotate 0; bgr 1; fps 30; }; };逐个解释这些属性的作用。compatible字段决定了驱动怎么找到这个节点。fbtft框架下compatible写fbtft,st7789v就能被fbtft的内置驱动匹配上。如果你决定自研驱动可以自定义一个compatible字符串比如mycomp,spilcd。reg 0表示这是这个SPI控制器上的第0个片选设备对应CS0。spi-max-frequency就是前面说的SPI传输时钟上限单位Hz。我写的是10MHz你可以根据实际屏幕支持调整。dc-gpios和reset-gpios是SPI LCD的两个命门。DC引脚用来区分当前SPI总线上的字节是命令还是数据fbtft驱动会在这个引脚上做精确的时序控制。reset引脚则负责上电时序后面会细说。rotate和bgr是fbtft特有的显示参数。rotate控制屏幕旋转方向0表示不旋转90/180/270对应不同方向bgr字段控制像素RGB通道是否交换这在同型号屏幕不同批次之间经常需要调整。3.2 fbtft接入还是自研驱动fbtft在主线内核里维护已久对标准ST7789、ILI9341、GC9A01这类屏幕支持得很完整。如果你的屏幕控制器是这些常见型号之一直接用fbtft是最省力的。但fbtft不是万能的。我在项目中遇到不适合fbtft的场景包括屏幕初始化序列特别长且需要精确的延时控制比如初始化过程中需要等待屏幕内部稳压器稳定fbtft的init序列框架对“写命令后等待特定时间”的支持不够灵活。设备树里不好表达复杂的偏置电压或Gamma设置这些参数在某些屏厂的非标控制器上需要一次性写入几十个连续寄存器fbtft的初始化数组写起来非常费劲。项目要求内核裁剪到极致不希望引入fbtft的公共代码和依赖。这种情况下自研一个FrameBuffer驱动的确更合适。自研的工作量其实不大核心就是注册一个platform_driver在probe里完成spi_device挂载、fb_info分配、显存映射和FB ops定义。下面这个章节我会把自研驱动的关键代码骨架写出来。3.3 FrameBuffer注册过程与显存映射思路无论用fbtft还是自研FrameBuffer的注册逻辑都是一样的。核心是填充fb_info结构体然后调用register_framebuffer把它注册到内核显示子系统。fb_info有几个字段必须正确设置var结构体描述显示参数包括分辨率、像素格式、行间距fix结构体描述固定属性包括显存起始地址、长度、类型还有fb_ops函数集里面至少要实现fb_setcolreg、fb_blank、fb_fillrect、fb_copyarea、fb_imageblit这几个基本函数不然用户态的Qt和fbcon用起来会出问题。显存分配这一块有个关键点。SPI LCD本质上是一个慢速输出设备显存只是内核里的一块普通RAM不需要也不应该关联复杂的DMA引擎。我的做法是用dma_alloc_coherent分配一块一致性的内存区域即使SPI控制器不走DMA这样也是安全的。它比kmalloc的好处是内存地址物理连续如果后续你决定让SPI控制器走DMA传输可以无缝切换同时它的cache属性配置比较干净用户态mmap和内核写显存之间不容易出现缓存一致性带来的花屏问题。4. 驱动代码实现与刷新机制剖析4.1 probe函数流程与初始化序列下发我先给一个自研驱动的probe流程骨架然后逐个环节展开解释。static int spilcd_probe(struct spi_device *spi) { struct fb_info *info; struct spilcd_par *par; int ret; // 1. 解析设备树参数 par kzalloc(sizeof(*par), GFP_KERNEL); par-dc devm_gpiod_get(spi-dev, dc, GPIOD_OUT_LOW); par-reset devm_gpiod_get(spi-dev, reset, GPIOD_OUT_HIGH); // 2. 设置屏幕初始化序列 spilcd_reset(par); spilcd_init_sequence(par); // 3. 分配fb_info和显存 info framebuffer_alloc(sizeof(*par), spi-dev); info-screen_base dma_alloc_coherent(spi-dev, SZ_2M, par-dma_addr, GFP_KERNEL); info-fbops spilcd_ops; info-fix spilcd_fix; info-var spilcd_var; // 4. 注册FrameBuffer ret register_framebuffer(info); if (ret) { dev_err(spi-dev, register framebuffer failed\n); return ret; } return 0; }probe里最容易被忽略的是reset时序。ST7789的规格书要求reset引脚拉低至少10ms拉高后再等120ms才能下发初始化命令。这个时序不满足屏幕可能表现为白屏、花屏或随机显示异常。更麻烦的是这种问题在逻辑分析仪上不一定看得出来因为reset脚通常没接逻辑分析仪你只会看到初始化命令发出去了但屏幕没反应。我建议在reset信号上加一个固定延时函数不要依赖gpiod_set_value的快速返回。实际操作中我会这样写static void spilcd_reset(struct spilcd_par *par) { gpiod_set_value(par-reset, 1); usleep_range(10000, 12000); gpiod_set_value(par-reset, 0); usleep_range(10000, 12000); gpiod_set_value(par-reset, 1); usleep_range(120000, 130000); }这里先拉高再拉低再拉高是为了确保上电后reset有一个完整的下降沿和上升沿让屏幕控制器的内部复位逻辑可靠触发。后面120ms是屏幕初始化等待时间这个值我一般按最大规格来提前个10ms都可能在某些批次上出问题。4.2 屏初始化序列的构造技巧初始化序列是指屏幕控制器的寄存器配置指令它决定了显示方向、颜色格式、Gamma曲线、电源时序等。这个序列通常由屏厂提供每个屏幕型号都不一样。在驱动里初始化序列一般组织成一个命令数组格式为“命令长度、命令码、参数1、参数2...”static const u8 st7789v_init_seq[] { 0x01, 0x36, 0x00, // MADCTL 0x01, 0x3A, 0x05, // COLMOD 16bit 0x02, 0xC8, 0x13, 0x04, // Power Control 1 ... 0x00, 0x11, // 无参数Sleep Out 0x80, 0x29, // 延时后 Display On };fbtft的init序列机制有个约定命令长度用0xFF表示无参数命令用0x80开头表示命令执行后需要额外延时。自研驱动时我也沿用了这种协议写了一个简单的解析函数遇到0x80标记就在下发完当前命令后msleep。这里有个经验之谈初始化序列的顺序千万不能改动尤其Power Control相关的命令必须在Sleep Out之前执行Display On之前的延时必须留足。我调试时曾经把Sleep Out提到前面结果屏幕亮度明显偏低查了半天才发现是Power Control的命令被覆盖了。4.3 SPI传输与DC引脚的控制逻辑SPI LCD的传输和普通SPI设备有个核心区别DC引脚决定了总线上的字节是命令还是数据。发送命令时DC拉低发送像素数据时DC拉高。fbtft和自研驱动都采用spi_message和spi_transfer结构来组织传输。一个常见的优化是把命令阶段的DC切换和数据阶段的DC切换放到同一个spi_message里用两个spi_transfer分别对应命令字段和数据字段这样整个帧数据只需要一次spi_sync调用。struct spi_transfer tr[2]; struct spi_message msg; tr[0].tx_buf cmd; tr[0].len cmd_len; tr[1].tx_buf data; tr[1].len data_len; spi_message_init(msg); spi_message_add_tail(tr[0], msg); spi_message_add_tail(tr[1], msg); spi_sync(spi, msg);注意在使用spi_transfer时DC引脚的电平控制需要你自己在transfer之前通过gpiod_set_value设置好。这里有一个容易犯的错误不要在每次transfer之间释放CS而是要在整个命令数据序列结束后才拉高CS。很多屏幕控制器对CS的连续性是敏感的如果CS在命令和数据之间出现一个高电平毛刺控制器可能把第一个数据字节误判为命令。另一个常见问题是spi_transfer的delay_usecs字段。某些屏幕要求在命令和相应数据之间保持至少几微秒的间隔可以在tr[0]和tr[1]之间设置delay。但尽量不要用这个字段做长延时它依赖SPI控制器的实际精度在RK3568上可能不准。4.4 FrameBuffer的读写接口与mmap支持注册了FrameBuffer之后用户态程序通过mmap打开/dev/fbX设备节点直接映射显存。static int spilcd_mmap(struct fb_info *info, struct vm_area_struct *vma) { return dma_mmap_coherent(info-dev, vma, info-screen_base, info-fix.smem_start, info-fix.smem_len); }dma_alloc_coherent分配的内存必须用dma_mmap_coherent来映射不能直接用remap_pfn_range否则cache一致性策略会出问题。fb_ops里的fb_write、fb_fillrect、fb_copyarea这些函数如果用户态用的是标准Qt over FrameBuffer或者directfb系统会调用这些函数。fbtft内部提供了一套默认的绘制实现效率还不错。自研驱动的话我建议直接复用内核里的cfb_fillrect、cfb_copyarea、cfb_imageblit它们是通用的软件绘制函数代码量小且经过充分验证。4.5 刷新时机与延迟控制SPI LCD是一个内存型显示设备数据写入屏幕控制器的GRAM后屏幕会持续刷新显示不需要额外动作。所以驱动的职责就是把用户态的显存内容推送到GRAM。这里有两套刷新策略。第一套是全屏刷新在fb_write或者其他写操作完成后把整块显存通过SPI写入GRAM。这套逻辑简单但带宽开销大。第二套是局部刷新维护一个脏矩形区域只把变化的部分推送到GRAM。fbtft的deferred IO机制实现的就是局部刷新它利用page fault或delayed work来检测脏区域。自制驱动里我用过最简单有效的局部刷新方案是在fb_write里记录写入的起始位置和长度然后合并相邻的写操作在schedule_delayed_work里延迟10毫秒统一刷新。这个延迟对显示静态内容的工控界面完全无感但能把SPI流量降低一个数量级以上。5. 调试实战常见问题与排查清单这部分我完全是拿实际调试的血泪史换来的照着这个顺序查能省下大半天的排查时间。5.1 白屏问题排查白屏是SPI LCD调试遇到最多的问题。白屏有两种情况整个屏幕完全点亮但无任何内容或者背光亮了但屏幕显示全白。完全点亮的白屏问题几乎都出在初始化序列没有生效。按这个顺序排查先检查reset时序用示波器量reset引脚看拉低宽度是否大于10ms拉高后是否延时了120ms。我在项目里就遇到过屏厂FAE给的示例代码是正确的但驱动里因为GPIO申请失败导致reset脚根本没动作。再检查DC引脚的初始电平DC在发送命令时要为低如果驱动加载时DC默认被拉高第一条命令就会被当成数据写入GRAM整个初始化就乱了。最后检查初始化序列本身找屏厂FAE要一份新的初始化代码很多非标控制器的初始化序列会随批次更新。背光亮了但屏幕全白通常是显存内容没有正确推送到GRAM。重点检查fb_info的虚拟分辨率xres_virtual和yres_virtual是否设置正确如果虚拟分辨率大于实际分辨率用户态写入的显存位置可能超出屏幕控制器的GRAM范围导致显示区域全是零值。5.2 花屏、噪点和颜色错乱的排查花屏的成因比白屏复杂我把优先级排一下最高优先级是SPI模式。mode 0和mode 3在逻辑分析仪上都能看到数据但如果采样沿不对屏幕控制器采到的就是错位的数据。表现是颜色分布完全随机像一个万花筒。用逻辑分析仪对比CLK的采样沿和MOSI数据变化时刻确保数据在采样沿之前至少保持半个时钟周期的稳定时间。第二优先级是SCLK速率。前面讲过速率过高时信号完整性变差表现是随机噪点、横向条纹、颜色闪烁而且无规律。这个最好排查把spi-max-frequency改成5MHz看是否消失。第三优先级是字节序和RGB通道顺序。ST7789用RGB565格式时发送到GRAM的数据是大端序还是小端序取决于COLMOD寄存器配置。设备树里的bgr字段如果配反红蓝通道会互换整体色调偏色。还有一个隐蔽的坑某些屏幕在rotate90或270时扫描起始位置和方向会改变颜色顺序也可能受影响。第四优先级是显存与GRAM的映射关系。FrameBuffer里每行的字节数必须是偶数对齐否则后续行的起始地址会错位表现为每隔一定行数出现一条斜向的错位条纹。5.3 刷新率上不去的瓶颈定位同样一块屏和驱动别人刷新率能到30fps你的只有8fps差距一般出在这几个地方。用perf top或tracepoint看CPU占用如果spi_sync的调用栈占了很大比例说明SPI传输本身耗时。这时检查SPI时钟是否真的到了配的10MHz有些内核SPI驱动会根据设备树时钟频率自动分频分频系数不对会把实际速率拉低。看传输的组织方式。如果每次传输都单独调用spi_syncCS会反复拉高拉低每帧的CS切换开销就很大。正确做法是一个spi_message里尽量包含整帧数据我实测下来10MHz SPI单次传输1.2Mbit和分成100次传输时间差距可以达到两倍以上。检查是否开启了SPI的DMA传输。RK3568的SPI控制器本身支持DMA但设备树里要正确配置dmas属性。如果SPI传输走PIO模式CPU需要逐个字节或者逐个FIFO搬运数据占用率会很高。SPI DMA需要两个通道一个TX一个RX即使你只发不收也要配置RX通道很多芯片的SPI控制器要求TX/RX成对使能。5.4 排查速查表现象优先排查项验证手段全灭无背光背光GPIO、背光电源万用表量背光电压白屏无内容reset时序、DC电平、初始化序列示波器量reset波形全白屏显存映射、虚拟分辨率dmesg看fb_info参数花屏SPI mode、SCLK速率逻辑分析仪对比时序颜色偏色RGB顺序、字节序显示纯红/纯绿/纯蓝测试图横向错位行字节数对齐、MADCTL显示渐变色测试图随机噪点SCLK速率、电平转换降低速率确认刷新率低SPI message组织、DMA配置tracepoint统计耗时6. 性能优化与工程落地的几个建议6.1 局部刷新与脏矩形机制如果屏幕需要以30fps以上显示动态内容全屏刷新这条路在10MHz SPI速率下走不通。唯一的出路是局部刷新。以ST7789为例屏幕控制器支持设置GRAM的窗口区域通过CASET列地址设置和RASET行地址设置指令你可以只更新屏幕的某个矩形区域。脏矩形机制的实现思路是在fb_write或deferred IO的回调中维护一个位图记录哪些行被修改过然后每隔一段时间把连续修改的区域合并成几个大矩形只推送这些矩形到GRAM。实际效果非常可观。我对一块240x320的屏幕做过测试刷新一个240x10的进度条区域SPI数据量只有全屏刷新的1/32帧率提升空间巨大。但要注意窗口切换指令本身也有开销如果脏矩形过小反而会因为频繁切换窗口导致效率下降。工程上我建议脏矩形合并的最小宽度设置为16像素。6.2 背光调节与PWM参数SPI LCD的背光一般走PWM调光。设备树里用pwm-backlight节点或者直接在lcd节点下加backlight属性。PWM频率建议在10kHz到30kHz之间低于5kHz时人眼能感知闪烁高于30kHz时部分屏幕驱动芯片的调光线性度变差。有一个容易被忽略的点PWM归零和屏幕Sleep Out的时序配合。如果PWM关闭的同时屏幕进入睡眠模式再次唤醒时可能出现背光时序紊乱。我习惯在驱动suspend回调里先关PWM再发Sleep In命令resume时顺序相反。6.3 FrameBuffer模式下跑Qt的体验很多项目需要在这个FrameBuffer上跑Qt界面。Qt有linuxfb和eglfs两个插件可以跑FrameBuffer前者只做软件渲染后者会尝试用GPU。RK3568自带GPU在纯FrameBuffer模式下eglfs是可以直接对接fb设备的但由于没有DRM的plane合成能力GPU渲染的结果需要额外拷贝到FrameBuffer的显存里性能比主流的DRM/KMS路径差一些。实测下来用eglfs在FrameBuffer上跑简单控件界面问题不大但如果涉及视频播放或复杂动画建议还是走DRM流程。Qt界面在这个模式下要特别注意字体和控件布局对性能的影响。开启Qt的QT_ANIMATION_DURATION0关闭不必要的过渡动画能明显提升流畅度。6.4 从FrameBuffer平移到DRM的路径如果你的产品后续要升级到复杂UI或需要和摄像头、GPU做深度联动终归还是要走DRM。好消息是SPI LCD的核心驱动代码不用全部推翻。DRM框架下有一个drm_panel机制你可以把SPI LCD的初始化序列和液晶面板控制逻辑独立成drm_panel驱动然后把SPI传输和命令控制封装在panel里最后接一个虚拟的connector和encoder。这样工作量比直接编写一个完整DRM驱动小很多。另一个更省事的方案是继续保留FrameBuffer用DRM的drm_fbdev_emulation做兼容层这样既能享受DRM的现代管理方式又保留了/dev/fbX的接口用户态程序改动最小。不过这个方案的SPI LCD真实性还是依赖于你自己实现的drm_panel绕不开。收尾一点个人经验最后分享一个小经验设备树里的spi-max-frequency我一开始按屏幕控制器的标称值60MHz来写结果屏花到完全没法看降到10MHz后稳定运行一个多月没出过问题。后来查了波形才发现是PCB布局上SCLK走线太长振铃严重。所以调SPI LCD时永远先把速率降下来确认链路通畅再慢慢提上去别一上来就追求标称值。另外一个让我印象深刻的坑是——某批次的屏幕初始化序列里有一个控制内部稳压器的命令屏厂更新的代码里改了一个参数但同一个供应商给的另外一个型号屏幕还在用旧序列。结果就是同一条产线两种屏一种正常一种色彩发暗最后靠分型号加载不同初始化序列解决。遇到SPI LCD项目永远把屏幕型号和对应的初始化序列版本管理起来这比驱动代码本身更容易出问题。
返回列表