ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

STM32 LTDC驱动RGB屏黑屏:SDRAM刷新与时序带宽排查实战

STM32 LTDC驱动RGB屏黑屏:SDRAM刷新与时序带宽排查实战

1. 先把黑屏这件事的范围划清楚

上周一位做工业触摸终端的同行发来一段十几秒的视频:板子上电,背光已经亮起来,屏幕是均匀的纯黑,没有雪花、没有彩条、也没有闪一下再黑的过程,从头到尾就是彻底的黑。他用的方案是 STM32 通过 LTDC 驱动一块 7 寸 RGB 接口屏,显存放在外挂的 SDRAM 里。他试过把 LTDC 层的颜色改成红色,屏幕没反应;又试着把背景色改成绿色,屏幕还是黑的。折腾了一整晚,最后把 SDRAM 的刷新计数器随手改大了一倍,屏幕居然出图了,但跑几分钟又开始花。

这个案例很有代表性。STM32 LTDC 驱动 RGB 屏黑屏这个现象,看起来是显示问题,实际根子往往落在 SDRAM 上,而且是"刷新"和"时序"两件事拧在一起。LTDC 自己不产生图像,它只是一个老老实实的搬运工,按照像素时钟的节奏,从你指定的显存地址里一行一行地读数据,塞进 FIFO,再推到 RGB 并口上。只要显存那边供不上数据,或者供上来的数据本身是坏的,屏幕就只有黑屏、花屏两种结局。全黑说明系统的搬运链路根本没跑通;花屏说明跑通了但数据在途中被搞坏了。这两种现象指向的排查方向完全不同,所以第一步不是急着改代码,而是先把"黑屏"这个模糊的描述拆开。

这篇文章面向的是已经能点灯、能跑通 GPIO、手里有 RGB 屏和 SDRAM 硬件,但在 LTDC 上卡住的人。如果你连 SDRAM 都还没跑通,建议先去把 FMC 的读写测试做扎实再回来。下面我从排查顺序讲起,然后深入到 LTDC 与 SDRAM 之间的带宽和时序冲突,再把关键参数的计算过程一项项摊开,最后给一套我自己反复用过的递进式调试流程和一张现象对照表。文中涉及的具体数值都会说明推导过程,你可以直接套到自己的配置里。

1.1 三种"黑屏"必须先区分开

很多人把"屏幕不亮"和"黑屏"混为一谈,这是排查走弯路的第一个原因。我习惯把它分成三类,判断方法很简单,用肉眼加一个万用表就够了。

第一类是完全不亮,背光都没开。这种时候你把房间灯关掉,从侧面斜着看屏幕,如果连一点点微弱的灰白底都看不到,那就是背光电路的问题,跟 LTDC 一点关系都没有。RGB 屏的背光是独立的 LED 串,需要单独的升压或者恒流驱动,LTDC 只负责信号。这种情况去查 LTDC 是浪费时间。

第二类是背光亮但纯黑,也就是那位同行遇到的情况。侧面看能看到均匀的灰白底,说明背光正常,液晶层没有被驱动出任何图像。这一类的可能性集中在三处:LTDC 没有使能、层没有使能、显存读出来全是 0。

第三类是背光亮但有噪点或者横纹,这类反而是好消息,说明 LTDC 已经在扫描了,只是数据不稳或者时序不对。

区分第二类和第三类的意义在于:SDRAM 刷新不足的典型表现是花屏、噪点、图像撕裂,而不是纯黑。因为刷新不够时,SDRAM 里的电容电荷会缓慢泄漏,数据是逐位随机翻转的,表现出来就是雪花点和色块闪烁。如果你看到的是干干净净的纯黑,刷新计数器通常不是第一嫌疑,问题更可能出在 LTDC 的使能链路或者 SDRAM 根本没初始化成功。很多人一上来就怀疑刷新,改来改去反而把本来对的参数改乱了。

判断小技巧:把 LTDC 的背景色寄存器设成一个明显的颜色(比如纯红),层全部关闭。如果屏幕变成红色,说明 LTDC 的时序和使能都通了,问题在层和显存;如果还是黑的,说明 LTDC 根本没在扫描,往上查时钟和使能。

1.2 为什么刷新和时序要放在一起看

SDRAM 是动态存储器,靠电容存电荷,电荷会漏,所以必须周期性刷新。刷新的本质是"把整行读出来再写回去",这个过程会占用总线。LTDC 读显存也占用总线。两者共用同一个 FMC 控制器和同一片存储颗粒,天然存在竞争。

更麻烦的是时序。FMC 访问 SDRAM 时,行激活、列读写、预充电这些操作之间必须满足最小间隔,这些间隔由时序寄存器里的参数决定。参数配得松,访问慢但稳;配得紧,跑得快但容易出错。而 LTDC 是实时流式读取,它不像 CPU 那样可以等,FIFO 一旦被读空就直接输出错误数据。所以 LTDC 对 SDRAM 的时序余量要求,比 CPU 跑读写测试要苛刻得多。

我见过太多这样的情况:CPU 跑 SDRAM 读写测试,几 MB 数据比对全对,于是认为 SDRAM 稳了,结果一开 LTDC 就黑屏或者花屏。原因是 CPU 测试是突发式的、有间隙的,而 LTDC 是持续不断的。两者对"稳定"的定义根本不是一个量级。所以这两件事必须放在一起调,单独调任何一个都可能得出错误结论。

1.3 排查顺序:从时钟树往上倒推

我自己的固定顺序是:电源与时钟 → SDRAM 初始化 → SDRAM 读写自检 → LTDC 时序参数 → LTDC 层配置 → 显存地址与缓存 → 刷新与带宽余量。这个顺序是从下往上,每一层确认通过再走下一层,绝不跳步。

之所以从时钟树开始,是因为 LTDC 的像素时钟、FMC 的 SDRAM 时钟、CPU 主频之间有固定的倍频分频关系。比如某主流型号的 SDRAM 时钟只能是 HCLK 的二分频或三分频,而 LTDC 的像素时钟来自单独的 PLL 分频。如果你改了系统主频去做功耗优化,SDRAM 时钟跟着变,刷新计数器和时序参数的单位也跟着变,原来算好的值全部失效。这种"改了一个参数,整条链都歪了"的情况,在实际项目里非常常见,尤其是从开发板例程移植到自己板子上的时候。

我一般会在调试初期把时钟配置打印出来,或者用调试器直接看 RCC 相关寄存器,确认三个频率:HCLK、SDRAM 实际工作时钟、LTDC 像素时钟。这三个数字写在一张纸上贴在显示器边上,后面所有计算都以此为准。这一步花五分钟,能省掉后面几小时的怀疑人生。

2. LTDC 取数和 SDRAM 刷新的底层冲突

要理解为什么黑屏,得先搞清楚 LTDC 到底是怎么把数据从 SDRAM 搬到屏幕上的。这个过程不像很多人想的那么"智能",它非常机械,甚至有点笨。你把这一层想通了,后面所有的参数配置就都有了依据,而不是照着例程抄一堆数字然后祈祷它能跑。

2.1 LTDC 是如何一行一行搬数据的

LTDC 内部有两个 FIFO,分别对应两层。每一行开始的时候,它根据层配置里的帧缓冲地址、行长度、行间距,算出这一行在显存里的起始地址和字节数,然后向 FMC 发起读请求,把这一行的数据搬进 FIFO。FIFO 满了就停,FIFO 空了就继续搬。同时,像素输出端按照像素时钟的节奏,从 FIFO 里取数据推出去。

关键点在"FIFO 空了怎么办"。如果 FIFO 被取空,LTDC 不会暂停扫描(它没法暂停,屏幕的时序不能停),它只能输出 FIFO 里的残留或者默认值。表现出来就是这一行后半段颜色不对,或者整屏错乱。所以 LTDC 能否正常显示,本质上取决于FMC 能不能在 FIFO 被读空之前把下一批数据送过来。

这就引出一个很重要的概念:LTDC 需要的是有保障的带宽,而不是平均带宽。平均下来够用没用,必须在每一个瞬间都够用。SDRAM 的刷新操作恰恰是那个会周期性打断带宽供给的因素。刷新期间,总线被刷新命令占用,LTDC 的读请求只能排队。如果排队时间超过了 FIFO 的缓冲深度对应的时长,就会下溢。

2.2 刷新操作打断突发传输的代价

一次刷新操作要占用多长时间?可以粗略估算。刷新本质上是激活一行、读出来、写回去、预充电,这个过程至少包含一次行激活加一次预充电,也就是 tRC 的量级。以常见的 64ms 刷新 8192 行的颗粒为例,平均每 7.8 微秒就要刷新一次。如果 tRC 是 60 纳秒,那么刷新占用的时间比例大约是 60ns / 7.8us,不到百分之一。

听起来微不足道,对吧?问题在于它是突发占用,不是均匀分摊。刷新命令一旦发出,接下来几十纳秒内总线都不可用。而 LTDC 的 FIFO 深度是有限的,通常只能缓冲几十到一百多个像素的数据。假设 FIFO 深度是 64 个像素,像素时钟 50MHz,那么 64 个像素只够撑 1.28 微秒。刷新占用 60 纳秒,看起来还有余地。

但实际情况要糟得多。因为刷新往往和 CPU 的访问、DMA2D 的访问撞在一起。三个主设备同时抢总线,仲裁器一排队,实际阻塞时间可能是单个刷新操作的好几倍。这时候 FIFO 就见底了。我在实际项目里遇到过最极端的场景:CPU 在中断里往显存刷一小块区域,正好和刷新、LTDC 读数三者撞上,屏幕上出现一条固定的横线,位置每次都一样。查了很久才定位到是总线竞争导致的瞬时下溢。

2.3 带宽账必须提前算清楚

与其等到出问题再猜,不如一开始就把带宽算出来。这个账不难算,但很多人从来没算过,直接抄了例程的参数就上,分辨率一提高就出问题。

先说 LTDC 这边的需求。以 1024×600 分辨率、RGB565(每像素 2 字节)、60Hz 刷新率为例,每秒需要搬运的字节数是:1024 × 600 × 2 × 60 = 73,728,000 字节,约 73.7 MB/s。这只是有效像素的部分,不考虑消隐期。实际上 LTDC 在消隐期不读数据,所以真实占用就是 73.7 MB/s。

再说 SDRAM 能提供多少。16 位数据总线、时钟 90MHz 的情况下,理论峰值带宽是 16 ÷ 8 × 90M = 180 MB/s。看起来有 2.4 倍余量,很安全。

但这个 180 MB/s 是理论值,实际可用带宽要打折扣。第一,SDRAM 的读写切换、行激活、预充电都有开销,连续突发读写时效率大概在 70% 到 85% 之间;第二,刷新要占掉一部分;第三,CPU 和 DMA2D 还要用。按 75% 效率算,实际可用约 135 MB/s,扣掉 LTDC 的 73.7 MB/s,还剩 61 MB/s 给其他主设备,勉强够用。

如果分辨率提到 1280×800,需求变成 1280 × 800 × 2 × 60 = 122.9 MB/s,余量就非常紧张了。这时候如果再叠加 DMA2D 做图层混合,很容易崩。而很多人出问题的根因就在这里——分辨率一提高,SDRAM 的时钟却没跟着提,或者刷新参数没重算,带宽直接不够,表现为黑屏(FIFO 持续下溢导致整帧无效)或者严重花屏。

提醒一点:如果你用的是 RGB888(每像素 3 字节,实际存储常按 4 字节对齐),带宽需求直接翻倍甚至更多。选像素格式的时候一定要把带宽算进去,别只看显存占用。

3. 参数计算:把数字算出来再写代码

这一节是全篇最"硬"的部分。我不打算直接给一堆数字让你抄,因为每块板子的时钟、每颗颗粒的参数都不一样,抄错了比不抄更糟。我给出的是每个参数的推导方法,你代入自己的值就能算出正确结果。

3.1 像素时钟与 FMC 时钟的配比

像素时钟决定了 LTDC 往外推数据的速度,它由屏幕的时序参数决定。计算公式是:像素时钟 = (水平总周期) × (垂直总周期) × 刷新率。

水平总周期 = 有效宽度 + HSW + HBP + HFP,垂直总周期 = 有效高度 + VSW + VBP + VFP。这些参数必须从屏幕的数据手册里拿,不能猜。以一块常见的 7 寸 1024×600 屏为例,手册给的 HSW=20、HBP=140、HFP=160、VSW=3、VBP=20、VFP=12。那么水平总周期 = 1024+20+140+160 = 1344,垂直总周期 = 600+3+20+12 = 635。60Hz 下像素时钟 = 1344 × 635 × 60 ≈ 51.2 MHz。

LTDC 的像素时钟来自 PLL 的某个分频输出。你要做的是找一个分频组合,让输出尽量接近 51.2 MHz,宁可略低不要略高。略高会导致刷新率超标,有些屏会黑屏保护;略低只是刷新率略降,人眼看不出来。比如 PLL 输出 204.8 MHz,四分频得 51.2 MHz,正好。如果找不到整数分频,可以用 5 分频得 40.96 MHz,也能点亮,只是刷新率降到 48Hz 左右。

这里有个容易忽略的点:像素时钟不能随便改成"差不多就行"。屏幕内部的时序控制是靠像素时钟同步的,偏差太大会导致同步信号位置漂移,表现为图像整体偏移、边缘有黑边,严重的直接黑屏。所以这个值必须按公式算。

3.2 刷新计数器到底填多少

这是最容易出错的一项。FMC 的刷新计数器是一个递减计数器,减到零就发一次刷新命令。要填的值由刷新周期、行数、SDRAM 时钟共同决定。

公式是:计数值 = (刷新周期 ÷ 行数) × SDRAM 时钟频率 − 安全余量。

举具体例子。颗粒手册标称 64ms 刷新 8192 行,那么每行的刷新间隔是 64ms ÷ 8192 = 7.8125 微秒。SDRAM 时钟 90MHz,周期 11.11 纳秒。7.8125 微秒 ÷ 11.11 纳秒 = 703.1,取整 703。减去安全余量 20,得 683。这个 20 是业界习惯做法,用来补偿计数启动、总线仲裁带来的延迟。

如果填 0 会怎样?有些实现里填 0 表示禁用,有些表示每个时钟都刷新。前者会导致数据缓慢丢失,后者会占满总线,两者都是灾难。我见过一个案例,初始化结构体里刷新计数字段忘了赋值,默认 0,结果就是开机图像正常,大约十几秒后开始出现零星白点,然后越来越多,最后整屏花掉。这种"延迟发作"的故障最难查,因为第一反应总是怀疑接线或者屏坏了。

反过来,把计数值填得过大(比如 2000)也不对。刷新频率不够,同样会丢数据,只是丢得慢一些。所以这个数字必须老老实实算。

还有一个连带影响:如果你改动了系统主频,SDRAM 时钟跟着变,这个计数值必须重算。从 90MHz 改到 84MHz,同样的 7.8125 微秒对应的计数值变成 656-20=636。差值 47,看起来不大,但已经足够让刷新变得不够及时。这是我强调"把三个频率写在纸上"的原因。

3.3 FMC 时序参数逐项推导

SDRAM 的时序参数有好几个,每个都对应颗粒手册里的一个时间指标,单位要换算成 SDRAM 时钟周期数并向上取整。我用一块典型的 16 位、64Mbit 颗粒举例,它的手册参数大概是:tRCD=18ns、tRP=18ns、tRAS=42ns、tRC=60ns、tMRD=2tCK、tXSR=70ns、tWR=12ns。SDRAM 时钟 90MHz,周期 11.11ns。

换算结果:tRCD 需要 18/11.11=1.62,向上取整为 2;tRP 同样为 2;tRAS 需要 42/11.11=3.78,取 4;tRC 需要 60/11.11=5.4,取 6(注意 tRC 必须大于等于 tRAS+tRP,4+2=6,正好吻合);tMRD 手册直接给 2 个时钟,填 2;tXSR 需要 70/11.11=6.3,取 7(这里要多留一个,因为退出自刷新后还有额外延迟);tWR 需要 12/11.11=1.08,取 2。

这几个值填进时序寄存器之后,还要注意两个隐藏项。一个是读管道延迟,当 HCLK 超过某个阈值(通常是 120MHz)时,必须设置读数据的管道延迟,否则读回来的数据会错位。这个参数在寄存器手册里往往只有一行说明,很容易漏掉,而它的症状恰恰是"写进去的和读出来的不一样",让人误以为是 SDRAM 坏了。另一个是写保护时间,有些颗粒在写完最后一个数据后需要额外等待才能预充电,参数没设够会导致偶发写入失败。

3.4 显存地址、对齐与缓存配置

显存地址这一项,坑主要在三个地方。

第一个是地址必须落在 SDRAM 的映射区间内,而且要对齐。LTDC 的帧缓冲地址寄存器对低几位有要求,具体看型号,常见的是要求 8 字节或者 32 字节对齐。如果你的数组定义没有加对齐属性,编译器可能把它放在任意位置,导致 LTDC 读到的地址偏移了几个字节,表现出来就是图像整体错位,或者前半行正常后半行花。

第二个是行长和行间距寄存器的"+3"。这两个寄存器填的不是直接的字节数,而是字节数减一……不对,是字节数加上 3。这个设计非常反直觉,我第一次看手册的时候反复确认了三遍。假设一行是 1024 个像素、RGB565,那么一行是 2048 字节,寄存器里要填 2048+3=2051。行间距(pitch)同理。如果这里填错,每一行的起点就全歪了,屏幕会出现斜条纹或者重影。

第三个,也是最隐蔽的,是缓存一致性。如果你的芯片带 D-Cache(比如 Cortex-M7 内核的型号),CPU 写显存的时候数据先进 Cache,不一定会立刻写回 SDRAM。而 LTDC 是直接读 SDRAM 的物理地址,它看不到 Cache 里的新数据。结果就是 CPU 明明改了显存,屏幕却纹丝不动,或者显示的是几帧之前的旧内容。这个问题在调试时特别迷惑,因为你在调试器里看内存是"对的"(调试器读内存也会走 Cache 或者强制同步),但屏幕就是不对。

解决办法有两个:把 SDRAM 区域配置成非缓存或者写通模式,或者在每次更新显存后手动清理 Cache。前者的性能损失可以接受,后者更灵活但需要记得在每个更新点都调用。我个人的习惯是显示缓冲区统一配置成写通模式,简单粗暴,省得后面忘了清理。

4. 实操调试:从纯色到图片的递进验证

前面讲的是原理和计算,这一节讲怎么落地。我调试这类问题从来不追求"一次点亮",而是设计一条递进的验证路径,每一步只引入一个新变量。这样一旦出问题,责任范围非常明确。

4.1 第一步:绕开 SDRAM,用内部 SRAM 点亮色块

这是我最推荐的第一步,也是最容易被跳过的一步。很多人的第一反应是"我的显存必须放 SDRAM,因为内部 SRAM 不够大"。但验证 LTDC 本身是否工作,根本不需要一整帧。

小分辨率可以用内部 SRAM 试。比如先用 320×240、RGB565,一帧只需要 153,600 字节,大多数型号的内部 SRAM 放得下。把帧缓冲地址指向内部 SRAM,填一个纯色,打开层和 LTDC,看屏幕有没有颜色。

这一步能验证的东西很多:像素时钟对不对、同步信号的极性对不对、HSW/HBP/HFP 这些参数对不对、层的使能顺序对不对。如果这一步都点不亮,那问题完全在 LTDC 配置或者硬件连接上,跟 SDRAM 无关,别再去折腾刷新计数器了。

判断极性有个笨办法但很好用:如果屏幕上有图像但整体偏移、左边或右边有黑边、或者图像上下滚动,就是同步极性或者消隐参数不对。如果屏幕完全没反应,先查 DE 模式还是 SYNC 模式选错了没有——有些屏只认 DE 模式,你按 SYNC 模式配就是不出图。

4.2 第二步:把显存搬到 SDRAM,先验证静态显示

LTDC 本身跑通之后,把帧缓冲地址改到 SDRAM,其他配置不动,仍然是纯色显示。这一步如果黑屏,问题就锁定在 SDRAM 上。

这时候不要急着改 LTDC 的参数,先做 SDRAM 自检。写一段代码,往整片 SDRAM 写测试图案,读回来比对。图案要用两种:0x5555 和 0xAAAA,还有 0x0000 和 0xFFFF。为什么要用两种互补的?因为地址线或者数据线短路、虚焊的时候,单一图案可能凑巧通过,互补图案能查出来。

自检通过之后,再看屏幕。如果自检通过但屏幕还是黑,那基本可以确定是 LTDC 的读带宽或者缓存问题。可以先把 Cache 关掉试试,如果关掉就正常了,那就是缓存一致性问题。

4.3 第三步:逐步加压到目标分辨率

纯色能显示之后,别急着上图片,先上渐变或者棋盘格。棋盘格能暴露行间距配置错误,渐变能暴露带宽不足。

我的加压顺序是:先固定分辨率不变,把刷新率从 30Hz 提到 60Hz,看是否稳定;然后固定刷新率,把分辨率从 640×480 提到 800×480,再到 1024×600。每提一档,观察几分钟,看有没有偶发噪点或者横线。

横线是最典型的带宽不足信号。如果横线的位置固定不变,说明是某一行的读取总是被某个周期性事件打断,通常是刷新或者某个定时器中断里的内存访问;如果横线位置随机跳动,说明是总线竞争导致的随机下溢。

4.4 第四步:双缓冲与 DMA2D 的配合

到了产品阶段,基本都会用双缓冲加 DMA2D 做界面。这两者和 LTDC 抢总线的程度比单缓冲高得多,必须重新评估带宽。

双缓冲的做法是准备两块帧缓冲,LTDC 读 A 的时候 CPU 或 DMA2D 写 B,写完后在垂直消隐期间切换 LTDC 的地址寄存器,这样不会出现撕裂。切换的时机很关键,不能随时切,必须在消隐期切。大多数型号的 LTDC 提供了垂直消隐中断或者重载机制,用那个。

DMA2D 做填充和混合的时候,是连续的大块读写,对总线的冲击比 CPU 零散访问大得多。我的经验是给 DMA2D 的访问让出优先级,或者把它的操作拆成小块,中间留出空隙给 LTDC。如果 DMA2D 一次搬一整个屏幕,很可能会让 LTDC 在那一段时间里持续下溢。可以开 LTDC 的下溢中断,一旦触发就说明带宽被抢得太狠了,这时候要么降 DMA2D 的优先级,要么把它的任务拆细。

5. 现场问题速查与避坑清单

调试到这一步,大部分情况都能定位了。下面这张表是我这些年攒下来的现象对照,遇到问题时先扫一眼,能省掉不少时间。

5.1 常见现象与对应原因速查

现象最可能的原因快速验证方法
背光不亮,侧面看无灰底背光电路未工作测背光升压输出
背光亮,全屏纯黑,改背景色无效LTDC 未使能或像素时钟缺失查 LTDC 使能位与 PLL 分频
背光亮,全屏纯黑,改背景色有效层未使能或显存地址错误查层使能位与帧缓冲地址
开机正常,数秒后出现白点并增多刷新计数值过小或未使能重算刷新计数器
固定位置的横线周期性总线竞争导致 FIFO 下溢开下溢中断,调整优先级
随机跳动的横线或噪点SDRAM 时序余量不足加大各时序参数,观察是否改善
图像整体偏移,边缘黑边消隐参数或同步极性错误对照屏幕手册逐项核对
图像斜条纹或重影行长/行间距寄存器填错检查是否漏了加 3
CPU 改了显存但屏幕不变Cache 未清理,一致性失效关 Cache 或配置写通模式
高低温下表现不同时序余量不足或刷新不够按最坏温度重新计算余量

5.2 几个反直觉的坑

第一个坑:SDRAM 读写测试全过,不代表 LTDC 能用。前面说过,CPU 测试是突发有间隙的,LTDC 是持续流式的。我现在的做法是,SDRAM 自检通过之后,还要再加一项"长时间连续读"测试,用一个定时器不停地读同一块区域,读几个小时不出错,才算过关。

第二个坑:时序参数全都满足手册最小值,仍然可能不稳。手册给的是典型条件下的最小值,实际 PCB 的走线长度、阻抗、负载都会影响信号质量。尤其是 SDRAM 的时钟线如果走得不理想,采样点会偏移。我的做法是在最小值的基础上普遍加一到两个周期作为余量,代价是性能略降,但稳定性提升明显。如果需要压性能,再一点点往下减,减到出问题为止,然后回退一档。

第三个坑:刷新计数器不是越大越安全。有人为了"保险",把刷新频率提高到远超手册要求,结果总线被刷新占满,LTDC 反而没数据可读。刷新只要满足手册要求就行,多加一点余量可以,翻倍就过头了。

第四个坑:改了主频忘了改其他。系统主频一动,SDRAM 时钟、刷新计数、时序参数、像素时钟全都要跟着重算。我见过有人为了省电把主频降低,结果屏幕开始闪,查了两天才发现是刷新计数器没跟着改。

5.3 上电顺序与初始化细节

SDRAM 的初始化序列对时序很敏感,尤其是上电后的等待时间。手册通常要求上电后至少等待 100 微秒(有些颗粒要 200 微秒)再发第一条命令,这段时间是让电源稳定、内部电路复位。如果等待时间不够,初始化命令可能被忽略,SDRAM 就处于未定义状态,后续所有读写都不可靠。

初始化序列一般是:延时 → 预充电所有 bank → 至少 8 次自动刷新 → 加载模式寄存器 → 配置刷新计数器 → 正常读写。这中间的每一步之间都要有足够的间隔,不是写完就能立刻发下一条。模式寄存器里要配置突发长度、CAS 延迟、突发类型、写模式,这些参数和时序参数要匹配,比如 CAS 延迟设成 3,那时序里对应的读延迟也要按 3 来算。

还有一个细节:模式寄存器的写入要用特定的 bank 地址和命令组合,写错了会被当成普通写操作,SDRAM 的模式根本没变。这个在寄存器手册里写得很清楚,但代码里如果用的是封装好的库函数,有时候看不出来,出问题时可以抓一下总线波形确认。

最后提醒一句:调试 LTDC 和 SDRAM 的时候,示波器或者逻辑分析仪的价值远大于调试器。时序类的问题,看波形十分钟能定位,看代码可能要看一天。如果条件允许,把像素时钟、HSYNC、VSYNC、DE 和 SDRAM 的时钟、片选、读写信号都引出来,出问题时先看波形对不对,再回头改代码。

我个人在实际项目里的体会是,这类黑屏问题九成以上不是某一个参数写错,而是几个环节的余量同时被吃掉。比如时序参数按最小值配、刷新计数器按理论值算、分辨率又卡在带宽边缘,单独看每一项都"刚好够",叠加起来就崩了。所以我的习惯是每个参数都留一档余量,宁可性能打个八折,也要保证现场跑几个月不出问题。毕竟客户不会关心你的带宽利用率有多高,他们只关心屏幕会不会在关键时刻黑掉。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进