嵌入式中 gui 顯示單緩衝 雙緩衝 三緩衝的原理

2021-09-25 09:37:42 字數 3650 閱讀 2973

嵌入式中,gui 中影象的顯示最終通過呼叫底層 lcd 驅動提供的介面來將 【framebuffer】 重新整理到螢幕上。framebuffer 的大小與螢幕大小、螢幕解析度、顯示方式有關。對於 gui 而言,所有呼叫 lcd 驅動重新整理 framebuffer 到螢幕上顯示之前的繪製操作都是通過對 framebuffer 的操作完成的。這裡提及的操作主要是 memcpy、memset ,它們的速度將直接限制操作 framebuffer 的速度,因此一般都會編寫多個版本的 memset、memcpy 來優化設定、拷貝的速度。

上面的敘述表明 gui 中完全可以實現一套底層 lcd 驅動的介面,用以適配不同的 lcd 控制器。這樣就能夠遮蔽 lcd 控制器的差別,便於在不同的平台上進行移植。

lcd 控制器的正常工作需要配置一些顯示引數。如前肩後肩的延時等等。這裡提及的前後肩延時其實只是為了相容老的 lcd 控制器。這些控制器必須在重新整理 framebuffer 到螢幕上的前後進行延時,這個延時實際是為了消抖,以得到更好的顯示效果。

當配置完成 lcd 控制器後,你還要設定 lcd 重新整理時使用的 framebuffer 位址,與 framebuffer 大小。這一般在 gui 呼叫 lcd 提供的繪製介面中進行設定,設定後 lcd 控制器便會持續的從該位址按照指定的大小重新整理資料到螢幕上。

對於需要持續重新整理的 lcd 控制器而言,其對設定的 framebuffer 的讀取是一直在進行的,除非關閉 lcd 控制器。這就使得軟體的**邏輯中必須對緩衝區交換的時機進行判斷。這個判斷可以通過幀同步訊號來完成。一般來說,常見的 lcd 控制器會在刷完一幀之後發出乙個幀同步訊號,這個幀同步訊號就相當於通知使用者時機已到,可以設定下一幀的位址了。

顧名思義,單緩衝就是僅僅使用乙個 framebuffer 來完成繪製。此 framebuffer 的位址在設定給 lcd 控制器中的對應暫存器之前由 gui 填充。

由於我們只是用了乙個 framebuffer,這就可能導致 gui 在操作這個 framebuffer 的同時,lcd 控制器也在同時操作相同的記憶體。這樣如果 framebuffer 的內容有所變化,那麼我們可能會看到乙個重疊的顯示效果。這常見於用單緩衝去實現動態切換的內容及複雜的頁面的場景中。這也就說明單緩衝並不適合用來設計複雜的頁面,這是它的一大缺點。不過相較多緩衝機制,它的實現相對簡單,使用的記憶體也相對較少,這是單緩衝的優勢。

誠如其名,雙緩衝使用了兩個 framebuffer。常見的實現中這兩個 framebuffer 乙個稱為 online fb、乙個稱為 offline fb。這兩個 framebuffer 也可稱為乙個前置緩衝與乙個後置緩衝。前置緩衝是 lcd 控制器繪製正使用的緩衝區,後置緩衝是 gui 能夠繼續填充的緩衝區。

在實際的實現中,我們需要對這兩個緩衝區進行交換。緩衝區的交換並不會再次進行 memcpy,而是通過交換指標來實現。也就是說,lcd 當前正在使用的 online fb 指標將會與 offline fb 指標進行切換,這樣會極大的提高效率。

這個時機的確定要以 lcd 控制器提供的功能為準。常見的方式是使用幀同步訊號來完成,其它的方式請參考 lcd 控制器的使用者手冊。使用幀同步訊號來實現的情況中,獲取到乙個幀同步訊號就表明 lcd 已經刷完了上一幀,可以交換指標來重新整理的一幀。

**在這裡我們需要注意交換指標一定要在新的一幀重新整理開始之前完成。**一般在 lcd 控制器中設定的前後肩時間為交換指標操作提供了緩衝,不太容易出現問題。對於那些有兩個 framebuffer 位址暫存器的 lcd 控制器而言,交換可以通過寫入位址到當前未被用於顯示的位址暫存器中,由 lcd 控制器來完成具體的指標切換工作。

交換指標的安全保證讓雙緩衝的方式避免了單緩衝方式在動態內容顯示中的問題,增加的乙個緩衝區也在一定程度上提高了 gui 繪製的效率,這種方式的使用十分廣泛。

三緩衝的工作原理與雙緩衝類似,只是緩衝區的數目增加了乙個。這個增加的緩衝區讓緩衝區交換的實現更為複雜,卻也提供了乙個提高 gui 繪製效率的機會。在這種實現中我們將三個緩衝區分為乙個前置緩衝區與兩個後置緩衝區。當 cpu 填充 framebuffer 的速度非常快的時候,雙緩衝方式可能會出現兩個緩衝區都被占用的情況。這種情況下,由於緩衝區已經被用完,gui 無法繪製新的資料到framebuffer 中,這意味著它必須等待一次緩衝區交換的完成才能繼續填充新的緩衝區。

在相同的情況下,三緩衝增加的乙個緩衝區能夠在有限範圍內避免不必要的等待。當兩個緩衝區都被占用,gui 仍舊能夠獲取到乙個後置緩衝區,不用等待就能繼續在此緩衝區上進行繪製,這樣就能提高繪製的效率。

不過可以想象,在這種方式下緩衝區的交換比雙緩衝更難實現。如果我們再增加緩衝區數目,不僅記憶體占用會成倍增加,且緩衝區交換功能實現的難度會不斷上公升,而繪製效率的提公升空間卻可能微乎其微,因此我們很難看到諸如四緩衝、五緩衝之類的實現。

gui 在實際繪製中可能存在許多問題。這裡我想重點講講 framebuffer 記憶體屬性配置對顯示的一些影響,以 armv7-m 架構為例。

armv7-m 參考手冊的第三章講解了 arm 架構的記憶體模型。其中提到了記憶體的型別和屬性及記憶體的序列模型。

記憶體型別主要有三種——normal、device、strongly-ordered。framebuffer 的記憶體分配主要與 normal 型別的記憶體有關。

normal 記憶體可以分為非共享與共享兩個類別。這種分類是以記憶體是否能被多個處理器訪問劃分的。非共享 normal 記憶體只能被單個處理器訪問。共享 normal 記憶體能夠被多個處理器或其它系統主控訪問。

對於非共享 normal 記憶體,有四個具體的屬性可以配置。

write-through cacheable

write-back,write-allocate

write-back,no write-allocate

non-cacheable

潛在的問題

這裡的可能存在的問題主要與 cache 有關。對於可 cache 的記憶體屬性,第一種屬性會在資料寫入到 cache 之後立刻將改動更新到記憶體中,第

二、三種屬性不會立刻將 cache 快取的記憶體資料改動立刻寫回到記憶體中,最後一種屬性標誌記憶體區域不可 cache。

當你使用第

二、三中屬性配置 framebuffer 所屬的記憶體區域時,顯示時可能會出現部分資料殘缺,且很有規律。在這種情況中,由於記憶體屬性配置為寫回型, cache 中快取的一些介面資料沒有立刻更新到記憶體中,導致顯示出現部分殘缺。將記憶體屬性修改為第一種寫通型就能顯示正常的介面效果。第四種不可 cache 的配置也可以解決這個問題,只是它的效率不及第一種,因此不推薦使用。

按照我的理解。寫回型的配置其實是將資料變動更新到記憶體的時間延後了,一般會在當前的 cache 行被置換出去之前進行。這樣的方式在大多數時間內都有助於提高效能,卻並不適用於所有的情況,這裡就是乙個具體的例子。

本文描述了嵌入式中 lcd 控制器的基本工作原理及在 gui 顯示中經常使用的不同緩衝技術。通過對這幾種不同緩衝技術的描述,我們對 gui 呼叫 lcd 控制器完成繪製的過程有了更好的了解。在文章最後我講了乙個與 framebuffer 記憶體屬性配置有關的問題,這是乙個相對基礎的問題。實際開發中 gui 顯示不正常可能有多種原因。其中既可能有記憶體屬性配置不正確的問題,也可能有其它的問題,需要根據實際情況進行判定!

嵌入式GUI介紹

今天看了一下韋東山寫的 嵌入式linux開發完全手冊 解決了我很多關於qt的疑問。所以便寫一下今天的讀書總結吧。首先說一下linux的gui系統架構 linux下的gui大致可以分為六部分 1。x server 負責顯示,傳遞使用者輸入事件。主要包括鍵盤及滑鼠等硬體裝置的輸入 2,graohic l...

嵌入式GUI移植小結

幾天來,移植了乙個小型的gui,小,但是五臟俱全,基本體現了gui的理念,值得推薦學習。下面就移植的方方面面總結如下 1.gui的設計理念 我們知道,當我們不去動鍵盤,不去動觸控螢幕滑鼠的時候,是不會有gui動作的,中斷訊號時鐘訊號除外。所以,gui的設計出發點,實際上是事件,或者說是訊號,當然也可...

嵌入式GUI與Qt Embedded

由於開發中使用的開源嵌入式3d圖形庫klimt是基於opengl 和opengl es的,而opengl和opengl es需要有嵌入式gui的支援,以提供給它與圖形裝置的介面,以便把三維影象顯示給使用者。所以我需要選擇一種嵌入式gui系統,然後把它移植到嵌入式linux上,再有是把基於opengl...