動(dòng)時(shí)序?qū)崙?zhàn):從上下電到DMA刷屏的完整指南)
第一次拿到LAT1313這塊JDI屏的時(shí)候我心里多少有點(diǎn)不以為然。LCD驅(qū)動(dòng)嘛上電、配置寄存器、打點(diǎn)三步走網(wǎng)上教程一抓一大把。結(jié)果現(xiàn)實(shí)很快給了我一記悶棍連續(xù)兩個(gè)晚上屏要么全黑要么鋪滿橫紋甚至有一版初始化序列完全照著官方示例寫的還是會(huì)在點(diǎn)亮之后三秒內(nèi)自動(dòng)熄滅。后來把邏輯分析儀掛在總線上對(duì)著規(guī)格書一頁頁核對(duì)才發(fā)現(xiàn)所有問題都指向同一個(gè)源頭驅(qū)動(dòng)時(shí)序。這篇文章就是那幾天踩坑的總結(jié)圍繞 LAT1313 這塊 JDI 屏的驅(qū)動(dòng)時(shí)序展開同時(shí)也覆蓋了 JDI 系 TFT 屏普遍存在的上電時(shí)序、初始化節(jié)奏、數(shù)據(jù)刷屏同步和 GOA 掃描時(shí)序問題。如果你正在被一塊屏的偶發(fā)異常折磨或者準(zhǔn)備在嵌入式項(xiàng)目里接入一款新的 LCD 模組這篇筆記應(yīng)該能幫你省下幾個(gè)通宵。1. 點(diǎn)亮 LAT1313 之前先把時(shí)序兩個(gè)字當(dāng)回事1.1 這塊 JDI 屏在我的系統(tǒng)里扮演什么角色先交代一下背景。LAT1313 是 JDI 面向工業(yè)顯示領(lǐng)域的一顆 TFT-LCD 模組在我這邊是作為主顯示接入 MCU 系統(tǒng)的。屏本身不復(fù)雜但 JDI 的模組有一個(gè)共同特點(diǎn)對(duì)時(shí)序的要求非常較真。它不像一些消費(fèi)級(jí)屏稍微忍一下也能出圖JDI 屏一旦時(shí)序不對(duì)要么直接不亮要么亮出來的畫面讓你懷疑人生。JDI 規(guī)格書第一頁往往是接口框圖里面會(huì)明確畫出電源引腳、信號(hào)引腳、背光引腳。LAT1313 的接口里除了像素?cái)?shù)據(jù)通道還包含一組控制信號(hào)復(fù)位、片選、時(shí)鐘、數(shù)據(jù)/命令選擇、TETearing Effect等等。很多人一開始只盯著數(shù)據(jù)線接得對(duì)不對(duì)忽略了控制信號(hào)之間的相對(duì)時(shí)間關(guān)系這是后續(xù)一切詭異現(xiàn)象的溫床。我在項(xiàng)目里用 MCU 的 FSMC 接口去掛這塊屏初始化用并行總線寫寄存器圖像數(shù)據(jù)用 DMA 搬運(yùn)。這套組合本身沒毛病但 FSMC 的讀寫時(shí)序參數(shù)、DMA 啟動(dòng)時(shí)機(jī)、TE 信號(hào)的處理方式每一步都在跟 LAT1313 的時(shí)序要求對(duì)齊。如果你用的是 SPI 接口版本過程類似只是信號(hào)少一點(diǎn)但等待和同步的邏輯完全一致。1.2 為什么時(shí)序問題這么容易背鍋LCD 是一個(gè)數(shù)字和模擬混合的設(shè)備。CPU 發(fā)出去的命令、數(shù)據(jù)都是數(shù)字信號(hào)但屏內(nèi)的源極驅(qū)動(dòng)、柵極驅(qū)動(dòng)、Gamma 電路全是模擬電路。數(shù)字時(shí)序稍微偏一點(diǎn)模擬電路不會(huì)直接報(bào)錯(cuò)而是表現(xiàn)為黑屏、花屏、閃屏、橫線、偏色這類讓你無從下手的現(xiàn)象。這也是時(shí)序問題最坑的地方它沒有日志沒有報(bào)錯(cuò)碼只有看起來不對(duì)勁的畫面。我見過不少人最后把鍋甩給屏的硬件質(zhì)量實(shí)際上規(guī)格書里時(shí)序圖早就畫得明明白白只是沒人逐項(xiàng)核對(duì)。解決時(shí)序問題的核心思路很簡(jiǎn)單把規(guī)格書里的每一段時(shí)間參數(shù)都和實(shí)際波形上的每一段對(duì)應(yīng)起來。1.3 我遇到的第一個(gè)黑屏案例那次黑屏的原因特別低級(jí)上電順序錯(cuò)了。我先給了數(shù)字電源然后去寫代碼調(diào)試串口忘了給模擬電源屏當(dāng)然毫無反應(yīng)。補(bǔ)上之后屏亮了但十分鐘后我又發(fā)現(xiàn)一個(gè)更隱蔽的問題——復(fù)位信號(hào)的釋放時(shí)機(jī)不對(duì)導(dǎo)致屏內(nèi)部的 Power On Reset 沒有正確觸發(fā)初始化命令發(fā)了一堆屏仍然是白屏。這個(gè)問題后面詳細(xì)拆解。2. 電源域與上下電時(shí)序屏沒點(diǎn)亮九成是這里錯(cuò)了2.1 為什么必須按順序上電TFT-LCD 模組內(nèi)部一般有多個(gè)電源域邏輯電源VDD/VCI、模擬電源AVDD、柵極驅(qū)動(dòng)電源VGH/VGL、背光 LED 電源。LAT1313 這類 JDI 模組電源域之間是有明確先后關(guān)系的。拿常見架構(gòu)來說邏輯電源先建立給時(shí)序控制器TCON供電讓內(nèi)部狀態(tài)機(jī)進(jìn)入復(fù)位準(zhǔn)備狀態(tài)接著是模擬電源建立給源極驅(qū)動(dòng)和 Gamma 偏壓電路供電最后才是背光。為什么必須按這個(gè)順序如果背光先亮而柵極驅(qū)動(dòng)和源極驅(qū)動(dòng)還沒工作整塊屏幕會(huì)呈現(xiàn)所有像素都不受控的狀態(tài)這時(shí)候的漏電和電荷積累是有可能損傷面板的。同樣如果模擬電源先于邏輯電源建立TCON 內(nèi)部電路可能處于不確定狀態(tài)復(fù)位后也容易出現(xiàn)寄存器默認(rèn)值異常。我在實(shí)際項(xiàng)目里畫了一塊簡(jiǎn)單的電源控制板開機(jī)時(shí) MCU 通過 GPIO 控制三個(gè)電源芯片的使能腳按順序延時(shí)開啟// 上電順序示例邏輯電源 - 模擬電源 - 背光 power_enable(POWER_LOGIC); delay_ms(20); // 等 TCON 邏輯供電穩(wěn)定 power_enable(POWER_ANALOG); delay_ms(50); // 等源極驅(qū)動(dòng)和偏壓建立 lcd_reset(0); // 拉低復(fù)位開始復(fù)位流程 delay_ms(10); lcd_reset(1); // 釋放復(fù)位 delay_ms(120); // 等待內(nèi)部初始化完成 backlight_enable(1); // 最后開背光這個(gè)序我從那以后一直在用從未失手。2.2 典型上下電時(shí)序參數(shù)怎么看JDI 規(guī)格書里的時(shí)序圖通常會(huì)畫三根或四根電源軌并在波形上標(biāo)出 t1、t2、t3 這類參數(shù)。以常見的 JDI TFT 模組為例需要重點(diǎn)關(guān)注的參數(shù)有參數(shù)含義典型經(jīng)驗(yàn)值tVCI邏輯電源建立時(shí)間0~10ms 內(nèi)穩(wěn)定tRES復(fù)位脈沖寬度至少 10us建議留余量到 10mstRES-release復(fù)位釋放到 Sleep Out 指令時(shí)間至少 120mstAVDD-after-VCI模擬電源相對(duì)邏輯電源的延遲不早于邏輯電源穩(wěn)定后 0ms建議 10ms 以上tBacklight背光使能相對(duì) Display ON 的延遲5ms~20ms避免顯示未初始化就亮背光注意上面這些數(shù)值是典型參考值具體到你這顆 LAT1313一定要以規(guī)格書標(biāo)注為準(zhǔn)。我曾經(jīng)在另一個(gè)項(xiàng)目里只看了一份相近型號(hào)的規(guī)格書結(jié)果把復(fù)位低電平時(shí)間設(shè)成了 1ms正好卡在邊界屏有時(shí)亮有時(shí)不亮排查了很久才發(fā)現(xiàn)是復(fù)位時(shí)間不夠。2.3 實(shí)測(cè)電源上升斜率過慢導(dǎo)致的隱患這里有個(gè)容易忽略的坑電源電壓建立不等于電壓值到了還要看上升斜率。JDI 屏內(nèi)部的 Power On Reset 電路一般會(huì)在電壓爬升到某個(gè)閾值時(shí)觸發(fā)復(fù)位如果供電電源的上升沿特別緩慢比如是 RC 軟啟動(dòng)電路屏內(nèi)的 POR 可能反復(fù)觸發(fā)或者根本不觸發(fā)。我調(diào)試 LAT1313 時(shí)遇到過一次用實(shí)驗(yàn)室直流電源供電屏一切正常換成板載 DCDC 后偶爾上電白屏。用示波器看電源曲線發(fā)現(xiàn) DCDC 的輸出電容太大電壓從 0 到 3.3V 爬了將近 100ms中間有一段恰好落在 POR 閾值附近震蕩等于屏在不停地重啟。解決辦法是給 POR 電路加一個(gè)外部 RC 復(fù)位電路或者把上升沿斜率控制在規(guī)格書要求的范圍內(nèi)。這個(gè)細(xì)節(jié)不實(shí)測(cè)很難發(fā)現(xiàn)。3. 初始化序列的節(jié)奏控制不是把寄存器寫完就完事3.1 每條 DCS 指令之間的等待為什么重要上電順序解決之后屏應(yīng)該能亮背光了但畫面大概率還是白的。接下來要寫初始化序列。很多人以為初始化就是照著官方示例把一長(zhǎng)串寄存器值灌進(jìn)去灌完就出圖。實(shí)際操作中每條指令之間往往有隱含的時(shí)間要求。LCD 的時(shí)序控制器收到一條命令后內(nèi)部狀態(tài)機(jī)需要切換。比如 Sleep Out 指令之后內(nèi)部的 DC-DC、振蕩器、Gamma 電路需要時(shí)間啟動(dòng)。如果緊接著發(fā) Display ONTCON 可能還沒準(zhǔn)備好輸出掃描信號(hào)結(jié)果就是命令發(fā)了不生效或者屏亮一下又滅。LAT1313 的初始化序列我的經(jīng)驗(yàn)是要把流程拆成幾個(gè)階段每個(gè)階段之間留出明確的等待時(shí)間而不是一個(gè) for 循環(huán)把所有寄存器一口氣寫完。3.2 我整理的一份初始化流程模板下面這個(gè)流程是按照常見 JDI TFT-LCD 的初始化習(xí)慣整理的具體寄存器值以你的 LAT1313 規(guī)格書為準(zhǔn)但節(jié)奏控制適用于大多數(shù)同類型屏第一階段復(fù)位與基礎(chǔ)等待RESX 拉低保持至少 10msRESX 釋放等待 120ms第二階段Sleep Out發(fā)送 0x11Sleep Out等待 120ms期間不要發(fā)其他指令部分屏可以發(fā)讀狀態(tài)但寫命令最好等第三階段基礎(chǔ)設(shè)置發(fā)送 MADCTL0x36設(shè)置掃描方向發(fā)送 COLMOD0x3A設(shè)置像素格式比如 0x55 表示 16bit/像素發(fā)送調(diào)整 Gamma 的寄存器組第四階段喚醒顯示發(fā)送 0x29Display ON等待 20ms再打開背光對(duì)應(yīng)到代碼上大概是這樣的結(jié)構(gòu)void LAT1313_Init(void) { // 復(fù)位 LCD_RESET_CLR; delay_ms(20); LCD_RESET_SET; delay_ms(120); // Sleep Out LCD_WriteCmd(0x11); delay_ms(120); // 像素格式與掃描方向 LCD_WriteCmd(0x36); LCD_WriteData(0x00); // 按實(shí)際屏的安裝方向調(diào)整 LCD_WriteCmd(0x3A); LCD_WriteData(0x55); // RGB565 // Gamma 等廠商寄存器按規(guī)格書填寫 // ... // Display ON LCD_WriteCmd(0x29); delay_ms(20); // 開背光 backlight_enable(1); }這個(gè)模板在我調(diào)試過的多塊屏上都適用。你可能會(huì)覺得 120ms 太長(zhǎng)了浪費(fèi)啟動(dòng)時(shí)間。但在量產(chǎn)階段如果你為了省這 120ms 而出現(xiàn)萬分之一概率的點(diǎn)不亮代價(jià)遠(yuǎn)大于省下的時(shí)間。啟動(dòng)慢一點(diǎn)沒關(guān)系穩(wěn)定最重要。3.3 一個(gè)反復(fù)復(fù)現(xiàn)的白屏一閃又黑問題我在 LAT1313 上遇到過一個(gè)特別典型的時(shí)序問題初始化代碼全部執(zhí)行完背光也亮了但屏幕先亮白然后過兩三秒自動(dòng)變黑。反復(fù)復(fù)現(xiàn)而且和代碼改動(dòng)無關(guān)。排查方向一度懷疑背光控制和屏內(nèi)部保護(hù)。后來用示波器抓 AVDD 和 VGH 的電壓發(fā)現(xiàn)一個(gè)現(xiàn)象Display ON 之后AVDD 電壓會(huì)短暫跌落然后 VGH 電壓無法建立面板進(jìn)入保護(hù)狀態(tài)。根源是 Sleep Out 之后等待不夠內(nèi)部 DC-DC 還沒把電荷泵電壓建立穩(wěn)定Display ON 時(shí)瞬間負(fù)載增大電壓被拉垮。把 Sleep Out 后的等待從 50ms 改成 120ms問題徹底消失。規(guī)格書里的等待時(shí)間不是建議值是底線。4. 像素?cái)?shù)據(jù)發(fā)送的時(shí)序與 FSMCDMA 的同步坑4.1 并行接口的讀寫時(shí)序參數(shù)屏能亮、能顯示靜態(tài)畫面之后性能問題就浮出來了。如果只用 CPU 一條條寫像素刷一幀的時(shí)間會(huì)讓人崩潰。所以我用 FSMC 接口掛載 LAT1313再用 DMA 搬運(yùn)數(shù)據(jù)。但 FSMC 不是隨便配個(gè)默認(rèn)參數(shù)就能用的它需要與屏的讀寫時(shí)序?qū)R。并行接口讀寫的核心參數(shù)包括地址建立時(shí)間Address Setup、地址保持時(shí)間Address Hold、數(shù)據(jù)建立時(shí)間Data Setup、數(shù)據(jù)保持時(shí)間Data Hold。這些時(shí)間分別對(duì)應(yīng) FSMC 向 LCD 發(fā)出的片選、讀寫使能、地址、數(shù)據(jù)信號(hào)之間的相對(duì)位置。LAT1313 的規(guī)格書會(huì)給出一個(gè)讀寫周期的時(shí)序圖上面標(biāo)注了 tDS數(shù)據(jù)建立時(shí)間、tDH數(shù)據(jù)保持時(shí)間、tCS片選脈沖寬度等。FSMC 寄存器里的 DataSetupTime 和 AddressSetupTime 就用來控制這些時(shí)間。4.2 把 FSMC 的時(shí)序配置與 LAT1313 對(duì)齊用 STM32 的 FSMC 來舉例。FSMC 的時(shí)序配置項(xiàng)里AddressSetupTime 和 DataSetupTime 的單位是 HCLK 周期。假設(shè) HCLK 是 72MHz一個(gè)周期約 13.9ns。如果 LAT1313 規(guī)格書要求數(shù)據(jù)建立時(shí)間至少 30ns那 DataSetupTime 至少需要 3 個(gè) HCLK。我實(shí)際用的配置大概是這樣的具體值按屏的規(guī)格書換算FSMC_NORSRAM_TimingTypeDef Timing {0}; Timing.AddressSetupTime 5; // 地址建立時(shí)間 Timing.AddressHoldTime 2; // 地址保持時(shí)間 Timing.DataSetupTime 8; // 數(shù)據(jù)建立時(shí)間 Timing.BusTurnAroundDuration 0; Timing.ClkDivision 0; Timing.DataLatency 0; Timing.AccessMode FSMC_ACCESS_MODE_A;這里要說明一個(gè)很實(shí)在的問題如果你把時(shí)序配得太緊讀寫操作可能偶發(fā)失敗表現(xiàn)為花屏、字符錯(cuò)位、某些區(qū)域顏色不對(duì)配得太松刷新速率會(huì)被拖慢滾動(dòng)列表時(shí)能明顯感覺掉幀。最好的方式是根據(jù)規(guī)格書的最小值算出寄存器值再往上加 20% 余量。FSMC 里面還有一個(gè)坑LCD 的寄存器寫入和數(shù)據(jù)寫入共用同一片地址空間靠 RS 引腳在 FSMC 里通常映射到地址線 A0 或 A16區(qū)分。如果 FSMC 的地址建立時(shí)間不夠RS 信號(hào)可能和片選信號(hào)競(jìng)爭(zhēng)導(dǎo)致 CPU 想寫寄存器時(shí)屏實(shí)際收到的是數(shù)據(jù)。這個(gè)問題的典型現(xiàn)象是初始化登錄后畫面整體偏移或者顏色通道錯(cuò)亂。4.3 DMA 連續(xù)刷屏與撕裂效應(yīng)的根源初始化好了FSMC 也配好了開始用 DMA 刷屏。DMA 刷一陣子之后可能會(huì)遇到一個(gè)在靜態(tài)畫面下絕對(duì)看不到的問題屏幕滾動(dòng)或刷新時(shí)畫面中間出現(xiàn)一條橫向斷裂帶斷帶以上的畫面是舊幀斷帶以下是新幀。這就是撕裂效應(yīng)Tearing。撕裂的產(chǎn)生原理不復(fù)雜。LCD 內(nèi)部有一塊顯存GRAMTCON 持續(xù)從 GRAM 讀數(shù)據(jù)刷新面板而 MCU 通過接口往 GRAM 寫數(shù)據(jù)。如果 MCU 寫入的速度追上了 TCON 的讀取指針兩者在 GRAM 里撞車屏幕上就會(huì)有一條明顯的分界線。解決撕裂的標(biāo)準(zhǔn)做法是使用 TETearing Effect信號(hào)。TE 是屏主動(dòng)輸出的一個(gè)脈沖表示我現(xiàn)在剛好刷新完一幀GRAM 讀寫安全區(qū)到了。MCU 可以等 TE 脈沖到來之后再啟動(dòng) DMA這樣寫入操作只會(huì)發(fā)生在安全窗口內(nèi)。我在工程里的做法是把 TE 引腳接到 MCU 的 EXTI 輸入TE 下降沿觸發(fā)中斷在中斷里啟動(dòng)下一幀的 DMA 傳輸void EXTI_IRQHandler(void) { if (EXTI_GetITStatus(TE_PIN) ! RESET) { // TE 到達(dá)安全窗口開始啟動(dòng) DMA 刷下一幀 LCD_DMA_Start((uint32_t*)frame_buffer, buffer_size); EXTI_ClearITPendingBit(TE_PIN); } }這樣每一幀的起始時(shí)刻都和屏的內(nèi)部刷新節(jié)奏對(duì)齊。如果 DMA 傳輸時(shí)間超過一幀還會(huì)出現(xiàn)下一幀丟幀的情況那就需要檢查傳輸時(shí)間或改用雙緩沖。LAT1313 的 TE 信號(hào)頻率一般等于刷新率我這邊實(shí)測(cè)大約 60Hz一幀的傳輸窗口大約 16.6msDMA 在 HCLK 72MHz 下傳一屏 RGB565 數(shù)據(jù)大概耗時(shí) 6~8ms足夠用了。4.4 實(shí)測(cè)踩坑記錄Tearing 只在特定角度看到橫線TE 同步解決之后撕裂現(xiàn)象基本消失。但有一個(gè)案例讓我印象很深某次用手機(jī)拍屏的時(shí)候畫面里總有一條橫線肉眼看卻完全正常。后來發(fā)現(xiàn)不是撕裂而是 TE 信號(hào)本身存在抖動(dòng)導(dǎo)致 DMA 偶爾在安全窗口邊緣啟動(dòng)這時(shí)候撕裂只持續(xù)極短時(shí)間肉眼難以察覺但相機(jī)的高速快門能拍到。解決方式是在 TE 觸發(fā)后加一個(gè)固定短延時(shí)再啟動(dòng) DMA讓寫入點(diǎn)略微遠(yuǎn)離 TCON 的讀取指針void EXTI_IRQHandler(void) { if (EXTI_GetITStatus(TE_PIN) ! RESET) { delay_us(200); // 等待安全偏移量 LCD_DMA_Start((uint32_t*)frame_buffer, buffer_size); EXTI_ClearITPendingBit(TE_PIN); } }這個(gè) 200us 是我通過示波器反復(fù)量出來的LAT1313 的規(guī)格書里一般會(huì)標(biāo)出 TE 脈沖與 GRAM 寫入安全區(qū)的關(guān)系。不同屏的延遲窗口不一樣你要自己抓波形確認(rèn)不要照抄我的值。5. GOA 與雙邊同步寫在玻璃內(nèi)部的掃描時(shí)序5.1 GOA 是什么為什么要在意它如果你拆開一塊高分辨率的 LCD 模組會(huì)發(fā)現(xiàn)它的柵極驅(qū)動(dòng)芯片少了很多。這是因?yàn)楝F(xiàn)代面板大量采用 GOAGate on Array技術(shù)把柵極驅(qū)動(dòng)電路直接做在 TFT 陣列的玻璃基板上。GOA 電路本質(zhì)上是一串移位寄存器。TCON 提供一個(gè)起始脈沖STV和兩組時(shí)鐘CKV/CKVNGOA 電路中的每一級(jí)依次打開一行柵極。這樣逐行掃描的節(jié)奏完全由 TCON 送出的時(shí)序決定。如果這個(gè)啟動(dòng)時(shí)序不對(duì)整個(gè)屏幕的行掃描順序就會(huì)亂。LAT1313 這類 JDI 模組內(nèi)部同樣有 GOA。對(duì)驅(qū)動(dòng)開發(fā)者來說GOA 的存在意味著我們不能只關(guān)注顯存讀寫還要關(guān)注 TCON 初始化時(shí)對(duì) GOA 的配置。比如起始脈沖的極性、時(shí)鐘的相位、掃描方向都會(huì)影響實(shí)際顯示。5.2 傳統(tǒng)雙邊同步發(fā)送到底同步了啥規(guī)格書里如果出現(xiàn)雙邊同步發(fā)送這類描述通常指的是柵極驅(qū)動(dòng)信號(hào)同時(shí)從面板左右兩側(cè)送入。GOA 電路分布在左右兩側(cè)每一行柵極由兩側(cè)的移位寄存器同時(shí)驅(qū)動(dòng)。這種設(shè)計(jì)的目的是保證行掃描的充電時(shí)間在整行范圍內(nèi)均勻一致。如果只從單邊發(fā)送起始脈沖和時(shí)鐘面板另一端的信號(hào)會(huì)因?yàn)樽呔€延遲而滯后導(dǎo)致同一行左右兩端的充電時(shí)間不一致。后果是屏幕一側(cè)亮一側(cè)暗或者在快速滾動(dòng)時(shí)一側(cè)的刷新先于另一側(cè)畫面上出現(xiàn)奇怪的斜向撕裂。在代碼層面我們需要確認(rèn)初始化序列里對(duì) GOA 左右驅(qū)動(dòng)的配置寄存器是否正確。JDI 屏的規(guī)格書一般會(huì)給出一個(gè)顯示方向或掃描方向的寄存器說明改這個(gè)寄存器時(shí)要注意它同時(shí)影響 GOA 的掃描方向和源極驅(qū)動(dòng)的數(shù)據(jù)輸出方向。改完之后最好在屏上畫一個(gè)F字樣的測(cè)試圖方向?qū)Σ粚?duì)一眼就能看出來。5.3 GOA 啟動(dòng)失敗的現(xiàn)象與排查GOA 啟動(dòng)失敗的現(xiàn)象非常有辨識(shí)度屏幕上半部分正常從某一行開始整個(gè)下半部分畫面靜止、全黑或者布滿噪點(diǎn)而且失敗位置常常是固定的比如正好是整個(gè)屏幕的三分之一或二分之一處。這是因?yàn)?GOA 移位寄存器在某個(gè)位置沒能繼續(xù)傳遞下去。我在 LAT1313 上遇到過一種情況初始化序列里少了設(shè)置 GOA 時(shí)鐘極性的命令導(dǎo)致 CKV/CKVN 的極性反了GOA 移位寄存器每隔一行翻一次畫面出現(xiàn)嚴(yán)重的行間錯(cuò)位看起來像百葉窗。解決辦法是檢查初始化序列里所有帶 0xB0、0xB1、0xB2 這類 bank 選擇或測(cè)試模式的命令確認(rèn)它們和規(guī)格書示例一致。另外有個(gè)排查技巧GOA 相關(guān)問題在低溫下更容易暴露。把屏放進(jìn)冰箱或者用風(fēng)扇吹如果半屏問題的出現(xiàn)概率明顯上升基本就是 GOA 時(shí)鐘/起始脈沖的建立時(shí)間不足。這時(shí)可以嘗試加大 TCON 初始化里 CLK 的脈寬設(shè)置或者調(diào)整 STV 相對(duì)于首行掃描的時(shí)序偏移。6. 背光亮度與顯示時(shí)序的隱形耦合6.1 PWM 頻率不要和幀頻打架背光 PWM 頻率和 LCD 的掃描時(shí)序看似互不相干實(shí)際上如果兩者頻率接近或成整數(shù)倍關(guān)系就會(huì)出現(xiàn)一種很難察覺的行走暗帶現(xiàn)象。人眼對(duì)亮度變化的感知比較遲鈍但如果 PWM 的頻率和面板刷新率存在差頻比如 PWM 是 1kHz刷新率是 60Hz那么在某些亮度下你會(huì)看到屏幕上有緩慢移動(dòng)的橫紋。這是因?yàn)楦哳l PWM 在一個(gè)幀周期內(nèi)平均亮度不均衡不同幀之間同一位置的亮度存在低頻調(diào)制。我的經(jīng)驗(yàn)是把 PWM 頻率設(shè)置在刷新率的 20 倍以上或者直接避開整數(shù)倍。比如刷新率 60HzPWM 用 10kHz差頻接近 9.94kHz人眼完全察覺不到。我實(shí)測(cè)下來10kHz 以上基本安全如果屏的背光驅(qū)動(dòng)采用恒流源頻率可以設(shè)到 20kHz 甚至更高避開音頻范圍還能減少電感嘯叫。6.2 亮度極低時(shí)的發(fā)紫和花屏背光亮度調(diào)到很低時(shí)你可能會(huì)發(fā)現(xiàn)屏幕顏色變紫或偏花。這不是 LCD 顯示時(shí)序的問題而是 LED 燈條在低電流下色溫漂移再加上 PWM 占空比過小LED 驅(qū)動(dòng)電流進(jìn)入非線性區(qū)所致。驅(qū)動(dòng)代碼里可以限制最小占空比。比如 PWM 是 10bit占空比最低不要低于 20這樣亮度雖然不能無限低但至少顏色是準(zhǔn)的。如果產(chǎn)品確實(shí)需要很低的亮度檔可以考慮在軟件里做亮度曲線映射把低亮度段拉長(zhǎng)避免在 PWM 極低值附近工作。6.3 背光與顯示使能的先后順序我在這塊屏上踩過的一個(gè)小坑是初始化代碼里 Display ON 和背光使能之間幾乎沒有間隔。結(jié)果是上電瞬間屏幕會(huì)閃一下花屏噪點(diǎn)然后才進(jìn)入正常畫面。原因很簡(jiǎn)單——背光打開時(shí)面板可能還沒進(jìn)入正常的顯示狀態(tài)這時(shí)候你等于把面板沒準(zhǔn)備好期間的混亂像素內(nèi)容放大了給用戶看。所以我現(xiàn)在的做法是Display ON 之后等待至少 5 到 20ms再打開背光。這個(gè)延遲在初始化代碼里毫不起眼但對(duì)用戶體驗(yàn)的提升非常明顯。如果開機(jī)時(shí)背光先亮、畫面后出用戶會(huì)潛意識(shí)里覺得這個(gè)設(shè)備開機(jī)慢反過來畫面先穩(wěn)定、背光再亮觀感就正常得多。7. 常見異常屏效的排查清單與中文顯示亂碼的真相7.1 一張問題現(xiàn)象到排查方向的速查表做 LCD 驅(qū)動(dòng)調(diào)試最忌諱隨機(jī)嘗試。我整理了一張速查表遇到問題先對(duì)著表格定位方向能省很多時(shí)間現(xiàn)象優(yōu)先排查方向白屏無顯示電源電壓、復(fù)位時(shí)序、背光使能順序花屏/噪點(diǎn)FSMC 數(shù)據(jù)建立時(shí)間、像素時(shí)鐘極性、DE/HS/VS 極性橫線/半屏異常GOA 時(shí)鐘極性、TE 同步、電源紋波豎線/整列偏移掃描方向寄存器、MADCTL、源極驅(qū)動(dòng)偏壓設(shè)置滾動(dòng)時(shí)撕裂TE 信號(hào)連接、DMA 啟動(dòng)時(shí)機(jī)、FSMC 時(shí)序余量畫面偏色Gamma 寄存器、RGB/BGR 順序、COLMOD 像素格式亮度不均/暗帶背光 PWM 頻率、VGH/VGL 電平、左右雙邊同步配置這個(gè)表不是讓你照著逐項(xiàng)試而是讓你知道每個(gè)典型現(xiàn)象的根源通常落在哪一層。7.2 屏亮了但中文顯示亂碼先別怪時(shí)序網(wǎng)絡(luò)上很多搜索LCD 顯示中文的人其實(shí)是卡在屏亮了但中文亂碼這一步。我要明確說一點(diǎn)LCD 根本不認(rèn)識(shí)中文這兩個(gè)字它只認(rèn)識(shí)像素點(diǎn)陣。中文顯示亂碼絕大多數(shù)情況不是顯示時(shí)序的問題而是字庫編碼或取模方向的問題。中文顯示鏈路是這樣的程序里的中文字符串UTF-8/GBK 編碼→ 通過內(nèi)碼查找字庫點(diǎn)陣 → 把點(diǎn)陣數(shù)據(jù)寫入顯存對(duì)應(yīng)區(qū)域。任何一個(gè)環(huán)節(jié)錯(cuò)位都會(huì)表現(xiàn)成亂碼。最常見的原因有三個(gè)第一字符編碼不一致。代碼文件是 UTF-8 編碼但字庫索引用的是 GB2312導(dǎo)致內(nèi)碼偏移。第二取模方向和面板掃描方向不一致。比如屏是從左到右、從上到下掃描但字庫取模是從下到上顯示出來的漢字就是左右鏡像或者上下顛倒。第三顯示緩沖區(qū)寬度和屏幕實(shí)際寬度不一致。如果一行漢字所需的字節(jié)數(shù)算錯(cuò)第二行漢字會(huì)錯(cuò)位到第一行后面。所以排查中文亂碼第一件事是拿一個(gè)已知正確的字模在屏上直接打點(diǎn)確認(rèn)行掃描方向是對(duì)的。方向?qū)α嗽俨榫幋a轉(zhuǎn)換最后查緩沖區(qū)寬度。不要在驅(qū)動(dòng)時(shí)序上浪費(fèi)時(shí)間這段邏輯和時(shí)序沒多大關(guān)系。7.3 用邏輯分析儀驗(yàn)證時(shí)序的正確姿勢(shì)如果前面的排查方向都對(duì)但問題依然存在那就需要示波器或邏輯分析儀出場(chǎng)。抓線的優(yōu)先級(jí)是RESX、CSX、SCL/WR、DCX/RS、TE以及電源軌的上升沿。我調(diào)試 LAT1313 時(shí)習(xí)慣把 RESX 和第一個(gè)初始化命令之間的時(shí)間、TE 脈沖和 DMA 啟動(dòng)之間的時(shí)間分別截圖保存然后在規(guī)格書的時(shí)序圖上找到對(duì)應(yīng)段落逐項(xiàng)核對(duì)。這樣做的好處是一旦出現(xiàn)問題你能很快判斷是那一小段時(shí)序不滿足而不是把整個(gè)初始化代碼翻來覆去地看。還有一個(gè)經(jīng)驗(yàn)如果屏的顯示偶爾出錯(cuò)、但不頻繁可以開蓋用示波器長(zhǎng)時(shí)間監(jiān)控 TE 引腳看有沒有毛刺。TE 信號(hào)一旦被電源噪聲干擾DMA 同步就會(huì)失效。給 TE 引腳加一個(gè) RC 濾波比如 100 歐姆加 1nF 電容很多偶發(fā)熱點(diǎn)問題會(huì)直接消失?;仡^看我調(diào) LAT1313 那幾天最深的體會(huì)是LCD 驅(qū)動(dòng)不是一個(gè)寫完配置就能跑的活它真正考驗(yàn)的是你對(duì)時(shí)序參數(shù)的理解和耐心。從那以后我做任何屏的驅(qū)動(dòng)都會(huì)先把規(guī)格書里的時(shí)序圖打印出來把上電順序、復(fù)位脈寬、Sleep Out 等待時(shí)間、TE 同步窗口這幾項(xiàng)用熒光筆標(biāo)出來再開始寫代碼。這個(gè)習(xí)慣幫我避開了不少坑。如果你現(xiàn)在也正被一塊 JDI 屏的時(shí)序問題折磨不妨先放下代碼去把規(guī)格書翻出來把那條從電源到像素的路徑完整走一遍。