0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

深度剖析STM32的“GPU”—DMA2D實(shí)例詳

RTThread物聯(lián)網(wǎng)操作系統(tǒng) ? 來(lái)源:RT-Thread社區(qū) ? 作者:夢(mèng)千年 ? 2021-06-30 14:50 ? 次閱讀

前言

GPU即圖形處理器,是現(xiàn)代顯卡的核心。在沒(méi)有GPU的時(shí)代,所有圖形的繪制都是由CPU來(lái)完成的,CPU需要計(jì)算圖形的邊界、顏色等數(shù)據(jù),并且負(fù)責(zé)將數(shù)據(jù)寫入顯存。

簡(jiǎn)單的圖形還沒(méi)有什么問(wèn)題,但隨著計(jì)算機(jī)的發(fā)展(尤其是游戲的發(fā)展),需要顯示的圖形圖像越來(lái)越復(fù)雜,CPU也就越來(lái)越力不從心。所以后來(lái)GPU應(yīng)運(yùn)而生,將CPU從繁重的圖形計(jì)算任務(wù)中拯救了出來(lái),大大加速了圖形的顯示速度。

單片機(jī)這邊也有類似的發(fā)展歷程。在早期的單片機(jī)使用場(chǎng)景中,極少有圖形顯示的需求。即使有,也只是簡(jiǎn)單的12864之類的顯示設(shè)備,運(yùn)算量不大,單片機(jī)的CPU可以很好的處理。

但是隨著嵌入式圖形的發(fā)展,單片機(jī)需要承擔(dān)的圖形計(jì)算和顯示任務(wù)越來(lái)越多,嵌入式系統(tǒng)的顯示分辨率和色彩也一路飆升。慢慢地,單片機(jī)的CPU對(duì)這些計(jì)算就開始力不從心了。

所以,自STM32F429開始,一個(gè)類似GPU的外設(shè)開始加入到STM32的單片機(jī)中,ST稱之為Chrom-ART Accelerator,也叫DMA2D(本文將使用此名稱)。DMA2D可以在很多2D繪圖的場(chǎng)合提供加速,完美嵌合了現(xiàn)代顯卡中“GPU”的功能。

雖然這個(gè)“GPU”只能提供2D加速,而且功能非常簡(jiǎn)單,與PC中的GPU不可同日而語(yǔ)。但是它已經(jīng)可以滿足大多數(shù)嵌入式開發(fā)中的圖形顯示加速需求,只要用好了DMA2D,我們?cè)趩纹瑱C(jī)上也可以做出流暢、華麗的UI效果。

本文將從實(shí)例出發(fā),介紹DMA2D在嵌入式圖形開發(fā)中的可以發(fā)揮的作用。目的是使讀者能簡(jiǎn)單、快速地對(duì)DAM2D的建立最基本的概念并且學(xué)會(huì)最基本的用法。

為了防止內(nèi)容過(guò)于晦澀和難懂,本文不會(huì)對(duì)DMA2D的高級(jí)功能和特性進(jìn)行深入地刨析(如詳細(xì)介紹DMA2D的架構(gòu)、全部的寄存器等等)。如果需要更加詳細(xì)、專業(yè)地學(xué)習(xí)DAM2D,可以在閱讀完本文后參考《STM32H743中文編程手冊(cè)》。

閱讀本文之前需要對(duì)STM32中的TFT液晶控制器(LTDC)和基本的圖形知識(shí)(如幀緩沖framebuffer、像素、顏色格式等概念)有一定的了解。

另,除了ST之外,其他不少廠商生產(chǎn)的MCU中也存在類似功能的外設(shè)(如NXP在RT系列中設(shè)計(jì)的的PxP),不過(guò)這些不在本文的討論范圍內(nèi),有興趣的朋友可以自行了解。

準(zhǔn)備工作

硬件準(zhǔn)備

可以使用任何的,帶有DMA2D外設(shè)的STM32開發(fā)板來(lái)驗(yàn)證本文中的例子,如STM32F429,STM32F746,STM32H750等MCU的開發(fā)板。本文中使用的開發(fā)板是ART-Pi 。

ART-Pi是由RT-Thread官方出品的開發(fā)板,采用了主頻高達(dá)480MHz的STM32H750XB+32MB SDRAM的強(qiáng)悍配置。而且板載了調(diào)試器(ST-Link V2.1),使用起來(lái)非常方便,特別適合各種技術(shù)方案的驗(yàn)證,用來(lái)作為本文的硬件演示平臺(tái)再合適不過(guò)了。

顯示屏可以是任意的彩色TFT顯示屏,推薦使用16位或24位顏色的RGB接口顯示屏。本文中使用的是一塊 3.5‘’ 的TFT液晶顯示屏,接口為RGB666,分辨率為320x240(QVGA)。在LTDC中,配置使用的顏色格式為RGB565

開發(fā)環(huán)境準(zhǔn)備

本文中介紹的內(nèi)容和出現(xiàn)的代碼可以在任何你喜歡的開發(fā)環(huán)境中使用,如RT-Thread Studio,MDK,IAR等。

開始本文的實(shí)驗(yàn)前你需要一個(gè)以framebuffer技術(shù)驅(qū)動(dòng)LCD顯示屏的基本工程。運(yùn)行本文中所有的代碼前都需要預(yù)先使能DMA2D。

使能DMA2D可以通過(guò)這個(gè)宏來(lái)實(shí)現(xiàn)(硬件初始化時(shí)使能一次即可):

1// 使用DMA2D之前一定要先使能DMA2D外設(shè)2__HAL_RCC_DMA2D_CLK_ENABLE();

DMA2D的簡(jiǎn)介

我們先來(lái)看看ST是怎么描述DMA2D的

乍一看有點(diǎn)晦澀,但其實(shí)說(shuō)白了就以下幾個(gè)功能:

顏色填充(矩形區(qū)域)

圖像(內(nèi)存)復(fù)制

顏色格式轉(zhuǎn)換(如YCbCr轉(zhuǎn)RGB或RGB888轉(zhuǎn)RGB565)

透明度混合(Alpha Blend)

前兩種都是針對(duì)內(nèi)存的操作,后兩個(gè)則是運(yùn)算加速操作。其中,透明度混合、顏色格式轉(zhuǎn)換可以和圖像復(fù)制一起進(jìn)行,這樣就帶來(lái)了較大的靈活性。

可以看到,ST對(duì)DMA2D的定位就像它的名字一樣,是一個(gè)針對(duì)圖像處理功能強(qiáng)化過(guò)的DMA。而在實(shí)際開發(fā)的過(guò)程中,我們會(huì)發(fā)現(xiàn)DMA2D的使用方式也非常類似傳統(tǒng)的DMA控制器。在某些非圖形處理場(chǎng)合,DMA2D甚至也可以代替?zhèn)鹘y(tǒng)的DMA來(lái)發(fā)揮作用。

需要注意的是,ST的不同產(chǎn)品線的DMA2D加速器是有微小區(qū)別的,比如STM32F4系列MCU的DMA2D就沒(méi)有ARGB和AGBR顏色格式互轉(zhuǎn)的功能,所以具體需要用到某個(gè)功能的時(shí)候,最好先查看編程手冊(cè)看所需的功能是否被支持。

本文只介紹所有平臺(tái)的DMA2D共有的功能。

DMA2D的工作模式

就像傳統(tǒng)DMA有外設(shè)到外設(shè),外設(shè)到存儲(chǔ)器,存儲(chǔ)器到外設(shè)三種工作模式一樣,DMA2D作為一個(gè)DMA,也分為以下四種工作模式:

寄存器到存儲(chǔ)器

存儲(chǔ)器到存儲(chǔ)器

存儲(chǔ)器到存儲(chǔ)器并執(zhí)行像素顏色格式轉(zhuǎn)換

存儲(chǔ)器到存儲(chǔ)器且支持像素顏色格式轉(zhuǎn)換和透明度混合

可以看出,前兩種模式起始就是簡(jiǎn)單的內(nèi)存操作,而后面兩種模式,則是在進(jìn)行內(nèi)存復(fù)制時(shí),根據(jù)需要同時(shí)進(jìn)行顏色格式轉(zhuǎn)換或/和透明度混合。

DMA2D和HAL庫(kù)

大多數(shù)情況下,使用HAL庫(kù)可以簡(jiǎn)化代碼編寫,提高可移植性。但是在DMA2D的使用時(shí)則是個(gè)例外。因?yàn)镠AL庫(kù)存在的最大問(wèn)題就是嵌套層數(shù)再加上各種安全檢測(cè)過(guò)多效率不夠高。

在操作別的外設(shè)時(shí),使用HAL庫(kù)損失的效率并不會(huì)有多大的影響。但是對(duì)于DMA2D這種以計(jì)算和加速為目的的外設(shè),考慮到相關(guān)的操作會(huì)在一個(gè)屏幕的繪制周期內(nèi)被多次調(diào)用,此時(shí)再使用HAL庫(kù)就會(huì)導(dǎo)致DAM2D的加速效率嚴(yán)重下降。

所以,我們大多時(shí)候都不會(huì)用HAL庫(kù)中的相關(guān)函數(shù)來(lái)對(duì)DMA2D進(jìn)行操作。為了效率,我們會(huì)直接操作寄存器,這樣才能起到最大化的加速效果。

因?yàn)槲覀兪褂肈MA2D的大多數(shù)場(chǎng)合都會(huì)頻繁變更工作模式,所以CubeMX中對(duì)DMA2D的圖形化配置也失去了意義。

DMA2D場(chǎng)景實(shí)例

1. 顏色填充

我們來(lái)思考一下如何把它繪制出來(lái)。

首先,我們需要使用白色來(lái)填充屏幕,作為圖案的背景。這個(gè)過(guò)程是不能忽略的,否則屏幕上原來(lái)顯示的圖案會(huì)對(duì)我們的主體產(chǎn)生干擾。然后,柱狀圖其實(shí)是由4個(gè)藍(lán)色的矩形方塊和一條線段構(gòu)成的,而線段也可以視作一個(gè)特殊的,高度為1的矩形。所以,這個(gè)圖形的繪制可以分解為一系列“矩形填充”操作:

使用白色填充一個(gè)大小等于屏幕大小的的矩形

使用藍(lán)色填充四個(gè)數(shù)據(jù)條

使用黑色填充一根高度為1的線段

在畫布中實(shí)現(xiàn)任意位置繪制任意大小的矩形的本質(zhì)就是將內(nèi)存區(qū)域中對(duì)應(yīng)像素位置的數(shù)據(jù)設(shè)定為指定的顏色。但是因?yàn)閒ramebuffer在內(nèi)存中的存儲(chǔ)是線性的,所以除非矩形的寬度正好和顯示區(qū)域的寬度重合,否看似連續(xù)的矩形的區(qū)域在內(nèi)存中的地址是不連續(xù)的。

下圖展示了典型的內(nèi)存分布情況,其中的數(shù)字表示了frame buffer中每個(gè)像素的內(nèi)存地址(相對(duì)首地址的偏移,這里忽略掉了一個(gè)像素占多個(gè)字節(jié)的情況),藍(lán)色區(qū)域是我們要填充的矩形??梢钥闯鼍匦螀^(qū)域的內(nèi)存地址是不連續(xù)的。

framebuffer的這種特性使得我們不能簡(jiǎn)單使用memset這類高效的操作來(lái)實(shí)現(xiàn)矩形區(qū)域的填充。通常情況下,我們會(huì)使用以下方式的雙重循環(huán)來(lái)填充任意矩形,其中xs和ys是矩形左上角在屏幕上的坐標(biāo),width和height表示矩形的寬和高,color表示需要填充的顏色:

1for(int y = ys; y 《 ys + height; y++){

2 for(int x = xs; x 《 xs + width; x++){

3 framebuffer[y][x] = color;

4 }

5}

代碼雖然簡(jiǎn)單,但實(shí)際執(zhí)行時(shí),大量的CPU周期浪費(fèi)在了判斷、尋址、自增等的操作,實(shí)際寫內(nèi)存的時(shí)間占比很少。這樣一來(lái),效率就會(huì)下降。

這時(shí)候DMA2D的寄存器到存儲(chǔ)器工作模式就可以發(fā)揮用場(chǎng)了,DAM2D可以以極高的速度填充矩形的內(nèi)存區(qū)域,即使這些區(qū)域在內(nèi)存中實(shí)際是不連續(xù)的。

首先,因?yàn)槲覀冎皇沁M(jìn)行內(nèi)存填充,而不需要進(jìn)行內(nèi)存拷貝,所以我們要讓DAM2D工作在寄存器到存儲(chǔ)器模式。這通過(guò)設(shè)置DMA2D的CR寄存器的[17:16]位為11來(lái)實(shí)現(xiàn),代碼如下:

1DMA2D-》CR = 0x00030000UL;

然后,我們要告訴DAM2D要填充的矩形的屬性,比如區(qū)域的起始地址在哪里,矩形的寬度有多少像素,矩形的高度有多少。

區(qū)域起始地址是矩形區(qū)域左上角第一個(gè)像素的內(nèi)存地址(圖中紅色像素的地址),這個(gè)地址由DAM2D的OMAR寄存器管理。而矩形的寬度和高度都是以像素為單位的,分別由NLR寄存器的高16位(寬度)和低16位(高度)來(lái)進(jìn)行管理,具體的代碼如下:

1DMA2D-》OMAR = (uint32_t)(&framebuffer[y][x]); // 設(shè)置填充區(qū)域的起始像素內(nèi)存地址2DMA2D-》NLR = (uint32_t)(width 《《 16) | (uint16_t)height; // 設(shè)置矩形區(qū)域的寬高

接著,因?yàn)榫匦卧趦?nèi)存中的地址不連續(xù),所以我們要告訴DMA2D在填充完一行的數(shù)據(jù)后,需要跳過(guò)多少個(gè)像素(即圖中黃色區(qū)域的長(zhǎng)度)。這個(gè)值由OOR寄存器管理。計(jì)算跳過(guò)的像素?cái)?shù)量有一個(gè)簡(jiǎn)單的方法,即顯示區(qū)域的寬度減去矩形的寬度即可。具體實(shí)現(xiàn)代碼如下:

1DMA2D-》OOR = screenWidthPx - width; // 設(shè)置行偏移,即跳過(guò)的像素

最后,我們需要告知DAM2D,你將使用什么顏色來(lái)進(jìn)行填充,顏色的格式是什么。這分別由OCOLR和OPFCCR寄存器來(lái)管理,其中顏色格式由LTDC_PIXEL_FORMAT_XXX宏來(lái)定義,具體代碼如下:

1DMA2D-》OCOLR = color; // 設(shè)置填充使用的顏色2DMA2D-》OPFCCR = pixelFormat; // 設(shè)置顏色格式,比如想設(shè)置成RGB565,就可以使用宏LTDC_PIXEL_FORMAT_RGB565

一切都設(shè)置完畢,DMA2D已經(jīng)獲取到了填充這個(gè)矩形所需要的全部信息,接下來(lái),我們要開啟DMA2D的傳輸,這通過(guò)將DMA2D的CR寄存器的第0位設(shè)置為1來(lái)實(shí)現(xiàn):

1DMA2D-》CR |= DMA2D_CR_START; // 開啟DMA2D的數(shù)據(jù)傳輸,DMA2D_CR_START是一個(gè)宏,其值為0x01

等DMA2D傳輸開始后,我們只需要等待它傳輸完畢即可。DAM2D傳輸完成后,會(huì)自動(dòng)把CR寄存器的第0位設(shè)置為0,所以我們可以通過(guò)以下代碼來(lái)等待DAM2D傳輸完成:

1while (DMA2D-》CR & DMA2D_CR_START) {} // 等待DMA2D傳輸完成

tips0:如果你使用了OS,則可以使能DMA2D的傳輸完畢中斷。然后我們可以創(chuàng)建一個(gè)信號(hào)量并且在開啟傳輸后等待它,隨后在DMA2D的傳輸完畢中斷服務(wù)函數(shù)中釋放該信號(hào)量。這樣的話CPU就可以在DMA2D工作的時(shí)候去干點(diǎn)別的事兒而不是在此處傻等。

tips1:當(dāng)然,由于實(shí)際執(zhí)行時(shí),DMA2D進(jìn)行內(nèi)存填充的速度實(shí)在是太快了,以至于OS切換任務(wù)的開銷都比這個(gè)時(shí)間要長(zhǎng),所以即便使用了OS,我們還是會(huì)選擇死等 :)。

為了函數(shù)的通用性考慮,起始傳輸?shù)刂泛托衅贫荚诤瘮?shù)外計(jì)算完畢后傳入,我們抽出的完整的函數(shù)代碼如下:

1static inline void DMA2D_Fill( void * pDst, uint32_t width, uint32_t height, uint32_t lineOff, uint32_t pixelFormat, uint32_t color) {

2 3 /* DMA2D配置 */

4 DMA2D-》CR = 0x00030000UL; // 配置為寄存器到儲(chǔ)存器模式 5 DMA2D-》OCOLR = color; // 設(shè)置填充使用的顏色,格式應(yīng)該與設(shè)置的顏色格式相同 6 DMA2D-》OMAR = (uint32_t)pDst; // 填充區(qū)域的起始內(nèi)存地址 7 DMA2D-》OOR = lineOff; // 行偏移,即跳過(guò)的像素,注意是以像素為單位 8 DMA2D-》OPFCCR = pixelFormat; // 設(shè)置顏色格式 9 DMA2D-》NLR = (uint32_t)(width 《《 16) | (uint16_t)height; // 設(shè)置填充區(qū)域的寬和高,單位是像素1011 /* 啟動(dòng)傳輸 */12 DMA2D-》CR |= DMA2D_CR_START;

1314 /* 等待DMA2D傳輸完成 */15 while (DMA2D-》CR & DMA2D_CR_START) {}

16}

為了方便編寫代碼,我們?cè)侔b一個(gè)針對(duì)所使用屏幕坐標(biāo)系的矩形填充函數(shù):

1void FillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color){

2 void* pDist = &(((uint16_t*)framebuffer)[y*320 + x]);

3 DMA2D_Fill(pDist, w, h, 320 - w, LTDC_PIXEL_FORMAT_RGB565, color);

4}

最后我們嘗試用代碼把本小節(jié)剛開始的示例圖表畫出來(lái):

1 // 填充背景色2 FillRect(0, 0, 320, 240, 0xFFFF);

3 // 繪制數(shù)據(jù)條4 FillRect(80, 80, 20, 120, 0x001f);

5 FillRect(120, 100, 20, 100, 0x001f);

6 FillRect(160, 40, 20, 160, 0x001f);

7 FillRect(200, 60, 20, 140, 0x001f);

8 // 繪制X軸9 FillRect(40, 200, 240, 1, 0x0000);

2.圖片顯示(內(nèi)存復(fù)制)

假設(shè)我們現(xiàn)在要開發(fā)一個(gè)游戲,然后想在屏幕上顯示一團(tuán)跳動(dòng)的火焰。一般是由美工先把火焰的每一幀都畫出來(lái),然后放到同一張圖片素材里面。

然后我們以一定的間隔輪流顯示每一幀圖像,就可以在屏幕上實(shí)現(xiàn)“跳動(dòng)的火焰”這個(gè)效果了。

我們現(xiàn)在略過(guò)素材文件加載到內(nèi)存的過(guò)程,假設(shè)這張素材圖片已經(jīng)在內(nèi)存中了。然后我們來(lái)考慮如何將其中的一幀圖片顯示到屏幕上。通常情況下,我們會(huì)這樣實(shí)現(xiàn):先計(jì)算得出每一幀的數(shù)據(jù)在內(nèi)存中的地址,然后將這一幀圖片的數(shù)據(jù)復(fù)制到framebuffer中相應(yīng)的位置即可。代碼類似于這樣:

1/**

2 * 將素材中的一幀畫面復(fù)制到framebuffer中的對(duì)應(yīng)位置

3 * index為畫面在幀序列中的索引

4 */ 5static void General_DisplayFrameAt(uint16_t index) {

6 // 宏說(shuō)明 7 // #define FRAME_COUNTS 25 // 幀數(shù)量 8 // #define TILE_WIDTH_PIXEL 96 // 每一幀畫面的寬度(等于高度) 9 // #define TILE_COUNT_ROW 5 // 素材中每一行有多少幀1011 // 計(jì)算幀起始地址12 uint16_t *pStart = (uint16_t *) img_fireSequenceFrame;

13 pStart += (index / TILE_COUNT_ROW) * (TILE_WIDTH_PIXEL * TILE_WIDTH_PIXEL * TILE_COUNT_ROW);

14 pStart += (index % TILE_COUNT_ROW) * TILE_WIDTH_PIXEL;

1516 // 計(jì)算素材地址偏移17 uint32_t offlineSrc = (TILE_COUNT_ROW - 1) * TILE_WIDTH_PIXEL;

18 // 計(jì)算framebuffer地址偏移(320是屏幕寬度)19 uint32_t offlineDist = 320 - TILE_WIDTH_PIXEL;

2021 // 將數(shù)據(jù)復(fù)制到framebuffer22 uint16_t* pFb = (uint16_t*) framebuffer;

23 for (int y = 0; y 《 TILE_WIDTH_PIXEL; y++) {

24 memcpy(pFb, pStart, TILE_WIDTH_PIXEL * sizeof(uint16_t));

25 pStart += offlineSrc + TILE_WIDTH_PIXEL;

26 pFb += offlineDist + TILE_WIDTH_PIXEL;

27 }

28}

可見要實(shí)現(xiàn)這個(gè)效果需要大量的內(nèi)存復(fù)制操作。在嵌入式系統(tǒng)中,需要大量數(shù)據(jù)復(fù)制的時(shí)候,硬件DMA的效率是最高的。但是硬件DMA只能搬運(yùn)地址連續(xù)的數(shù)據(jù),而這里,需要復(fù)制的數(shù)據(jù)在源圖片和frambuffer中的地址都是不連續(xù)的,這引來(lái)了額外的開銷(與第一小節(jié)中出現(xiàn)的問(wèn)題相同),也導(dǎo)致我們無(wú)法使用硬件DMA來(lái)進(jìn)行高效的數(shù)據(jù)復(fù)制。

所以,雖然我們實(shí)現(xiàn)了目標(biāo),但是效率不高(或者說(shuō)沒(méi)有達(dá)到最高)。

為了以最快的速度把素材圖片中的某一塊數(shù)據(jù)搬運(yùn)到幀緩沖中,我們來(lái)看如何使用DMA2D來(lái)實(shí)現(xiàn)。

首先,因?yàn)檫@次是要在存儲(chǔ)器中進(jìn)行數(shù)據(jù)復(fù)制,所以我們要把DMA2D的工作模式設(shè)定為“存儲(chǔ)器到存儲(chǔ)器模式”,這通過(guò)設(shè)置DMA2D的CR寄存器的[17:16]位為00來(lái)實(shí)現(xiàn),代碼如下:

1DMA2D-》CR = 0x00000000UL;

然后我們要分別設(shè)置源和目標(biāo)的內(nèi)存地址,與第一節(jié)中不同,因?yàn)閿?shù)據(jù)源也存在內(nèi)存偏移,所以我們要同時(shí)設(shè)定源和目標(biāo)位置的數(shù)據(jù)偏移

1DMA2D-》FGMAR = (uint32_t)pSrc; // 源地址2DMA2D-》OMAR = (uint32_t)pDst; // 目標(biāo)地址3DMA2D-》FGOR = OffLineSrc; // 源數(shù)據(jù)偏移(像素)4DMA2D-》OOR = OffLineDst; // 目標(biāo)地址偏移(像素)

然后依然是設(shè)置要復(fù)制的圖像的寬和高,以及顏色格式,這點(diǎn)與第一小節(jié)中的相同

1DMA2D-》FGPFCCR = pixelFormat;

2DMA2D-》NLR = (uint32_t)(xSize 《《 16) | (uint16_t)ySize;

同樣的方式,我們開啟DMA2D的傳輸,并等待傳輸完成:

1/* 啟動(dòng)傳輸 */2DMA2D-》CR |= DMA2D_CR_START;

34/* 等待DMA2D傳輸完成 */5while (DMA2D-》CR & DMA2D_CR_START) {}

最終我們抽出的函數(shù)如下:

1static void DMA2D_MemCopy(uint32_t pixelFormat, void * pSrc, void * pDst, int xSize, int ySize, int OffLineSrc, int OffLineDst)

2{

3 /* DMA2D配置 */ 4 DMA2D-》CR = 0x00000000UL;

5 DMA2D-》FGMAR = (uint32_t)pSrc;

6 DMA2D-》OMAR = (uint32_t)pDst;

7 DMA2D-》FGOR = OffLineSrc;

8 DMA2D-》OOR = OffLineDst;

9 DMA2D-》FGPFCCR = pixelFormat;

10 DMA2D-》NLR = (uint32_t)(xSize 《《 16) | (uint16_t)ySize;

1112 /* 啟動(dòng)傳輸 */13 DMA2D-》CR |= DMA2D_CR_START;

1415 /* 等待DMA2D傳輸完成 */16 while (DMA2D-》CR & DMA2D_CR_START) {}

17}

為了方便,我們包裝一個(gè)調(diào)用它的函數(shù):

1static void DMA2D_DisplayFrameAt(uint16_t index){

2 3 uint16_t *pStart = (uint16_t *)img_fireSequenceFrame;

4 pStart += (index / TILE_COUNT_ROW) * (TILE_WIDTH_PIXEL * TILE_WIDTH_PIXEL * TILE_COUNT_ROW);

5 pStart += (index % TILE_COUNT_ROW) * TILE_WIDTH_PIXEL;

6 uint32_t offlineSrc = (TILE_COUNT_ROW - 1) * TILE_WIDTH_PIXEL;

7 8 9 DMA2D_MemCopy(LTDC_PIXEL_FORMAT_RGB565, (void*) pStart, pDist, TILE_WIDTH_PIXEL, TILE_WIDTH_PIXEL, offlineSrc, offlineDist);

10}

然后輪流播放每一幀圖片,這里設(shè)置的幀間隔是50毫秒,并且將目標(biāo)地址定義到了frambuffer的中央:

1while(1){

2 for(int i = 0; i 《 FRAME_COUNTS; i++){

3 DMA2D_DisplayFrameAt(i);

4 HAL_Delay(FRAME_TIME_INTERVAL);

5 }

6}

3.圖片漸變切換

假設(shè)我們要開發(fā)一個(gè)看圖應(yīng)用,在兩張圖片進(jìn)行切換時(shí),直接進(jìn)行切換會(huì)顯得比較生硬,所以我們要加入切換時(shí)的動(dòng)態(tài)效果,而漸變切換(淡入淡出)是一個(gè)很經(jīng)常使用的,而且看起來(lái)還不錯(cuò)的效果。

這里我們需要先了解一下透明度混合(Alpha Blend)的基本概念。首先透明度混合需要有一個(gè)前景,一個(gè)背景。而混合的結(jié)果就相當(dāng)于透過(guò)前景看背景時(shí)的效果。如果前景完全不透明,那么就完全看不到背景,反之如果前景完全透明,那么就只能看到背景。而如果前景是半透明的,則結(jié)果就是兩者根據(jù)前景色的透明度按照一定的規(guī)則進(jìn)行混合。

如果1表示完全透明,0表示不透明,則透明度的混合公式如下,其中A是背景色,B是前景色:

1X(C)=(1-alpha)*X(B) + alpha*X(A)

因?yàn)轭伾蠷GB三個(gè)通道,所以我們需要對(duì)三通道都進(jìn)行計(jì)算,計(jì)算完成后在進(jìn)行組合:

1R(C)=(1-alpha)*R(B) + alpha*R(A)

2G(C)=(1-alpha)*G(B) + alpha*G(A)

3B(C)=(1-alpha)*B(B) + alpha*B(A)

而在程序中為了效率起見(CPU對(duì)于浮點(diǎn)的運(yùn)算速度很慢),我們并不用0~1這個(gè)范圍的值。通常情況下我們一般會(huì)使用一個(gè)8bit的數(shù)值來(lái)表示透明度,范圍從0~255。需要注意的是,這個(gè)數(shù)值越大表示越不透明,也就是說(shuō)255是完全不透明,而0表示完全透明(所以也叫不透明度),然后我們可以得到最終的公式:

1outColor = ((int) (fgColor * alpha) + (int) (bgColor) * (256 - alpha)) 》》 8;

實(shí)現(xiàn)RGB565顏色格式像素的透明度混合代碼:

1typedef struct{ 2 uint16_t r:5;

3 uint16_t g:6;

4 uint16_t b:5;

5}RGB565Struct;

6 7static inline uint16_t AlphaBlend_RGB565_8BPP(uint16_t fg, uint16_t bg, uint8_t alpha) {

8 RGB565Struct *fgColor = (RGB565Struct*) (&fg);

9 RGB565Struct *bgColor = (RGB565Struct*) (&bg);

10 RGB565Struct outColor;

1112 outColor.r = ((int) (fgColor-》r * alpha) + (int) (bgColor-》r) * (256 - alpha)) 》》 8;

13 outColor.g = ((int) (fgColor-》g * alpha) + (int) (bgColor-》g) * (256 - alpha)) 》》 8;

14 outColor.b = ((int) (fgColor-》b * alpha) + (int) (bgColor-》b) * (256 - alpha)) 》》 8;

151617 return *((uint16_t*)&outColor);

18}

了解了透明度混合的概念,也實(shí)現(xiàn)了單個(gè)像素的透明度混合后,我們來(lái)看如何實(shí)現(xiàn)圖片的漸變切換。

假設(shè)整個(gè)漸變?cè)?0幀內(nèi)完成,我們需要在內(nèi)存中開辟一塊兒大小等于圖片的緩沖區(qū)。然后我們以第一張圖片(當(dāng)前顯示的圖片)為背景,第二張圖片(接下來(lái)顯示的圖片)為前景,然后為前景設(shè)置一個(gè)透明度,對(duì)每個(gè)像素進(jìn)行透明度混合,并且將混合結(jié)果暫存至緩沖區(qū)中。待混合結(jié)束后,將緩沖區(qū)中的數(shù)據(jù)復(fù)制到framebuffer中即完成了一幀的顯示。接下來(lái)繼續(xù)進(jìn)行第二幀、第三幀……逐漸增大前景的不透明度,直到前景色的變?yōu)椴煌该?,即完成了圖片的漸變切換。

因?yàn)槊恳粠夹枰獙?duì)兩張圖片中的每一個(gè)像素都進(jìn)行混合運(yùn)算,這帶了來(lái)巨大的運(yùn)算量。交給CPU實(shí)現(xiàn)是很不明智的行為,所以我們還是把這些工作交給DMA2D來(lái)實(shí)現(xiàn)吧。

這次用到了DMA2D的混合功能,所以我們要使能DAM2D的帶顏色混合的存儲(chǔ)器到存儲(chǔ)器模式,對(duì)應(yīng)CR寄存器[17:16]位的值為10,即:

1DMA2D-》CR = 0x00020000UL; // 設(shè)置工作模式為存儲(chǔ)器到存儲(chǔ)器并帶顏色混合

然后分別設(shè)置前景、背景和輸出數(shù)據(jù)的內(nèi)存地址和數(shù)據(jù)傳輸偏移、傳輸圖像的寬和高:

1DMA2D-》FGMAR = (uint32_t)pFg; // 設(shè)置前景數(shù)據(jù)內(nèi)存地址2DMA2D-》BGMAR = (uint32_t)pBg; // 設(shè)置背景數(shù)據(jù)內(nèi)存地址3DMA2D-》OMAR = (uint32_t)pDst; // 設(shè)置數(shù)據(jù)輸出內(nèi)存地址45DMA2D-》FGOR = offlineFg; // 設(shè)置前景數(shù)據(jù)傳輸偏移6DMA2D-》BGOR = offlineBg; // 設(shè)置背景數(shù)據(jù)傳輸偏移7DMA2D-》OOR = offlineDist; // 設(shè)置數(shù)據(jù)輸出傳輸偏移89DMA2D-》NLR = (uint32_t)(xSize 《《 16) | (uint16_t)ySize; // 設(shè)置圖像數(shù)據(jù)寬高(像素)

設(shè)置顏色格式。這里設(shè)置前景色的顏色格式時(shí)需要注意,因?yàn)槿绻褂玫氖茿RGB這樣的顏色格式,那么我們進(jìn)行透明度混合時(shí),顏色數(shù)據(jù)中本身的alpha通道就會(huì)對(duì)混合結(jié)果產(chǎn)生影響,所以我們這里要設(shè)定在進(jìn)行混合操作時(shí),忽略前景色自身的alpha通道。并強(qiáng)制設(shè)定混合時(shí)的透明度。

輸出顏色格式和背景顏色格式

1DMA2D-》FGPFCCR = pixelFormat // 設(shè)置前景色顏色格式2 | (1UL 《《 16) // 忽略前景顏色數(shù)據(jù)中的Alpha通道3 | ((uint32_t)opa 《《 24); // 設(shè)置前景色不透明度45DMA2D-》BGPFCCR = pixelFormat; // 設(shè)置背景顏色格式6DMA2D-》OPFCCR = pixelFormat; // 設(shè)置輸出顏色格式

tips0:有時(shí)我們會(huì)遇到一張帶有透明通道的圖片與背景疊加顯示的情況,此時(shí)就不應(yīng)該禁用顏色本身的alpha通道

tips1:這個(gè)模式下,我們不僅可以進(jìn)行顏色混合,還可以同時(shí)轉(zhuǎn)換顏色格式,可以根據(jù)需要設(shè)置前景和背景以及輸出的顏色格式

最后,啟動(dòng)傳輸即可:

1/* 啟動(dòng)傳輸 */2DMA2D-》CR |= DMA2D_CR_START;

34/* 等待DMA2D傳輸完成 */5while (DMA2D-》CR & DMA2D_CR_START) {}

完整代碼如下:

1void _DMA2D_MixColors(void* pFg, void* pBg, void* pDst,

2 uint32_t offlineFg, uint32_t offlineBg, uint32_t offlineDist,

3 uint16_t xSize, uint16_t ySize,

4 uint32_t pixelFormat, uint8_t opa) {

5 6 DMA2D-》CR = 0x00020000UL; // 設(shè)置工作模式為存儲(chǔ)器到存儲(chǔ)器并帶顏色混合 7 8 DMA2D-》FGMAR = (uint32_t)pFg; // 設(shè)置前景數(shù)據(jù)內(nèi)存地址 9 DMA2D-》BGMAR = (uint32_t)pBg; // 設(shè)置背景數(shù)據(jù)內(nèi)存地址10 DMA2D-》OMAR = (uint32_t)pDst; // 設(shè)置數(shù)據(jù)輸出內(nèi)存地址1112 DMA2D-》FGOR = offlineFg; // 設(shè)置前景數(shù)據(jù)傳輸偏移13 DMA2D-》BGOR = offlineBg; // 設(shè)置背景數(shù)據(jù)傳輸偏移14 DMA2D-》OOR = offlineDist; // 設(shè)置數(shù)據(jù)輸出傳輸偏移1516 DMA2D-》NLR = (uint32_t)(xSize 《《 16) | (uint16_t)ySize; // 設(shè)置圖像數(shù)據(jù)寬高(像素)1718 DMA2D-》FGPFCCR = pixelFormat // 設(shè)置前景色顏色格式19 | (1UL 《《 16) // 忽略前景顏色數(shù)據(jù)中的Alpha通道20 | ((uint32_t)opa 《《 24); // 設(shè)置前景色不透明度2122 DMA2D-》BGPFCCR = pixelFormat; // 設(shè)置背景顏色格式23 DMA2D-》OPFCCR = pixelFormat; // 設(shè)置輸出顏色格式2425 /* 啟動(dòng)傳輸 */26 DMA2D-》CR |= DMA2D_CR_START;

2728 /* 等待DMA2D傳輸完成 */29 while (DMA2D-》CR & DMA2D_CR_START) {}

30}

編寫測(cè)試代碼,這次不需要二次包裝函數(shù)了:

1void DMA2D_AlphaBlendDemo(){

2 3 const uint16_t lcdXSize = 320, lcdYSize = 240;

4 const uint8_t cnvFrames = 60; // 60幀完成切換 5 const uint32_t interval = 33; // 每秒30幀 6 uint32_t time = 0;

7 8 // 計(jì)算輸出位置的內(nèi)存地址 9 uint16_t distX = (lcdXSize - DEMO_IMG_WIDTH) / 2;

10 uint16_t distY = (lcdYSize - DEMO_IMG_HEIGHT) / 2;

11 uint16_t* pFb = (uint16_t*) framebuffer;

12 uint16_t* pDist = pFb + distX + distY * lcdYSize;

13 uint16_t offlineDist = lcdXSize - DEMO_IMG_WIDTH;

1415 uint8_t nextImg = 1;

16 uint16_t opa = 0;

17 void* pFg = 0;

18 void* pBg = 0;

19 while(1){

20 // 切換前景/背景圖片21 if(nextImg){

22 pFg = (void*)img_cat;

23 pBg = (void*)img_fox;

24 }

25 else{

26 pFg = (void*)img_fox;

27 pBg = (void*)img_cat;

28 }

2930 // 完成切換31 for(int i = 0; i 《 cnvFrames; i++){

32 time = HAL_GetTick();

33 opa = 255 * i / (cnvFrames-1);

34 _DMA2D_MixColors(pFg, pBg, pDist,

35 0,0,offlineDist,

36 DEMO_IMG_WIDTH, DEMO_IMG_HEIGHT,

37 LTDC_PIXEL_FORMAT_RGB565, opa);

38 time = HAL_GetTick() - time;

39 if(time 《 interval){

40 HAL_Delay(interval - time);

41 }

42 }

43 nextImg = !nextImg;

44 HAL_Delay(5000);

45 }

46}

性能對(duì)比

前面介紹了三種嵌入式圖形開發(fā)種的實(shí)例,并對(duì)分別介紹了通過(guò)傳統(tǒng)和DMA2D實(shí)現(xiàn)的方法。這時(shí)候肯定有朋友會(huì)問(wèn),DMA2D實(shí)現(xiàn),比起傳統(tǒng)方法實(shí)現(xiàn),到底能快多少呢?我們來(lái)實(shí)際測(cè)試一下。

共同的測(cè)試條件如下:

framebuffer放置在SDRAM中,320x240,RGB565

SDRAM工作頻率100MHz,CL2,16位帶寬。

MCU為STM32H750XB,主頻400MHz,開啟I-Cache和D-Cache

代碼和資源在內(nèi)部Flash上,64位AXI總線,速度為200MHz。

GCC編譯器(版本:arm-atollic-eabi-gcc-6.3.1)

測(cè)試項(xiàng)目:矩形填充

繪制上一章第1節(jié)中的圖表,繪制10000次,統(tǒng)計(jì)結(jié)果

測(cè)試項(xiàng)目:內(nèi)存復(fù)制

繪制上一章第2節(jié)中的序列幀10000幀,統(tǒng)計(jì)結(jié)果

測(cè)試項(xiàng)目:透明度混合

漸變切換上一章第3小節(jié)中的兩張圖片100次,每次30幀完成,共計(jì)3000幀

混合結(jié)果直接輸出到framebuffer,不再通過(guò)緩沖區(qū)緩沖

性能測(cè)試總結(jié)

由上面的測(cè)試結(jié)果可以看出,DAM2D至少有2個(gè)優(yōu)勢(shì):

一是速度更快:在部分項(xiàng)目中,DMA2D實(shí)現(xiàn)的速度相比純軟件實(shí)現(xiàn)最高可以達(dá)到30倍的差距!這還是在主頻高達(dá)400MHz還帶L1-Cache的STM32H750平臺(tái)上測(cè)試的結(jié)果,如果是在無(wú)cache且主頻較低的STM32F4平臺(tái)上進(jìn)行測(cè)試,差距會(huì)進(jìn)一步拉大。

二是性能更加穩(wěn)定:由測(cè)試結(jié)果可以看出,DMA2D實(shí)現(xiàn)的方式受編譯器優(yōu)化等級(jí)的影響非常小,幾乎可以忽略不計(jì),這意味著,無(wú)論你使用IAR,GCC或是MDK,使用DMA2D都可以達(dá)到相同的性能表現(xiàn)。不會(huì)出現(xiàn)同一段代碼移植后性能相差很大的情況。

除這兩個(gè)直觀的結(jié)果外,其實(shí)還有第三點(diǎn)優(yōu)勢(shì),那就是代碼編寫更加簡(jiǎn)單。DMA2D的寄存器不多,而且比較直觀。在某些場(chǎng)合,使用起來(lái)要比軟件實(shí)現(xiàn)方便的多。

結(jié)語(yǔ)

本文中的三個(gè)實(shí)例,都是我本人在嵌入式圖形開發(fā)中經(jīng)常遇到的情況。實(shí)際上,DMA2D的用法還有很多,有興趣的話可以參考《STM32H743中文編程手冊(cè)》中的相關(guān)內(nèi)容,相信有了本文的基礎(chǔ),在閱讀里面的內(nèi)容時(shí)一定會(huì)事半功倍。

受限于作者的技術(shù),文章中的內(nèi)容無(wú)法做到100%的正確,如果存在錯(cuò)誤,請(qǐng)大家指出,謝謝。

編輯:jq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • mcu
    mcu
    +關(guān)注

    關(guān)注

    146

    文章

    16667

    瀏覽量

    347819
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    10702

    瀏覽量

    209401
  • gpu
    gpu
    +關(guān)注

    關(guān)注

    27

    文章

    4591

    瀏覽量

    128147
  • 液晶控制器
    +關(guān)注

    關(guān)注

    0

    文章

    11

    瀏覽量

    7542
  • DMA2D
    +關(guān)注

    關(guān)注

    0

    文章

    5

    瀏覽量

    2091

原文標(biāo)題:STM32的“GPU”——DMA2D實(shí)例詳解

文章出處:【微信號(hào):RTThread,微信公眾號(hào):RTThread物聯(lián)網(wǎng)操作系統(tǒng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    請(qǐng)問(wèn)解碼bmp圖片時(shí)是否可以使用dma2d功能 ?

    請(qǐng)教下,解碼bmp圖片時(shí)是否可以使用dma2d功能 ? 具體描述如下: 沒(méi)有使用 emWin 就是先讀取整副 bmp 的數(shù)據(jù),之后解碼暫存,之后使用 dma2d 功能顯示, 有沒(méi)有這個(gè)必要或者這個(gè)能否能否實(shí)現(xiàn)啊,如果確定使用 d
    發(fā)表于 04-30 06:39

    STM32CubeMx使用GUI_DrawGradientH GUI_DrawGradientV繪制一直顯示黑色,是哪里出錯(cuò)?

    使用STM32CubeMx配置 LTDC + DMA2D + FMC + GRAPHICS STemWin(開啟CRC),直接使用STM32CubeMx生成代碼,未進(jìn)行手動(dòng)修改。 問(wèn)題: 1
    發(fā)表于 04-26 06:38

    Chrom-Art Accelerator? 控制器中文手冊(cè)

    DMA2D LTDC FMC中文手冊(cè)
    發(fā)表于 04-25 17:13 ?0次下載

    STM32L4R9移植touchgfx GUI顯示控件異?;ㄆ恋脑??

    移植touchgfx工程后 (1)開啟GUI出顯示異常,問(wèn)題不能確定,懷疑是dma2d送數(shù)據(jù)不正確,目前未找到touchgfx的填充界面函數(shù)在那里??? (2)關(guān)閉GUI顯示函數(shù),直接調(diào)用底層dma2d寫的清屏函數(shù)可以顯示正常!
    發(fā)表于 04-07 08:30

    FPGA在深度學(xué)習(xí)應(yīng)用中或?qū)⑷〈?b class='flag-5'>GPU

    現(xiàn)場(chǎng)可編程門陣列 (FPGA) 解決了 GPU 在運(yùn)行深度學(xué)習(xí)模型時(shí)面臨的許多問(wèn)題 在過(guò)去的十年里,人工智能的再一次興起使顯卡行業(yè)受益匪淺。英偉達(dá) (Nvidia) 和 AMD 等公司的股價(jià)也大幅
    發(fā)表于 03-21 15:19

    DMA2D傳輸錯(cuò)誤導(dǎo)致touchgfx無(wú)法正常工作,UI畫面卡死怎么解決?

    程序正常運(yùn)行過(guò)程中,用手觸碰外部的sdram上的引腳后,UI畫面不動(dòng),仿真看到DMA2D的Transfer error標(biāo)志位被置起,touchgfx無(wú)法正常工作,復(fù)位重啟后正常工作;推測(cè)sdram上
    發(fā)表于 03-21 06:54

    stm32l4r9i-disco ARGB圖片不能正確顯示怎么解決?

    stm32l4r9i-disco開發(fā),使用板子上帶的390 x 390 DSI屏顯示ARGB8888圖片。具體用到了 DMA2D,GFXMMU和LTDC,但是顯示有問(wèn)題。圖片一分為二,而且是從屏幕中間開始顯示。請(qǐng)問(wèn)各位有沒(méi)有什么建議?
    發(fā)表于 03-19 07:17

    STM32H747I-DISCO驅(qū)動(dòng)STM32F4DIS-CAM攝像頭模塊,LCD一直灰屏是什么原因?

    嘗試使用STM32Cube_FW_H7_V1.11.0ProjectsSTM32H747I-DISCOExamplesDCMI中的例程,使用SDRAM中的相機(jī)幀緩沖器捕獲相機(jī)圖像,再使用DMA2D傳輸?shù)絃CD顯示。 程序燒錄到板子上一切正常,且綠燈閃爍,但是LCD一直灰屏
    發(fā)表于 03-15 06:35

    Stm32mp135打開cache之后,用作ltdc的顯存地址數(shù)據(jù)就會(huì)異常怎么解決?

    函數(shù),把相關(guān)地址進(jìn)行保護(hù)呢? 2-我看了芯片手冊(cè),沒(méi)找到芯片有DMA2D,請(qǐng)問(wèn)還有什么可以進(jìn)行圖形加速的嗎?或者說(shuō),可以使用MDMA代替DMA2D進(jìn)行顯存數(shù)據(jù)搬運(yùn)嗎? 感謝大家?。?!
    發(fā)表于 03-07 07:55

    STM32DMA的五大問(wèn)題

    1,DMA控制器的內(nèi)部結(jié)構(gòu)STM32中的DMA控制器是一種用于在外設(shè)和存儲(chǔ)器之間傳輸數(shù)據(jù)的專用硬件。DMA控制器的內(nèi)部結(jié)構(gòu)主要包括以下幾個(gè)關(guān)鍵部分:通道:
    的頭像 發(fā)表于 12-10 08:00 ?1482次閱讀
    <b class='flag-5'>STM32</b>的<b class='flag-5'>DMA</b>的五大問(wèn)題

    STM32 DMA傳輸?shù)膯?wèn)題分析

    用戶使用STM32G473RET6芯片,開發(fā)環(huán)境STM32CubeMX+Keil(LL庫(kù))。使用DMA1通道1,在半傳輸中斷和完全傳輸中斷里,拷貝ADC采集的數(shù)據(jù)。在應(yīng)用過(guò)程中發(fā)現(xiàn)DMA
    的頭像 發(fā)表于 12-01 09:19 ?2274次閱讀
    <b class='flag-5'>STM32</b> <b class='flag-5'>DMA</b>傳輸?shù)膯?wèn)題分析

    深度剖析 IGBT 柵極驅(qū)動(dòng)注意事項(xiàng)

    深度剖析 IGBT 柵極驅(qū)動(dòng)注意事項(xiàng)
    的頭像 發(fā)表于 11-24 14:48 ?539次閱讀
    <b class='flag-5'>深度</b><b class='flag-5'>剖析</b> IGBT 柵極驅(qū)動(dòng)注意事項(xiàng)

    基于深度學(xué)習(xí)的3D點(diǎn)云實(shí)例分割方法

    3D實(shí)例分割(3DIS)是3D領(lǐng)域深度學(xué)習(xí)的核心問(wèn)題。給定由點(diǎn)云表示的 3D 場(chǎng)景,我們尋求為每個(gè)點(diǎn)分配語(yǔ)義類和唯一的
    發(fā)表于 11-13 10:34 ?1633次閱讀
    基于<b class='flag-5'>深度</b>學(xué)習(xí)的3<b class='flag-5'>D</b>點(diǎn)云<b class='flag-5'>實(shí)例</b>分割方法

    SWM341 DMA2D模塊介紹

    SWM341 DMA2D模塊介紹
    的頭像 發(fā)表于 11-06 17:11 ?596次閱讀
    SWM341 <b class='flag-5'>DMA2D</b>模塊介紹

    stm32 LL庫(kù)開發(fā)-DMA使用方法

    本文以stm32 F303K8為例,不同型號(hào)的DMA配置方法略有不同。
    的頭像 發(fā)表于 10-26 14:58 ?1095次閱讀
    <b class='flag-5'>stm32</b> LL庫(kù)開發(fā)-<b class='flag-5'>DMA</b>使用方法