STM32液晶显示
?作者簡介:嵌入式入坑者,與大家一起加油,希望文章能夠幫助各位!!!!
📃個人主頁:@rivencode的個人主頁
🔥系列專欄:玩轉STM32
💬推薦一款模擬面試、刷題神器,從基礎到大廠面試題👉點擊跳轉刷題網站進行注冊學習
目錄
- 一.顯示器簡介
- 1.顯示器的基本參數
- 2.液晶控制原理
- 3.ILI9341液晶控制器簡介(重點)
- 二.FSMC模擬8080時序
- 1.FSMC控制異步NORFLASH時序
- 2.配置FSMC模式
- 三.液晶顯示(重點)
- 1.液晶驅動的初始化
- 2.常用液晶命令(畫一個實心矩形)
- 1)設置液晶顯示窗口的 X 坐標:2Ah
- 2)設置液晶顯示窗口的 Y 坐標:2Bh
- 3)填充像素點:0x2C
- 4)讀取像素點數據:2Eh
- 5)設置ILI9341的GRAM的掃描方向:0x36
一.顯示器簡介
主要要掌握基本的液晶顯示原理,理解像素、分辨率、色彩深度的概念不然學的糊里糊涂的。
顯示器屬于計算機的 I/O 設備,即輸入輸出設備,像鼠標,鍵盤是輸入設備,像打印機,顯示屏等就是輸出設備。它是一種將特定電子信息輸出到屏幕上再反射到人眼的顯示工具。常見的有 CRT 顯示器、液晶顯示器、LED 點陣顯示器及OLED 顯示器。
液晶顯示器,簡稱 LCD(Liquid Crystal Display),相對于上一代 CRT 顯示器(陰極射線管顯示器),LCD 顯示器具有功耗低、體積小、承載的信息量大及不傷眼的優點,因而它成為了現在的主流電子顯示設備,其中包括電視、電腦顯示器、手機屏幕及各種嵌入式設備的顯示器。
液晶是一種介于固體和液體之間的特殊物質,它是一種有機化合物,常態下呈液態,但是它的分子排列卻和固體晶體一樣非常規則,因此取名液晶。如果給液晶施加電場,會改變它的分子排列,從而改變光線的傳播方向,配合偏振光片,它就具有控制光線透過率的作用,再配合彩色濾光片,改變加給液晶電壓大小,就能改變某一顏色透光量的多少,各種顏色分量組合在一起就能拼湊成五顏六色的光
利用這種原理,做出可控紅、綠、藍光輸出強度的顯示結構,把三種顯示結構組成一個顯示單位,通過控制紅綠藍的強度,可以使該單位混合輸出不同的色彩,這樣的一個顯示單位被稱為像素。
液晶顯示屏的缺點
1.液晶本身是不發光的,所以需要有一個背光燈(純白光)提供光源,光線經一系列處理過程才到輸出,所以輸出的光線強度是要比光源的強度低很多的,比較浪費能源(像手機在室外看不清內容一樣)。
2.而且這些處理過程會導致顯示方向比較窄,也就是它的視角較小,從側面看屏幕會看不清它的顯示內容。另外,輸出的色彩變換時,液晶分子轉動也需要消耗一定的時間(液晶分子轉動畢竟是一種機械運動),導致屏幕的響應速度低。
1.顯示器的基本參數
- 像素
像素是組成圖像的最基本單元要素,顯示器的像素指它成像最小的點,即前面講解液晶原理中提到的一個顯示單元,顯示屏就是由一個一個像素點組成每個像素點都可以輸出各種顏色,當所以像素點有規律的輸出就能輸出一幅圖像。 - 分辨率
一些嵌入式設備的顯示器常常以“行像素值 x 列像素值”表示屏幕的分辨率。如分辨率 800x480 表示該顯示器的每一行有 800 個像素點,每一列有 480 個像素點,也可理解為有 800 列,480 行,知道分辨率就知道屏幕一共有多少個像素點。 - 色彩深度
色彩深度在計算機圖形學領域中表示在位圖或者視頻幀緩沖區中儲存1像素的顏色所用的位數,它也稱為位/像素(bpp)。色彩深度越高,可用的顏色就越多,常見的有16位RBG565 與24位 RBG888
- 點距
不是分辨率越高(像素點越多),屏幕的畫質就越好,屏幕的畫質還是主要取決于點距的大小。
點距:指兩個相鄰像素點之間 的距離,它會影響畫質的細膩度及觀看距離,相同尺寸的屏幕,若分辨率越高,則點距越小,畫質越細膩。如現在有些手機的屏幕分辨率比電腦顯示器的還大,這是手機屏幕點距小的原因;
2.液晶控制原理
這個完整的顯示屏由液晶顯示面板、電容觸摸面板以及 PCB 底板構成。圖中的觸摸面板帶有觸摸控制芯片,該芯片處理觸摸信號并通過引出的信號線與外部器件通訊。
根據實際需要,PCB 底板上可能會帶有“液晶控制器芯片”,圖中右側的液晶屏 PCB 上帶有 RA8875 液晶控制器。因為控制液晶面板需要比較多的資源,所以大部分低級微控制器都不能直接控制液晶面板,需要額外配套一個專用液晶控制器來處理顯示過程,外部微控制器只要把它希望顯示的數據直接交給液晶控制器即可。
STM32F429 系列的芯片不需要額外的液晶控制器,也就是說它把專用液晶控制器的功能集成到 STM32F429 芯片內部了,它節約了額外的控制器成本。而 STM32F1 系列的芯片由于沒有集成液晶控制器到芯片內部,所以它只能驅動自帶控制器的屏幕。
- 顯存
液晶屏中的每個像素點都是數據,在實際應用中需要把每個像素點的數據緩存起來,再傳輸給液晶屏,一般會使用 SRAM 或 SDRAM 性質的存儲器,而這些專門用于存儲顯示數據的存儲器,則被稱為顯存。
顯存一般至少要能存儲液晶屏的一幀(一幅圖像)顯示數據。
如分辨率為 800x480 的 液 晶 屏(顯示一個屏幕的圖像需要800*480個像素點) , 使 用 RGB888 格 式 顯 示 :一個像素點占24位也就是3個字節,則它 的 一 幀 顯 示 數 據 大 小 為 :3x800x480=1152000 字 節 ; 若 使 用 RGB565 格 式 顯 示 , 一 幀 顯 示 數 據 大 小 為 :2x800x480=768000 字節。
一般來說,外置的液晶控制器會自帶顯存,而像 STM32F429 等集成液晶控制器的芯片可使用內部 SRAM 或外擴 SDRAM 用于顯存空間。
液晶面板的控制信號
使用下圖信號線,液晶面板通過這些信號線與液晶控制器通訊,使用這種
通訊信號的被稱為 RGB 接口(RGB Interface)。
1)RGB 信號線
RGB 信號線各有 8 根,分別用于表示液晶屏一個像素點的紅、綠、藍顏色分量。使用紅綠藍顏色分量來表示顏色是一種通用的做法,如果是 RGB565 表示紅綠藍的數據線數分別為 5、6、5 根,一共為 16 個數據位,可表示 2的
16次方種顏色;
2) 同步時鐘信號 CLK
液晶屏與外部使用同步通訊方式,以 CLK 信號作為同步時鐘,在同步時鐘的驅動下,每個時鐘傳輸一個像素點數據。
3) 水平同步信號 HSYNC
水平同步信號 HSYNC(Horizontal Sync)用于表示液晶屏一行像素數據的傳輸結束,每傳輸完成液晶屏的一行像素數據時,HSYNC 會發生電平跳變,如分辨率為 800x480 的顯示屏(800 列,480 行),傳輸一幀的圖像 HSYNC 的電平會跳變 480 次。
4) 垂直同步信號 VSYNC
垂直同步信號 VSYNC(Vertical Sync)用于表示液晶屏一幀像素數據的傳輸結束,每傳輸完成一幀像素數據時,VSYNC 會發生電平跳變。其中“幀”是圖像的單位,一幅圖像稱為一幀,在液晶屏中,一幀指一個完整屏液晶像素點。人們常常用“幀/秒”來表示液晶屏的刷新特性,即液晶屏每秒可以顯示多少幀圖像,如液晶屏以 60 幀/秒的速率運行時(一秒鐘屏幕刷新60次),VSYNC 每秒鐘電平會跳變 60 次。
5) 數據使能信號 DE
數據使能信號 DE(Data Enable)用于表示數據的有效性,當 DE 信號線為高電平時,RGB 信號線表示的數據有效。
3.ILI9341液晶控制器簡介(重點)
ILI9341是一款262,144色單芯片SOC驅動器,用于a-TFT液晶顯示器,分辨率為240RGBx320點,包括一個 720 通道源驅動器、一個 320 通道柵極驅動器、172,800 字節 GRAM 圖形顯示240RGBx320點的數據,以及供電電路。ILI9341 支持并行 8/9/16/18 位數據總線 MCU 接口、6/16/18 位數據總線 RGB 接口和3/4 線串行外設接口 (SPI)。
顯存(GRAM)中每個存儲單元都對應著液晶面板的一個像素點。它右側的各種模塊共同作用把 GRAM 存儲單元的數據轉化成液晶面板的控制信號,使像素點呈現特定的顏色,而像素點組合起來則成為一幅完整的圖像。
框圖的左上角為 ILI9341 的主要控制信號線和配置引腳,根據其不同狀態設置可以使芯片工作在不同的模式,如每個像素點的位數是 6、16 還是 18 位這里一般選擇16位(2個字節一個像素點)RGB565的格式;
可配置使用 SPI 接口、8080 接口還是 RGB 接口與 MCU (stm32)進行通訊。MCU 通過 SPI、8080 接口或 RGB 接口與ILI9341 進行通訊,從而訪問它的控制寄存器(CR)、地址計數器(AC)、及 GRAM。
液晶屏的信號線及 8080 時序
接到stm32對應的具有FSMC外設功能的引腳通過FSMC模擬8080時序與液晶控制器ILI9341通信、
這些信號線即 8080 通訊接口,帶 X 的表示低電平有效,STM32 通過該接口與 ILI9341芯片進行通訊,實現對液晶屏的控制。
通訊的內容主要包括命令和顯存數據,顯存數據即各個像素點的 RGB565 內容;命令是指對 ILI9341 的控制指令,MCU 可通過 8080 接口發送命令編碼控制 ILI9341 的工作方式,例如復位指令、設置光標指令、睡眠模式指令等等,發送命令與數據都是使用數據線D[15:0]所以我們需要一個根信號線D/CX(高低電平)來區分我們發送給ILI9341 液晶控制器的是命令還是數據
寫時序圖
STM32向ILI9341液晶控制器寫入數據或命令
由圖可知,寫命令時序由片選信號 CSX 拉低開始
第一階段:主機(stm32)向ILI9341液晶控制器發送(發送像素點的命令0x2Ch)所以 D/CX 置為低電平表示寫入的是命令地址(比如填充像素點的指令:0x2Ch),以寫信號WRX 為低,讀信號 RDX 為高表示數據傳輸方向為寫入,同時,在數據線 D [17:0] ( 或D[15:0] 一般是16根數據線一個像素點16位表示)輸出命令地址ILI9341液晶控制器接收到0x2Ch指令后,然后準備開始接收像素點的數據。
第二階段:在第二個傳輸階段傳送的是命令的參數(像素點數據),所以 D/CX 要置高電平,表示寫入的是像素點數據,此時像素點的數據就被保存在ILI9341的SRAM顯存中
讀時序圖
由圖可知,寫命令時序由片選信號 CSX 拉低開始
第一階段:主機(stm32)向ILI9341液晶控制器發送(讀數據的命令0x2Eh)所以 D/CX 置為低電平表示寫入的是命令地址,以寫信號WRX 為低,讀信號 RDX 為高表示數據傳輸方向為寫入,同時,在數據線 D [17:0] ( 或D[15:0] 一般是16根數據線一個像素點16位表示)輸出命令地址ILI9341液晶控制器接收到0x2Eh指令后,然后準備開始返回數據給主機。
第二階段:ILI9341液晶控制器返回數據給主機,讀使能(RDX為低電平), D/CX 置為高電平代表讀取的數據。
二.FSMC模擬8080時序
對STM32FSMC外設不熟悉的請先一定看,《STM32FSMC擴展SRAM》
因為8080時序與FSMC控制存儲器(SRAM,NorFALSH等)的時序很相似,所以我們只需將ILI9341液晶控制器當成一個存儲器這里我們把它當做一個NorFALSH存儲器,所以我們STM32就可以通過FSMC外設與ILI9341液晶控制器通信,向他發送命令或者數據
前面帶N的代表低電平有效
2.FSMC 的地址映射
從FSMC的角度看,可以把外部存儲器劃分為固定大小為256M字節的四個存儲塊。
● 存儲塊1用于訪問最多4個NOR閃存或PSRAM存儲設備。這個存儲區被劃分為4個NOR/PSRAM區并有4個專用的片選。
● 存儲塊2和3用于訪問NAND閃存設備,每個存儲塊連接一個NAND閃存。
● 存儲塊4用于訪問PC卡設備
每一個存儲塊上的存儲器類型是由用戶在配置寄存器中定義的。
使用 FSMC 外接存儲器時,其存儲單元是映射到 STM32 的內部尋址空間的;在程序里,定義一個指向這些地址的指針,然后就可以通過指針直接修改該存儲單元的內容,FSMC 外設會自動完成數據訪問過程,讀寫命令之類的操作不需要程序控制。FSMC的地址映射見圖 FSMC 的地址映射。
FSMC 把整個 External RAM 存儲區域分成了 4 個 Bank 區域,并分配了地址范圍及適用的存儲器類型,如 NOR 及 SRAM 存儲器只能使用 Bank1 的地址。
1.FSMC控制異步NORFLASH時序
讀時序:該圖表示一個存儲器操作周期由地址建立周期(ADDSET)、數據建立周期(DATAST)以及 2 個 HCLK 周期組成。在地址建立周期中,地址線發出要訪問的地址,數據掩碼信號線指示出要讀取地址的高、低字節部分,片選信號使能存儲器芯片(ILI9341液晶控制器);地址建立周期結束后讀使能信號線發出讀使能信號,接著存儲器(ILI9341液晶控制器);通過數據信號線把目標數據傳輸給 FSMC,FSMC 把它交給內核。
寫時序:它的一個存儲器操作周期僅由地址建立周期(ADDSET)和數據建立周期(DATAST)組成,且在數據建立周期期間寫使能信號線發出寫信號,接著 FSMC把數據通過數據線傳輸到存儲器(ILI9341液晶控制器)中。
FSMC讀時序與ILI93141讀時序對比
FSMC寫時序與ILI93141寫時序也一樣,只有信號線D/CX對不上其他信號線的時序都一模一樣,所以我們只需要選取一根FSMC的地址線連接到D/CX信號線,我們就可以通過訪問相應不同的地址來控制的這根地址線的高低電平從而控制D/CX信號的高低電平去控制數據線傳輸的是命令還是地址
當 FSMC 外設被配置成正常工作,并且外部接了 NOR FLASH 時,我們這里選擇的是片選1即bank1的區1即訪問的地址范圍是:0x6000 0000~0x63FF FFFF。
若向 0x60000000 地址寫入數據如 0xABCD,FSMC 會自動在各信號線上產生相應的電平信號,寫入數據。FSMC會自動控制片選信號 NE1 選擇相應的 NOR 芯片,然后使用地址線 A[25:0]輸出0x60000000,在 NWE 寫使能信號線上發出低電平的寫使能信號,而要寫入的數據信號0xABCD 則從數據線 D[15:0]輸出,然后數據就被保存到 NOR FLASH 中了。
如何選擇存儲塊區域
HADDR[25:0]包含外部存儲器地址。HADDR是字節地址,而存儲器訪問不都是按字節訪問,因此接到存儲器的地址線依存儲器的數據寬度有所不同,如下表:
為了模擬出 8080 時序,我們可以把 FSMC 的 A16 地址線(也可以使用其它 A1/A2 等地址線)與 ILI9341 芯片 8080 接口的 D/CX 信號線連接,那么當A16為高電平時(即 D/CX 為高電平),數據線 D[15:0]的信號會被 ILI9341 理解為數值,若 A16為低電平時(即 D/CX 為低電平),傳輸的信號則會被ILI9341 理解為命令。
所以我們得到
要發送命令:0x6000 0000 &~(1<<17) =0X6000 0000
只要我們我們解引用這個地址,向這個地址的內存單元寫入數據,則FSMC外設會自動片選ILI9341,然后自動產生模擬8080時序,由于寫入的地址(地址線16為低電平即連接該地址線的D/CX信號線也為低電平)則在寫入的數據就被認為是命令
要發送數據:0x6000 0000 | (1<<17)=0X6002 0000
只要我們我們解引用這個地址,向這個地址的內存單元寫入數據,則FSMC外設會自動片選ILI9341,然后自動產生模擬8080時序,由于寫入的地址(地址線16為高電平即連接該地址線的D/CX信號線也為高電平)則在寫入的數據就被認為是數據
要讀取數據
其實要發送命令或者數據,不一定是這兩個地址,地址只需滿足在0x6000 0000 ~ 0x63FF FFFF之間,然后滿足地址線16電平為低或高電平
2.配置FSMC模式
詳細請參考《STM32FSMC擴展SRAM》
由于我們要使用異步 NOR FLASH 的方式模擬 8080 時序,所以選擇 FSMC 為模式 B,在 該 模 式 下 配 置 FSMC 的 控 制 時 序 結 構 體 中 , 實 際 上 只 有 地 址 建 立 時 間FSMC_AddressSetupTime(即 ADDSET 的值)以及數據建立時間 FSMC_DataSetupTime(即 DATAST 的值)成員的配置值是有效的,其它異步 NOR FLASH 沒使用到的成員值全
配置為 0 即可。而且,這些成員值使用的單位為:1 個 HCLK 的時鐘周期,而 HCLK 的時鐘頻率為 72MHz,對應每個時鐘周期為 1/72us=13.8 ns。
ILI9341 時序參數說明及要求可大致得知 ILI9341 的寫周期為最小 twc = 66ns,而讀周期最小為 trdl+trod=45+20=65ns。(對于讀周期表中有參數要一個要求為 trcfm 和 trc 分別為 450ns 及 160ns,但經過測試并不需要遵照它們的指標要求
**結合 ILI9341 的時序要求和 FSMC 的配置圖,代碼中按照讀寫時序周期均要求至少66ns 來計算,配置結果為 ADDSET = 1 及 DATST = 4,把時間單位 1/72 微秒(即 1000/72 納秒)代入
因此讀寫周期的時間被配置為:
讀周期: trc =((ADDSET+1)+(DATST+1)+2) *1000/72 = ((1+1)+(4+1)+2)*1000/72 = 125ns
寫周期:twc =((ADDSET+1)+(DATST+1)) 1000/72 = ((1+1)+(4+1))1000/72 = 97ns
其實經過測試配置很多配置都可以成功,所以不需要特別在于配置多少,能跑就行
static void ILI9341_FSMC_Config ( void ) {FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;FSMC_NORSRAMTimingInitTypeDef readWriteTiming;/*使能FSMC外設時鐘*/RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC,ENABLE);/*-----------------------SRAM 時序結構體-----------------------*///地址建立時間(ADDSET)為1個HCLK 2/72M=28nsreadWriteTiming.FSMC_AddressSetupTime = 0x01; //地址建立時間//數據保持時間(DATAST)+ 1個HCLK = 5/72M=70ns readWriteTiming.FSMC_DataSetupTime = 0x04; //數據建立時間//選擇匹配SRAM的模式readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_B; /*-----------------------下面成員未用到-----------------------*///地址保持時間(ADDHLD)模式A未用到readWriteTiming.FSMC_AddressHoldTime = 0x00; //設置總線轉換周期,僅用于復用模式的NOR操作readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;//設置時鐘分頻,僅用于同步類型的存儲器readWriteTiming.FSMC_CLKDivision = 0x00; //數據保持時間,僅用于同步型的NORreadWriteTiming.FSMC_DataLatency = 0x00; /*-----------------------NORFLASH 初始化結構體-----------------------*/// 選擇FSMC映射的存儲區域: Bank1 sram3FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM1;//設置要控制的存儲器類型:NORFLASH類型FSMC_NORSRAMInitStructure.FSMC_MemoryType =FSMC_MemoryType_NOR; //存儲器數據寬度:16位FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b; //存儲器寫使能 FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;// 不使用擴展模式,讀寫使用相同的時序FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;//讀寫時序配置FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming;//讀寫同樣時序,使用擴展模式時這個配置才有效FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &readWriteTiming;/*-----------------------下面成員未用到-----------------------*///設置地址總線與數據總線是否復用,僅用于NORFSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; //設置是否使用突發訪問模式,僅用于同步類型的存儲器FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode =FSMC_BurstAccessMode_Disable;//設置是否使能等待信號,僅用于同步類型的存儲器FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait=FSMC_AsynchronousWait_Disable;//設置等待信號的有效極性,僅用于同步類型的存儲器FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;//設置是否支持把非對齊的突發操作,僅用于同步類型的存儲器FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable; //設置等待信號插入的時間,僅用于同步類型的存儲器FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;//不使用等待信號FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable; //突發寫操作FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable; FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure); //初始化FSMC配置FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE); // 使能BANK }三.液晶顯示(重點)
1.液晶驅動的初始化
LCD驅動流程
初始化GPIO:
除了背光引腳BK、和復位引腳RST設置成普通推挽輸出,其他引腳全部設置為復用推挽輸出全部交給FSMC外設來控制與液晶控制器芯片通信。
- 復位液晶屏
- 初始化液晶屏(廠家自帶代碼)
上面提供了兩款液晶控制器芯片的初始化代碼:ILI9341和ST7789V芯片的初始化,不過兩個芯片功能差不多都是控制分辨率為240*320的LCD屏幕,根據自己的液晶控制芯片要選擇配套的初始化代碼,不然會顯示異常,初始化代碼里面也有好多我們就學習的指令,后面就一 一展開來講。
里面有一個讀ID的指令:
ST7789V液晶控制芯片:返回ID命令:0x04
ILI9341液晶控制芯片:返回ID命令:0xD3
- 開啟液晶屏的背光燈
到此液晶的初始化全部完畢(最后還有一個液晶掃描方式后面再講)
2.常用液晶命令(畫一個實心矩形)
初始化液晶之后,以顯示一個矩形為例講解,講解如何設置坐標和如何填充像素點。
首先講一下寫入命令或數據,讀取數據要注意的事項
向ILI9341寫入命令:
向ILI9341寫入數據:
從ILI9341讀取數據:
不管是寫入數據、命令還是讀取數據,本質是訪問ILI9341芯片(當成一個FSMC擴展的一個存儲器)映射的地址,往地址寫入數據或命令,就會產生相應的時序將數據或命令發送到LIL9341芯片,讀該地址的內存單元同樣會產生時序LIL9341將數據通過數據線存儲在該地址上的內存單元上。
第一個要注意:訪問的變量記得加volatile
詳細的volatile關鍵字的介紹:《register關鍵字深入解析》
第二個:什么是內聯函數
建議編譯器(編譯器不一定會怎么做)將指定的函數體插入并取代每一處調用該函數的地方(上下文),從而節省了每次調用函數帶來的額外時間開支。
使用內聯函數是典型的以空間換時間的操作:
編譯器會將調用該函數的地方直接插入該函數的函數體,通俗點講就是將函數中的代碼嵌入調用函數的地方,這樣就會節省函數的調用帶來的額外時間開支(函數棧幀的創建和釋放),不過每調用一次函數就會嵌入一段代碼所以要更多的空間來存儲。
與宏的比較
與宏的功能差不多,但是函數有類型檢查,而且更易于調試,而宏相反
想更多了解:調用函數怎么就耗時間啦——>《函數棧幀的形成與釋放》
接下來就是畫一個矩形
第一步開窗:
1)設置液晶顯示窗口的 X 坐標:2Ah
在默認掃描方式時,該指令用于設置x坐標,該指令帶有4個參數,實際上是2個坐標值:前面兩個參數:SC和后面兩個參數:EC,即列地址的起始值和結束值,SC必須小于等于EC,且0≤SC或EC≤239。
一般在設置x坐標的時候,我們只需要帶2個參數即可,也就是設置SC起始X坐標即可,因為如果EC沒有變化,我們只需要設置一次即可(在初始化ILI9341的時候設置),從而提高速度。
超出范圍的數據將被忽略
2)設置液晶顯示窗口的 Y 坐標:2Bh
在默認掃描方式時,該指令用于設置y坐標,該指令帶有4個參數,實際上是2個坐標值:前面兩個參數:SP和后面兩個參數:EP,即頁地址的起始值和結束值,SP必須小于等于EP,且0≤SP/EP≤319。一般在設置y坐標的時候,我們只需要帶2個參數即可,也就是設置SP即可,因為如果EP沒有變化,我們只需要設置一次即可(在初始化ILI9341的時候設置),從而提高速度。
超出范圍的數據將被忽略
- 開窗函數
3)填充像素點:0x2C
在收到指令0X2C之后,數據有效位寬變為16位(存儲一個像素點需要16位:RGB565),我們可以連續寫入LCD GRAM值,而GRAM的地址將根據wm 我們后面設置的掃描方向進行自增。例如:假設設置的是從左到右,從上到下的掃描方式,那么設置好起始坐標(通過SC,SP設置)后,每寫入一個顏色值,GRAM地址將會自動自增1(SC++),如果碰到EC,則回到SC,同時SP++,一直到坐標:EC,EP結束,其間無需再次設置的坐標,從而大大提高寫入速度。
(就是一行一行填充過去,碰到一行結尾自動換行,所以我們填充像素點的時候我們只需要確定要填充的像素點數目,屏幕會自動根據開設的窗口自動填充像素點)
效果展示一下:
4)讀取像素點數據:2Eh
列和頁寄存器分別重置為起始列 (SC) 和起始頁 (SP)。像素從中讀取
幀內存位于 (SC、SP)。然后,列寄存器遞增,并從幀存儲器中讀取像素,直到列寄存器等于結束列 (EC) 值。然后,列寄存器重置為 SC,并且頁面寄存器為遞增。從幀存儲器中讀取像素,直到頁面寄存器等于結束頁 (EP) 值或主機處理器發送另一個命令。
(從我們設置坐標(x,y)開始返回像素數據,同樣是根據掃描方向一行一行的返回像素數據,到行尾就自動換行)
發送完2Eh命令后,液晶芯片開始返回像素點數據
返回的一個參數數據是16位的
第一個參數返回的是空
第二個參數返回的是(高5位(D11 ~ D15)為第一個像素點的紅色數據分量,(D2 ~ D7)為第一個像素點的綠色數據分量,其余為空)
第三個參數返回的是(高5位(D11 ~ D15) 為第一個像素點的藍色數據分量,
(D3 ~ D7)為第二個像素點的紅色數據分量,其余為空)
…后面一直重復返回
所以我們要接收一個像素點顏色數據RGB565必須接收兩個參數的數據然后把他們拼湊起來。
讀取一個像素點的數據
自己對照著返回的參數,看看代碼是如何拼接一個像素點的數據的(16位RGB565),看代碼自己運算一下就非常清晰了
讀取某一個坐標點的像素數據
5)設置ILI9341的GRAM的掃描方向:0x36
直接看下圖解釋你會明明白白的
主要是設置:MY、MX、MV D5D7三個位,三個位組合就有07 8種模式
上面那個表對應下面這個圖
下面的圖是野火是掃描方向,仔細一對比你會發現好像都反了,其實是野火的屏幕裝的是倒過來的,所以把屏幕轉過180度就全對上了,當時這個問題困擾了好久問客服討論了很久才知道的
void ILI9341_GramScan ( uint8_t ucOption ) { //參數檢查,只可輸入0-7if(ucOption >7 )return;//根據模式更新LCD_SCAN_MODE的值,主要用于觸摸屏選擇計算參數LCD_SCAN_MODE = ucOption;//根據模式更新XY方向的像素寬度if(ucOption%2 == 0) {//0 2 4 6模式下X方向像素寬度為240,Y方向為320LCD_X_LENGTH = ILI9341_LESS_PIXEL;LCD_Y_LENGTH = ILI9341_MORE_PIXEL;}else {//1 3 5 7模式下X方向像素寬度為320,Y方向為240LCD_X_LENGTH = ILI9341_MORE_PIXEL;LCD_Y_LENGTH = ILI9341_LESS_PIXEL; }//0x36命令參數的高3位可用于設置GRAM掃描方向 ILI9341_Write_Cmd ( 0x36 );if(lcdid == LCDID_ILI9341){ILI9341_Write_Data ( 0x08 |(ucOption<<5));//根據ucOption的值設置LCD參數,共0-7種模式}else if(lcdid == LCDID_ST7789V){ILI9341_Write_Data ( 0x00 |(ucOption<<5));//根據ucOption的值設置LCD參數,共0-7種模式}ILI9341_Write_Cmd ( CMD_SetCoordinateX ); ILI9341_Write_Data ( 0x00 ); /* x 起始坐標高8位 */ILI9341_Write_Data ( 0x00 ); /* x 起始坐標低8位 */ILI9341_Write_Data ( ((LCD_X_LENGTH-1)>>8)&0xFF ); /* x 結束坐標高8位 */ ILI9341_Write_Data ( (LCD_X_LENGTH-1)&0xFF ); /* x 結束坐標低8位 */ILI9341_Write_Cmd ( CMD_SetCoordinateY ); ILI9341_Write_Data ( 0x00 ); /* y 起始坐標高8位 */ILI9341_Write_Data ( 0x00 ); /* y 起始坐標低8位 */ILI9341_Write_Data ( ((LCD_Y_LENGTH-1)>>8)&0xFF ); /* y 結束坐標高8位 */ ILI9341_Write_Data ( (LCD_Y_LENGTH-1)&0xFF ); /* y 結束坐標低8位 *//* write gram start */ILI9341_Write_Cmd ( CMD_SetPixel ); }上面的程序就是設置MY、MX、MV D5~D7三個位的值,從000 ~ 111 一共八種模式,然后根據模式不同開整屏 (320*240)的窗口,就是把000 ~ 111 z左移5位移到MY、MX、MV三個位上。
總結
- 上一篇: 靠着这Java面试210题,成功拿下了1
- 下一篇: 浏览器中修改视频播放速度