MCU如何高效地驅(qū)動LCD——RGB篇
????承接上一篇文章,這次來說說MCU該如何高效地驅(qū)動RGB接口的LCD,同時避免畫面撕裂。本片文章將會以帶LTDC外設(shè)的STM32型號來說明幾種驅(qū)動方法,并結(jié)合lvgl這個UI庫進行實戰(zhàn)。中間還會穿插一點關(guān)于ESP32S3的內(nèi)容,因為ESP32S3也有LCD控制器,但沒有STM32的LTDC這么強大。
????這里插入一張RGB LCD的時間參數(shù)圖,下文會提到其中的一些參數(shù)。

????首先要說的就是關(guān)于RGB屏如何避免撕裂。在上篇文章《LCD撕裂的產(chǎn)生原因和雙緩沖的概念》中有說到,避免LCD撕裂最重要的就是進行讀寫指針同步,而像RGB LCD這類GRAM在MCU側(cè)的LCD,最好的方法就是雙緩沖,創(chuàng)建2個幀緩沖區(qū)(framebuffer),在垂直消隱區(qū)(非可視區(qū))直接修改LCD控制器的幀緩沖區(qū)地址,即可避免畫面撕裂。那么如何能夠直到當(dāng)前LCD控制器刷新到垂直消隱區(qū)了呢,就需要使用中斷了。
????在說明具體的思路之前,先說一種非常常見的錯誤方式,這在上篇文章也有簡略提到。就是直接使用DMA2D將渲染好的數(shù)據(jù)填入當(dāng)前的幀緩沖區(qū),就像下面的代碼這樣。
????這個代碼著非常致命的一點,沒有做讀寫指針同步,假設(shè)當(dāng)前LCD控制器已經(jīng)刷新了半屏,突然DMA2D將整個framebuffer全部更新了,這時LCD控制器刷新下半屏的時候就會產(chǎn)生一個錯位線,即撕裂現(xiàn)象。
????為了避免這種現(xiàn)象的產(chǎn)生,我們可以利用LTDC的行中斷進行讀寫指針同步,將LTDC的行中斷配置為在消隱區(qū)的行,最簡單的是配置為第0行,因為這行一般都位于幀同步區(qū)(VSYNC)。然后在行中斷中設(shè)置標志位或者開啟DMA2D的中斷傳輸,如果使用了RTOS,還可以使用信號量(Semphore)配合,就像下面這樣。
????這是避免撕裂最常用且最簡單有效的方法。像樂鑫官方的esp32s3的RGB例程就是這么做的,在lvgl需要進行拷貝時,先利用信號量等待幀(vsync)事件,當(dāng)vsync事件產(chǎn)生并中斷時,此時LCD控制器正好刷到垂直消隱區(qū)后沿(VBP)之前,這時再發(fā)送信號量令lvgl開始進行數(shù)據(jù)拷貝,從而達到消除撕裂的效果。樂鑫官方的RGB例程lvgl flush代碼如下。
? ? 上面說的是RGB LCD最通用的消除畫面撕裂的方法。對于STM32強大的LTDC而言,還有另一種更為 方便高效的方法——雙緩沖(2個全尺寸framebuffer)。通過查看ltdc的HAL庫函數(shù)我們會發(fā)現(xiàn),有許多函數(shù)分為兩種,一種為帶_NoReload后綴的函數(shù),另一種則不帶后綴。先說一下這兩類函數(shù)的區(qū)別:
帶_NoReload后綴的函數(shù)是沒有配置重裝載寄存器的,調(diào)用該函數(shù)后配置只寫到影子寄存器中,不更新現(xiàn)有寄存器配置。
不帶后綴的函數(shù)配置了重裝載寄存器,調(diào)用該函數(shù)后配置寫入刀影子寄存器中后,立刻更新了現(xiàn)有寄存器的配置。
????跳轉(zhuǎn)到函數(shù)查看代碼,可以發(fā)現(xiàn),沒有帶后綴的函數(shù)多了一句代碼, 這句代碼配置了SRCR寄存器。

????通過查閱手冊可以看到,對SRCR這個寄存器的bit 0寫1表示立即更新寄存器,對bit 1寫1則表示在垂直消隱區(qū)前沿(VFP)的第一行進行更新。后者正是我們所需要的,而且HAL庫已經(jīng)提供了一個函數(shù)用于配置重裝載和裝載中斷HAL_Reload(),直接使用即可。那么顯而易見,使用LTDC令lvgl更新framebuffer的步驟因該是下面這樣:
1.調(diào)用HAL_LTDC_SetAddress_NoReload()設(shè)置新的framebuffer地址。
2.調(diào)用HAL_Reload()配置在垂直消隱區(qū)進行寄存器更新并開啟中斷。
3.在重裝載中斷回調(diào)HAL_LTDC_RealoadEventCallback()中設(shè)置標志位或則發(fā)送信號量。
4.在lvgl的disp_wait_flush()函數(shù)中獲取標志為或者信號量并調(diào)用lv_disp_flush_ready()。
以下是代碼示例:
? ? 這種方法還可以與lvgl的雙緩沖結(jié)合,需要令lvgl開啟全屏刷新,然后當(dāng)lvgl繪制好一幀數(shù)據(jù)后,交換LTDC的幀緩沖區(qū),令LTDC刷新剛渲染好的這個緩沖區(qū),另一塊緩沖區(qū)則交給lvgl渲染下一幀圖像。上篇文章也說過,LCD控制器層面的雙緩沖可以解決畫面撕裂,UI層面的雙緩沖可以提高渲染幀率,像這樣就可以達到了兩者都具有雙緩沖的效果了,既避免了畫面撕裂,又提高了渲染幀率。具體的代碼我將會提交到gitee供大家參考,鏈接在文章末尾,板子是我以往視頻中開源的一塊帶3.5寸LCD的UI開發(fā)板。
????RGB LCD除了比較吃RAM的空間和帶寬以外,算是要達到高效無撕裂最容易驅(qū)動的LCD了。后面我將會繼續(xù)講解8080接口的MCU屏和SPI屏該如何避免撕裂且高效地驅(qū)動。不過目前手頭上暫時沒有可用的板子,可能會鴿一段時間搞個板子吧。如果覺得對你有幫助的話還望點點大拇指,給Up一份支持。
gitee鏈接:https://gitee.com/Jumping99/ltdc-lvgl-demo.git