理解 LCD 屏幕的驱动原理与调试过程,示例的驱动 IC 为 GC9308 ,展示整个屏幕的驱动过程。
起因
最近拿到了一個比較新的驅動 IC 的 LCD 了,此前 K210 上面使用的都是 ST7789V ILI9342C SH1106 這類驅動 IC 的屏幕模塊。
這次來了一個 GC9308 ,我想我需要認識一下屏幕驅動的整體架構,也就是拿起數據手冊當作學習教材來學了,實際上學完以后,懂了以后都不難,重點在如何總結這些屏幕的驅動邏輯,以此打下往后的屏幕驅動理解基礎。
我需要讀懂圖像的二進制定義、還有傳輸方式,我找了一本中文的屏幕數據手冊來讀,了解一下相關的流程和細節
本文我只會交待軟件層面的理解,硬件方面的定義和特性我無法給出準確的解釋,姑不會提及。
屏幕的發展歷程
讓我們看一下這個大哥的故事,就很好的說明了這段 LCD MCU 發展的歷史。
https://blog.csdn.net/special00/article/details/78128374
記得在很早的時候,那時候還都是FSTN的顯示屏滿天飛的時候(也是小弟剛剛畢業開始作手機的時候)。LCD的驅動電路有很多是兩片芯片的,一片LCDC,一片LCD Driver,一般的LCDC里面有一個display的buffer。LCDDriver是電路驅動液晶顯示部分的電路,沒有什么好講的。更早的時候,LCD上就一片LCDDriver就行了,程序員需要控制兩個(H,V)場掃描信號,而且程序員希望在某個坐標顯示,都需要編程控制驅動電路來實現,后來發現顯示屏越來越大,而MCU以及程序員沒有這個能力和精力來對LCD進行這類的同步控制,于是LCDC就誕生出來承擔起這些個功能。后來加上了buffer,就是說程序員可以把大批的顯示內容以顯示矩陣(display matrix)的形式寫到buffer里,讓LCDC來讀取buffer里的數據再由LCDDriver顯示到顯示屏上。后來這個buffer越來越大,除了顯示矩陣以外還放很多命令,所以也不能老把它籠統的叫buffer啊,所以就對放顯示矩陣的存儲空間有了一個專用的名字叫做GRAM。到現在嘛,這些驅動/控制電路以及buffer都合起來放在一片芯片中,統稱為driver IC啦。也就是LCM上那顆COG的芯片,相信看這片回帖的兄弟們都看到過。而且這顆driver IC的功能越來越nb,有什么dimm功能啊,gamma功能啊,什么省電啊等等亂七八糟的功能,不過大多功能程序員都不需要去詳細了解,現在的程序員都很輕松啦,只需要用很簡單的幾條命令就可以控制這顆driver IC來驅動LCD。
更多的內容可以自己去跳轉閱讀。
如何理解現在的 LCD 屏幕的驅動架構
因為我是在學習和整理過后進行消化的知識,所以我會對我所掌握的內容做一個重新編排,我認為理解一個模塊或事物,應當從它的 本質架構 或 程序設計 上去理解它,這樣的理解會存在一個同類事物的共性,如屏幕的驅動共性。
以往供應商給到我的數據手冊全是英文的,這個確實影響到了我對屏幕的認知,一方面是英文的翻譯和中文的專業術語不對稱,存在理解誤差。另一方面是英文的描述在初學階段并不能駐留在腦子里,如果是為了效率學習,母語的學習效率將會極高,因為專業術語有鋪墊,所以我找了一本中文的屏幕數據手冊來作為原始的基礎學習的材料。
想要進一步的理解復雜功能,必然要先建立一個基礎知識體系,讓一個從來沒有開發過屏幕驅動,只會調調代碼的朋友去開發一款全新的 LCD 驅動代碼,例如對接一款全新的 LCD 屏幕,通過 MCU 接口驅動一款 8bit 數據總線 16bit 顏色的 LCD 屏幕,并同步支持 GUI 的基礎繪圖操作,從初始化到繪圖顯示圖像等功能實現,那么要如何去解決這個問題呢?
也許你和我一樣是個軟件工程的開發者,知道去使用類似的 code 來復現驅動邏輯,但你未必知道你所用的硬件之中的定義,對應的功能又是哪些,你如果不了解你的驅動芯片,你又該如何寫它呢?也許邏輯是對的,但是對這款芯片也一樣適用嗎?
這也是我最初的困惑,在我沒有深入學習之前,我是可以通過抄代碼邏輯來完成對一款屏幕的驅動,然而現在我拿到的是網絡上連代碼都沒有的 IC ,就有那么一些迷茫,現在我把這些學習和理解的過程記錄下來,給未來可能出現同樣問題的你,建立一個容易理解的體系概念。
為了確保自己提供的內容是準確且符合客觀事實的,所有訊息的參考依據都是來源于數據手冊,這意味著數據手冊就是我們最好的學習材料 XD 。
LCD 屏幕的初始化流程
按道理來說,任何一個屏幕都應該在這里完成對屏幕的初始化,產生雪花屏的效果,不然后面的內容都可以不用做了,但這里的內容我打算是放到最后的實踐章節來講的,所以放一張圖占位就好了嘻嘻。
所謂的雪花屏就是背后那些密密麻麻的的白色色塊,這表示 LCD 屏幕初始化完畢。
LCD 屏幕的體系架構
我認為先基礎理解這個是重中之重,關于這個結構的問題,在我看的這么 5 本屏幕數據手冊之中來看,體系架構是在進化的,但本質是不變的,所以可以放心的去獲取自己所需要的部分。
整體的架構框圖(Block diagram)
現在我們很少會直接去使用這里面的內容了,因為所謂的屏幕供應商廠家會根據你的屏幕接口定制你所需要的線序,減少我方驅動開發人員的對驅動硬件層面的理解,從而讓軟件開發人員只需要關注寫圖像數據這件事情。
像我們必然會確認的幾個關鍵信息,如這圖的 A0(DC) IM0:2 WR RES D0:7 等引腳資源,基本就可以判斷出這個 lcd 的支持層次和范圍,還可以進一步判斷 它的 Display Data Ram 的信息,但事實上,知道還是不知道都不影響我們對它的驅動,只是幫助我們快速感知這個屏幕的架構情況,所以知道了部分就可以繼續往下看了。
顯示內存(DDRAM)的線路地址映射關系
關于顯示內存,有的數據手冊可能會稱為 Graphics RAM (GRAM),但大體意思是不變的,為避免誤解我附個圖。
雖然說法都有點不太一樣,但這并不影響我們對它的理解,它實際上的用途就是一塊用于顯示的內存,它代表了 驅動 IC 的會通過讀取它的數據進行屏幕的掃描顯示,而我們要做的就是對它寫入顯示的數據和對應的行列起始地址(address),有得會稱為行計、列數器(counter),其實效果如下圖。
如何設置顯示的內容呢?這就要看懂它的 DDRAM 的地址關系,將數據寫入到對應的 DDRAM 中就可以做到了(驅動 IC 會從中讀取數據顯示到屏幕的這個過程),所以我們必然要理解這個內容,它的結構圖如下。
至此可以理解的是,驅動 IC 的工作內容,接著我們深入理解一下 DDRAM 的線路地址,關于它的尋址方式。
這是一張很形象的 SH1106 的地址尋址圖,關于圖像的存儲方式和行列尋址方式,我現在來解釋一下上面這張圖想要表達的意思。
首先我們知道這是單色的 LCD 屏幕,所以看到的圖像中標記的紅線表示每一次傳輸的 D0:7 列數據(colum address),顯示出來就是對應的圖像像素點(1 bit),有得又會叫列計數器,實際上都是指這個列數據的索引數據,同樣的還有行數據(line address),那么我們繼續理解它的 頁面地址(page) 這個實際上是可以這樣形象的理解的。
如果不做出說明和講解的話,這些張圖可能很難讓人理解,不妨想象一下屏幕就是一款打印機,它想要顯示數據就需要打印一張紙(page)出來,打印的紙張將根據起始的行地址來按行打印每行的一列數據,這樣最后打印出來的內容就是屏幕所顯示的數據拉,不妨結合下圖來感受和理解這個存儲結構。
它實際上就是想說明下述效果。
現在你是否能夠理解了呢?如果理解了,你再看看這張圖,是不是就能理解了呢?(頁面地址并不是每款都會去設計的,因為不一定需要多級緩存,通常雙緩沖足以)
那么理解了這個對我們有什么意義呢?至少你現在應該知道如何控制代碼去給這些驅動 IC 設置想要顯示的數據了,它們基本都是相通的,不足為奇。
拓展:關于如何理解數據手冊中所講的設置屏幕的行掃描方向(顯示方向) https://www.cnblogs.com/amanlikethis/p/3872515.html
LCD 屏幕的數據總線接口類型
既然我們知道應該如何去設置顯示內存了,那么就得看看我們應該如何連接我們的 LCD 驅動 IC 了。
數據總線這個用詞是來源于微機原理,其實就是一堆用于傳輸數據的數據線,指都有哪些通信手段,或者說傳輸接口。
首先確認 im 的配置,這個配置通常由硬件上的接線決定,如下圖我們得知。
只有先確認了我們的連接的配置才會進一步的配置寄存器、寫入顯示數據,同樣的也要進一步確認我們的 Data 是 8-bit 還是 9-bit,這會再下面做出說明。
MCU
MCU接口標準名稱是 I80 ,因為主要針對單片機的領域在使用,因此得名。后在中低端手機大量使用,其主要特點是價格便宜的。MCU-LCD接口的標準術語是 Intel 提出的 8080 總線標準,因此在很多文檔中用 I80 來指 MCU-LCD 屏。
MUC接口主要又可以分為 8080 模式和 6800 模式,這兩者之間主要是時序的區別;數據位傳輸有 8 位, 9 位, 16 位, 18 位, 24 位; 連線分為:CS/,RS(寄存器選擇),RD/,WR/,再就是數據線了。
優點是:控制簡單方便,無需時鐘和同步信號。缺點是:要耗費 GRAM ,所以難以做到大屏(3.8寸以上)。對于 MCU 接口的 LCM ,其內部的芯片就叫 LCD 驅動器。主要功能是對主機發過的數據/命令,進行變換,變成每個象素的RGB數據,使之在屏上顯示出來。這個過程不需要點、行、幀時鐘。
詳細可看 https://blog.csdn.net/qq_28986985/article/details/88546029
I(intel)8080模式
CS 片選信號
RS (D/I 數據/指令選擇線, 置1為寫數據, 置0為寫命令)
/WR (為0表示寫數據)
/RD (為0表示讀數據)
RESET 復位LCD(用固定命令系列 0 1 0來復位)
M(Motorola) 6800 模式
M6800模式支持可選擇的總線寬度 8/9/16/18-bit (默認為8位),其實際設計思想是與 I80 的思想是一樣的,主要區別就是該模式的總線控制讀寫信號組合在一個引腳上(/WR),而增加了一個鎖存信號(E)數據位傳輸有8位,9位,16位和18位。
事實上關于這種我們可以直接在數據手冊中的時序圖得到理解,它們一般也會一起提供對應的 timing 或者 pause ,翻譯過來都是時序的意思,后者有時序片段的意思。
可以看出它有一根 DC 腳,如果有這個腳則意味著,在代碼上要通過控制這個引腳來完成本次傳輸的數據是 命令 Command 還是 數據 Data 了,而所謂的 9-bit 就是指這個省略了這個引腳,在數據 D0:7 部分改成了 D0:8 ,其中第一個數據用做命令和數據的標識區分,如下圖所示。
也就是說,它的主體邏輯其實很簡單,就 RST 控制硬件復位、DC 引腳只要能區分數據就行,可有可無(如 9-bit 不需要),接著是 WR 用于提示該發送 D0:X 的數據了,以及 RD 提醒 IC 可以發數據過來給我接收了。
剩下的無非就是具體問題具體分析,遇到問題仔細對時序就好了,例如下圖這樣。
這并不難理解,讓我們繼續。
SPI
在這之前我一直認為 SPI 就是指 摩托羅拉公司推出的標準 SPI 協議,標準的 CS MOSI MISO CLK 四個引腳,但后來才知道有如下拓展 SPI 接口。
擴展SPI協議 https://cloud.tencent.com/developer/article/1486642
但其實最重要的是 SPI 的翻譯為 spi serialport interface 接口,意味著就是串行數據通信的方式,這與 Parallel Interface 是 并行接口 相對,這在下面的說明就會體現出來了。
4 線串行接口( 4 線 SPI)
串行接口由串行時鐘 SCL,串行數據 SI,A0 和CS 組成。在 SCL 的每個上升沿按照 D7,D6,......和 D0 的順序將 SI 移入 8 位移位寄存器。每隔 8 個時鐘對 A0 進行采樣,并將移位寄存器中的數據字節同一時鐘寫入顯示數據 RAM (A0 = 1)或命令寄存器( A0 = 0)。
3 線串行接口( 3 線 SPI)
3 線串行接口由串行時鐘 SCL,串行數據 SI 和CS 組成。在 SCL 的每個上升沿按照 D/ C ,D7,D6,......和 D0 的順序將 SI 移入 9 位移位寄存器。 D/ C 位(9 位中的第一位)將確定傳輸的數據被寫入顯示數據 RAM (D/ C = 1)或命令寄存器( D/ C = 0)。
不難看出,它和 8-bit 與 9-bit 的區別有異曲同工之妙,但為什么它叫 SPI 呢,實際上這是與并行相對的地方,比如將 D0:7 的數據壓縮到了一條 SI 通道中傳輸了。
I2C
I2C 并不是每個屏幕都會支持,因為它接口簡單,速度不快,所以只會出現在一下微小的屏幕上,但不同的是,它要通過硬件來標記從機產生的地址。
拿 SH1106 為例來說,它支持讀寫訪問。 R/ W 位是從機地址的一部分。 在 I2C 總線上傳輸任何數據之前, 應首先尋址應響應的設備。 SH1106 保留兩個 7 位從地址( 0111100 和 0111101 )。通過將輸入 SA0 連接到邏輯 0(VSS)或 1(VDD1)來設置從地址的最低有效位。
至今位置的接口傳輸的數據基本都沒有太大的區別,都是傳輸像素點的顏色數據。
RGB
現在就說點不同的屏幕接口,通常 RGB 接口結構,區別有下述。
圖片資料來源 https://blog.csdn.net/vrk731/article/details/85221189
對于RGB接口的LCM,主機輸出的直接是每個象素的RGB數據,不需要進行變換(GAMMA校正等除外),對于這種接口,需要在主機部分有個LCD控制器,以產生RGB數據和點、行、幀同步信號。
也就是說,傳統的MCU屏顯示數據寫入DDRAM,而RGB屏數據不寫入DDRAM,直接寫屏,讀寫速度更快。
VSYNC 模式:
該模式其實就是就是在MCU模式上加了一個VSYNC信號,應用于運動畫面更新,這樣就與上述兩個接口有很大的區別。該模式支持直接進行動畫顯示的功能,它提供了一個對MCU接口最小的改動,實現動畫顯示的解決方案。在這種模式下,內部的顯示操作與外部VSYNC信號同步。可以實現比內部操作更高的速率的動畫顯示。但由于其操作方式的不同,該模式對速率有一個限制,那就是對內部SRAM的寫速率一定要大于顯示讀內部SRAM的速率。
通常我們在 LCD 數據手冊中看到的多數指這種 VSYNC 模式,了解它的工作機制也是一樣通過看數據手冊才能得知,如下圖所示。
在理解上圖之前,需要確定下述兩個概念。
垂直同步脈沖(Vertical synchronization, Vsync)是加在兩幀之間。跟水平同步脈沖類似,但它指示著前一幀的結束,和新一幀的開始。 垂直同步脈沖是一個持續時間比較長的脈沖。
水平同步脈沖(Horizontal synchronization pulse, Hsync)加在兩個掃描行之間。它是一個短小的脈沖,在一行掃描完成之后,它就會出現,指示著這一行掃描完成,同時它也指示著下一行將要開始。
來源百度百科 https://baike.baidu.com/item/垂直同步/7263524?fr=aladdin
然后我附圖說明這個關系。
但是很可惜的是,這里我無法解釋的更為細致的原因了,想知道更多的,只能請你自己去查閱 ILI9342C 數據手冊的 VSYNC Interface 一章中對時序的計算和屏幕的掃描方式,要特別注意 Back porch (2 lines) 和 Front porch (2 lines) 的用途。
當然,這個你可以看這份資料來理解為什么。 https://www.cnblogs.com/biglucky/p/4142505.html
VSYNC Interface 的 RGB 接口要看懂還挺難的,我看了一點就懵逼了,還有公式換算,應該是期望在 VSYNC 同步觸發前應當把 幀 數據寫入屏幕,原諒我沒有真正的實踐這個,只能理解到這里了。
LCD 屏幕的像素點與顏色值定義的關系
如果你已經得知如何去確認通信接口和模式,那么就要確認的就是通信的數據格式了,通常我們的通信的總線通道(D0:7)并沒有那么寬,但我們也一樣可以傳輸更多的數據,這就需要去看數據手冊的數據定義了,它很貼心的把 RGB 顏色的范圍定義通過顏色表示出來了。
實際上我們傳輸的數據假設為一個 16bit 的變量,則如下圖還原,其他同理。
其實我們通常使用的 16-bit 的 color 值就是指 RGB16 的意思。
RGB16
所以這段魔幻代碼的操作你就知道為什么了。
//獲取高字節的5個bit
R = color & 0xF800;
//獲取中間6個bit
G = color & 0x07E0;
//獲取低字節5個bit
B = color & 0x001F;
RGB32
看這里有更多的解釋 https://blog.csdn.net/byhook/article/details/84262330 。
關于 BGR 的問題
是 RGB 還是 BGR 這個問題還是歷史遺留因素,那如果真的發現顏色值不對的時候,應當檢查寄存器的配置,確認顏色模式。
因為這個屏幕考慮到早期的代碼因素,所以才會存在這種差異性,這也意味著我們后來寫的代碼,還是得分清楚,是 RGB 還是 BGR。
至此你已經知道了,屏幕通信過程中的顏色值情況,也就明白了如何應該如何傳輸像素點的顏色值,剩下的就是如何傳輸的功能問題了。
LCD 屏幕的初始化流程
這個不同屏幕有不同的初始化流程,但都有一個標準和大概的范圍,也和硬件有關,有時候還不如不接,讓屏幕自行進入初始化?(迷惑
我分別截圖幾個手冊的情況和舉實例吧。
其實不難,主要是硬件方面設計好就可以了。
關于 I8080 傳輸數據的細節
需要注意的是,這個傳輸數據對時序的要求還挺高的。
LCD 屏幕繪圖接口的主體邏輯
設置填充的區域
設置一下行地址和列地址的起始位置和停止位置。
void lcd_set_area(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
uint8_t data[4] = {0};
x1 += lcd_ctl.start_offset_w;
x2 += lcd_ctl.start_offset_w;
y1 += lcd_ctl.start_offset_h;
y2 += lcd_ctl.start_offset_h;
data[0] = (uint8_t)(x1 >> 8);
data[1] = (uint8_t)(x1);
data[2] = (uint8_t)(x2 >> 8);
data[3] = (uint8_t)(x2);
tft_write_command(HORIZONTAL_ADDRESS_SET);
tft_write_byte(data, 4);
data[0] = (uint8_t)(y1 >> 8);
data[1] = (uint8_t)(y1);
data[2] = (uint8_t)(y2 >> 8);
data[3] = (uint8_t)(y2);
tft_write_command(VERTICAL_ADDRESS_SET);
tft_write_byte(data, 4);
tft_write_command(MEMORY_WRITE);
}
填充內容的方式
如果是 fill 的操作的話,將持續發送該顏色填充到指定的區域即可。
void lcd_fill_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color)
{
if((x1 == x2) || (y1 == y2))
return;
uint32_t data = ((uint32_t)color << 16) | (uint32_t)color;
lcd_set_area(x1, y1, x2-1, y2-1);
tft_fill_data(&data, (x2 - x1) * (y2 - y1) / 2);
}
如果是 draw 的操作的話,將緩沖區中的顏色數據持續的到指定的區域即可完成數據的顯示。
void lcd_draw_picture(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height, uint32_t *ptr)
{
lcd_set_area(x1, y1, x1 + width - 1, y1 + height - 1);
tft_write_word(ptr, width * height / 2);
}
GC9308 調試示例與后記
補充說明,后來的 LCD 屏幕都多了一個叫 LUT 的映射表,實際上就是一個 16-bit to 18-bit color depth conversion 的轉換函數。
軟件方面比較麻煩的地方在于 SPI 的通信模式、像素點的顏色值轉換、驅動的基礎顏色定義、驅動的寄存器配置,基本上確認了這些大多數問題都可以解決。
硬件方面設計有缺陷,個人判斷應該是功耗不足,屏幕閃白線或起霧,看起來就好像要斷電熄滅了一樣。
唯一不滿意的地方就是沒有辦法量出 K210 輸出 并口數據 的信號,因為沒有轉接板可以接出來,這令我十分苦惱。
想知道更多,或者學習調試,不妨看看這份資料 RGB接口和MPU接口區別 https://blog.csdn.net/special00/article/details/78128374 。
總結
以上是生活随笔為你收集整理的理解 LCD 屏幕的驱动原理与调试过程,示例的驱动 IC 为 GC9308 ,展示整个屏幕的驱动过程。的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: windows中msvcp110.dll
- 下一篇: 3DMAX软件怎么制作镂空球体