完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980
第56章 STM32H7的DMA2D應用之刷色塊,位圖和Alpha混合
本章節為大家講解DMA2D應用中經常用到的刷色塊,刷位圖,Alpha混合和圖片混合的實現。
56.1 初學者重要提示
56.2 DMA2D驅動設計
56.3 制作C文件格式的位圖
56.4 DMA2D常用操作(重要)
56.5 DMA2D驅動移植和使用
56.6 實驗例程設計框架
56.7 實驗例程說明(MDK)
56.8 實驗例程說明(IAR)
56.9 總結
56.1 初學者重要提示
- 學習本章節前,務必優先學習第55章,需要對DMA2D的基礎知識有個認識。
- DMA2D里面有一個重要的概念就是行偏移,這知識點務必要認識到位,詳見本章2.2小節。
- DMA2D可以直接繪制ARGB8888,RGB565顏色格式位圖,並且可以方便的做各種透明效果和圖像混合顯示。
- LCD的加速全靠DMA2D,所有務必要熟練掌握其用法。
56.2 DMA2D驅動設計
56.2.1 DMA2D驅動設計思路
DMA2D的驅動設計比較省事:
- 用戶僅需調用函數__HAL_RCC_DMA2D_CLK_ENABLE使能DMA2D即可使用。
- 默認DMA2D的API都是采用阻塞式,這種方式在使用RTOS的時候比較方便,用戶可以將GUI任務的優先級設置的僅比空閑任務高即可,這樣有高優先級任務要執行,可以及時切換到高優先級任務里,GUI任務等待DMA2D執行完成即可。從而可以充分利用DMA2D和CPU,使芯片性能得到最大發揮。
- 最重要的一條,所有的DMA2D操作,直接采用寄存器方式,不再使用HAL庫給的API,讓性能得到最大發揮。
56.2.2 行偏移的含義(重要)
理解DMA2D傳輸的關鍵就是理解行偏移。前景層,背景層和輸出區都有一個行偏移的寄存器,為了方便大家理解,這里畫一個框圖:

條件:
- 前景層,背景層和輸出區分辨率都是800*480分辨率,顏色格式均為RGB565。
- 將前景層里面起始坐標(40,30),長480,高272的數據與背景層里面起始坐標(50,40),長480,高272的數據復制到輸出區起始地址(60,50),長480,高272的區域。
引出問題:
那么問題來了,前景層和背景層的起始坐標在各自數據緩沖區的起始位置都比較好計算。比如前景層就是前景層首地址加上30*800*2 + 40*2,乘以2的原因是RGB565顏色格式的1個像素占用兩個字節。
而難點就在如何保證前景層復制完480長度的數據后,如何切換到下一行。這個時候,行偏移就派上用場了,行偏移的意思是一行結束到下一行開始的距離,單位像素個數(也就是上面框圖中兩個紅色箭頭的總長度)。通過這個行偏移,我們就可以從前景層復制出來480*272的數據。
同理,背景層和輸出區的行偏移也是這個意思。
56.3 制作C文件格式的位圖
由於DMA2D刷新圖片要用到,所以本小節為大家介紹下位圖的制作。
56.3.1 什么是位圖
位圖(bitmap),又稱為點陣圖,是使用像素陣列來表示圖像。位圖中每個位置的像素都有自己的顏色值,這些顏色值是由RGB組合或者灰度值來表示。其中,RGB是指的Red紅色,Green綠色和Blue藍色,任何顏色都可以由這三種顏色來組成。電腦端繪圖類的軟件基本都有自定義顏色功能,可以很好的說明RGB三原色的作用:
根據位深度,可以將位圖分為1位(單色),2位(4色,CGA),4位(16色,VGA),8位(256色),16位(增強色),24位(真彩色)和32位等。
關於位圖,還有個概念就是alpha通道。所謂alpha通道就是指在原有的圖片編碼方法的基礎上,增加像素的透明度信息。圖形處理中,通常把RGB三種顏色信息稱為紅通道、綠通道和藍通道,相應的把透明度稱為Alpha通道。
56.3.2 圖標下載
這里為大家推薦一個圖標下載網址:http://www.easyicon.net/ ,此網站非常好用,需要什么圖標直接檢索關鍵字就可以了。這里以關鍵字cartoon進行檢索,最終選擇地址:
https://www.easyicon.net/581941-Lufy_cartoon_icon.html里面的圖標,我們下載128*128點陣大小的PNG圖片(下載的圖片已經放在了本章教程配套例子的Doc文件夾里面):
下面我們分兩步走,分別將其轉換為ARGB8888格式位圖和RGB565格式位圖。
56.3.3 轉換PNG圖片為ARGB8888格式位圖
下載小軟件BmpCvt:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93478 。
- 第1步:打開BmpCvtST.exe ,直接將PNG格式的圖片拖到此軟件里面即可,或者點擊File->Open進行加載也是可以的。
- 第2步:點擊File->Save as,彈出如下窗口
上面截圖中共分了4步進行操作,其中第2步修改名字是因為原有的名字太長了,不方便程序代碼的調用。
- 第3步:第2步操作完畢后,彈出如下窗口:
第1個選項的True color with alpha顏色格式是ABGR,我們這里選擇的第2個,這個是ARGB格式。當然,也可以選擇ABGR格式,因為H7的DMA2D可以設置Alpha通過翻轉和R通道與B通道交互位置,從而實現ABGR轉為ARGB格式。
點擊OK按鈕后會在桌面出現一個新文件,即lufy.c,保存在桌面是因為第2步中選擇的路徑是桌面。
打開lufy.c文件,代碼如下:
/********************************************************************* * SEGGER Microcontroller GmbH & Co. KG * * Solutions for real time microcontroller applications * * www.segger.com * ********************************************************************** * * * C-file generated by * * * * Bitmap Converter (ST) for emWin V5.32. * * Compiled Oct 8 2015, 11:58:22 * * * * (c) 1998 - 2015 Segger Microcontroller GmbH & Co. KG * * * ********************************************************************** * * * Source file: pic * * Dimensions: 64 * 64 * * NumColors: 16bpp: 65536 * * * ********************************************************************** */ #include <stdlib.h> #include "GUI.h" #ifndef GUI_CONST_STORAGE #define GUI_CONST_STORAGE const #endif extern GUI_CONST_STORAGE GUI_BITMAP bmlufy; static GUI_CONST_STORAGE unsigned long _aclufy[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0042454E, 0x00494C4E, 0x00393E50, 0x0046402A, 0x00968C62, 0x093E3C26, 0x2A787653, 0x597F8266, 0x606F6F4D, 0x5F7D7D54, 0x37747B50, 0x0B455035, 0x00745F3F, 0x00615856, 0x00655751, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, /* 后面的數據未列出 */ }; GUI_CONST_STORAGE GUI_BITMAP bmlufy = { 128, // xSize 128, // ySize 512, // BytesPerLine 32, // BitsPerPixel (unsigned char *)_aclufy, // Pointer to picture data NULL, // Pointer to palette GUI_DRAW_BMPM8888I }; /*************************** End of file ****************************/
對於生成的代碼僅需用到數組static GUI_CONST_STORAGE unsigned long _aclufy[],這個數值要稍微修改下,將GUI_CONST_STORAGE修改為const即可用到工程里面。const表示數組存儲到flash里面。
56.3.4 轉換PNG圖片為RGB565格式位圖
轉換方法與56.4.1小節相似,主要下面兩個地方不同:
- 第1點不同:使用電腦端的畫圖小軟件將前面下載的圖標轉換為BMP格式(PNG圖片中的透明通道會濾被掉),再用BmpCvt軟件打開后的效果如下,已經沒有Alpha通道。
- 第2點不同,轉換格式選擇如下:
轉換后生成的代碼如下:
/********************************************************************* * SEGGER Microcontroller GmbH & Co. KG * * Solutions for real time microcontroller applications * * www.segger.com * ********************************************************************** * * * C-file generated by * * * * Bitmap Converter (ST) for emWin V5.44. * * Compiled Nov 10 2017, 08:52:20 * * * * (c) 1998 - 2017 Segger Microcontroller GmbH & Co. KG * * * ********************************************************************** * * * Source file: Lufy_cartoon_128px_581941_easyiconnet * * Dimensions: 128 * 128 * * NumColors: 16bpp: 65536 * * * ********************************************************************** */ #include <stdlib.h> #include "GUI.h" #ifndef GUI_CONST_STORAGE #define GUI_CONST_STORAGE const #endif extern GUI_CONST_STORAGE GUI_BITMAP bmLufy_cartoon_128px_581941_easyiconnet; static GUI_CONST_STORAGE unsigned short _acLufy_cartoon_128px_581941_easyiconnet[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xF7BE, 0xE73C, 0xD699, 0xC657, 0xCE77, 0xDF1A, 0xF7BE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, /* 后面的數據未列出 */ }; GUI_CONST_STORAGE GUI_BITMAP bmlufy = { 128, // xSize 128, // ySize 256, // BytesPerLine 16, // BitsPerPixel (unsigned char *) aclufy, // Pointer to picture data NULL, // Pointer to palette GUI_DRAW_BMPM565 }; /*************************** End of file ****************************/
這里生成的代碼也是僅需用到數組static GUI_CONST_STORAGE unsigned long _aclufy[],並將GUI_CONST_STORAGE修改為const即可用到工程里面。
56.4 DMA2D常用操作(重要)
DMA2D的常用API要熟練掌握,后面的GUI的底層驅動加速,JPEG硬解,攝像頭等部分都要用到。這里為大家介紹如下幾個常用API:
- _DMA2D_Fill
- _DMA2D_Copy
- _DMA2D_MixColorsBulk
- _DMA2D_AlphaBlendingBulk
- _DMA2D_DrawAlphaBitmap
56.4.1 函數_DMA2D_Fill
函數原型:
1. /* 2. ****************************************************************************************************** 3. * 函 數 名: _DMA2D_Fill 4. * 功能說明: DMA2D顏色填充功能 5. * 形 參: pDst 顏色數據目的地址 6. * xSize 色塊X軸大小,即每行像素數 7. * ySize 色塊Y軸大小,即行數 8. * OffLine 前景層圖像的行偏移 9. * ColorIndex 色塊顏色值 10. * PixelFormat 目標區顏色格式 11. * 返 回 值: 無 12. ****************************************************************************************************** 13. */ 14. static void _DMA2D_Fill(void * pDst, 15. uint32_t xSize, 16. uint32_t ySize, 17. uint32_t OffLine, 18. uint32_t ColorIndex, 19. uint32_t PixelFormat) 20. { 21. 22. /* DMA2D采用寄存器到存儲器模式, 這種模式用不到前景層和背景層 */ 23. DMA2D->CR = 0x00030000UL | (1 << 9); 24. DMA2D->OCOLR = ColorIndex; 25. DMA2D->OMAR = (uint32_t)pDst; 26. DMA2D->OOR = OffLine; 27. DMA2D->OPFCCR = PixelFormat; 28. DMA2D->NLR = (uint32_t)(xSize << 16) | (uint16_t)ySize; 29. 30. /* 啟動傳輸 */ 31. DMA2D->CR |= DMA2D_CR_START; 32. 33. /* 等待DMA2D傳輸完成 */ 34. while (DMA2D->CR & DMA2D_CR_START) {} 35. }
函數描述:
此函數主要用於LCD的顏色填充。
- 第23行,設置DMA2D采用寄存器往存儲器傳輸數據模式,即DMA2D將OCOLR寄存器設置顏色值填充到存儲器里面。
- 第24行,OCOLR寄存器用於設置輸出顏色值,特別注意要跟第27行的輸出顏色格式匹配。比如設置的是RGB565,那么設置的顏色值就要是16位色的。
- 第25行,設置輸出填充區的首地址。
- 第26行,設置輸出行偏移。
- 第27行,設置輸出顏色格式,如果是輸出到LCD顯存,那么此格式要與LCD顏色格式一致,否則顯示不正常。
- 第28行,高16位用於設置每行的像素數,低16位用於設置行數,合並起來決定總傳輸次數。
- 第31-34行,啟動傳輸,並等待傳輸完成。
注意事項:
- 使用此函數前務必要調用函數__HAL_RCC_DMA2D_CLK_ENABLE使能DMA2D時鍾。
使用舉例:
起始坐標(24, 20),輸出顏色格式RGB565,繪制一個128x128的紅色填充區:
1. _DMA2D_Fill((void *)(SDRAM_LCD_BUF1 + g_LcdWidth*20*2 + 24*2), /* 顯示起始地址(24, 20) */ 2. 128, /* 色塊長 */ 3. 128, /* 色塊高 */ 4. g_LcdWidth-128, /* 色塊行偏移 */ 5. CL_RED, /* 色塊顏色 */ 6. LTDC_PIXEL_FORMAT_RGB565); /* 色塊顏色格式 */
- 第1行,根據設置的起始坐標,計算起始坐標在LCD顯存中的具體位置。
SDRAM_LCD_BUF1表示LCD顯存首地址。
g_LcdWidth*20*2計算的是行數占用的字節數。
24*2計算的是所在行的具體地址。
乘以2是因為RGB565顏色格式的1個像素占用兩個字節。
- 第4行,輸出行偏移的意思就是一行結束到下一行開始的距離,單位像素個數。由於填充區的長度是128,那么行偏移就是LCD的長度減去128。
56.4.2 函數_DMA2D_Copy
函數原型:
1. /* 2. ****************************************************************************************************** 3. * 函 數 名: _DMA2D_Copy 4. * 功能說明: 通過DMA2D從前景層復制指定區域的顏色數據到目標區域 5. * 形 參: pSrc 顏色數據源地址 6. * pDst 顏色數據目的地址 7. * xSize 目的區域的X軸大小,即每行像素數 8. * ySize 目的區域的Y軸大小,即行數 9. * OffLineSrc 前景層圖像的行偏移 10. * OffLineDst 輸出的行偏移 11. * PixelFormat 目標區顏色格式 12. * 返 回 值: 無 13. ****************************************************************************************************** 14. */ 15. static void _DMA2D_Copy(void * pSrc, 16. void * pDst, 17. uint32_t xSize, 18. uint32_t ySize, 19. uint32_t OffLineSrc, 20. uint32_t OffLineDst, 21. uint32_t PixelFormat) 22. { 23. 24. /* DMA2D采用存儲器到存儲器模式, 這種模式是前景層作為DMA2D輸入 */ 25. DMA2D->CR = 0x00000000UL | (1 << 9); 26. DMA2D->FGMAR = (uint32_t)pSrc; 27. DMA2D->OMAR = (uint32_t)pDst; 28. DMA2D->FGOR = OffLineSrc; 29. DMA2D->OOR = OffLineDst; 30. 31. /* 前景層和輸出區域都采用的RGB565顏色格式 */ 32. DMA2D->FGPFCCR = LTDC_PIXEL_FORMAT_RGB565; 33. DMA2D->OPFCCR = LTDC_PIXEL_FORMAT_RGB565; 34. 35. DMA2D->NLR = (uint32_t)(xSize << 16) | (uint16_t)ySize; 36. 37. /* 啟動傳輸 */ 38. DMA2D->CR |= DMA2D_CR_START; 39. 40. /* 等待DMA2D傳輸完成 */ 41. while (DMA2D->CR & DMA2D_CR_START) {} 42. }
函數描述:
此函數用於從前景層復制指定區域的顏色數據到目標區域。
- 第25行,設置DMA2D采用存儲器往存儲器傳輸數據模式,輸入存儲器只能是前景層。即DMA2D將FGMAR寄存器所指向的存儲數據傳輸到寄存器OMAR所指向的存儲器。
- 第26行,設置前景層數據首地址。
- 第27行,設置輸出存儲器首地址。
- 第28行,設置前景層行偏移。
- 第29行,設置輸出區行偏移。
- 第32-33行,設置前景層和輸出區域都采用RGB565顏色格式。另外注意,如果是輸出到LCD顯存,那么輸出顏色格式要與LCD顏色格式一致,否則顯示不正常。
- 第35行,高16位用於設置每行的像素數,低16位用於設置行數,合並起來決定總傳輸次數。
- 第38-41行,啟動傳輸,並等待傳輸完成。
注意事項:
- 使用此函數前務必要調用函數__HAL_RCC_DMA2D_CLK_ENABLE使能DMA2D時鍾。
使用舉例:
將大小128*128,顏色格式為RGB565的位圖繪制到LCD起始坐標為(328, 20)的區域,輸出顏色格式也配置為RGB565。
1. _DMA2D_Copy((uint32_t *)_acmickey, /* 位圖地址 */ 2. (uint32_t *)(SDRAM_LCD_BUF1 + g_LcdWidth*20*2 + 328*2), /* 顯示起始地址(328, 20) */ 3. 128, /* 位圖長 */ 4. 128, /* 位圖高 */ 5. 0, /* 位圖行偏移 */ 6. g_LcdWidth-128, /* 目標區行偏移 */ 7. LTDC_PIXEL_FORMAT_RGB565); /* 目標區顏色格式 */
- 第1行是位圖首地址。
- 第2行,根據設置的起始坐標,計算起始坐標在LCD顯存中的具體位置。
SDRAM_LCD_BUF1表示LCD顯存首地址。
g_LcdWidth*20*2計算的是行數占用的字節數。
328*2計算的是所在行的具體地址。
乘以2是因為RGB565顏色格式的1個像素占用兩個字節。
- 第5行是位圖的行偏移,行偏移的意思就是一行結束到下一行開始的距離,單位像素個數。由於整個位圖都要繪制,所有行偏移就是0。
- 第6行,輸出行偏移的,由於輸出區需要的長度是128,那么行偏移就是LCD的長度減去128。
56.4.3 函數_DMA2D_MixColorsBulk
函數原型:
1. /* 2. ****************************************************************************************************** 3. * 函 數 名: _DMA2D_MixColorsBulk 4. * 功能說明: 前景層和目標區域的顏色混合 5. * 形 參: pColorFG 前景層數據源地址 6. * OffLineSrcFG 前景層圖像的行偏移 7. * pColorDst 目標區數據地址 8. * OffLineDst 目標區的行偏移 9. * xSize 目的區域的X軸大小,即每行像素數 10. * ySize 目的區域的Y軸大小,即行數 11. * Intens 設置前景層的透明度,255表示完全不透明,0表示完全透明 12. * 返 回 值: 無 13. ****************************************************************************************************** 14. */ 15. static void _DMA2D_MixColorsBulk(uint32_t * pColorFG, 16. uint32_t OffLineSrcFG, 17. uint32_t * pColorDst, 18. uint32_t OffLineDst, 19. uint32_t xSize, 20. uint32_t ySize, 21. uint8_t Intens) 22. { 23. /* DMA2D采用存儲器到存儲器模式, 這種模式前景層和背景層作為DMA2D輸入,且支持顏色格式轉換和顏色混合 */ 24. DMA2D->CR = 0x00020000UL | (1 << 9); 25. DMA2D->FGMAR = (uint32_t)pColorFG; 26. DMA2D->BGMAR = (uint32_t)pColorDst; 27. DMA2D->OMAR = (uint32_t)pColorDst; 28. DMA2D->FGOR = OffLineSrcFG; 29. DMA2D->BGOR = OffLineDst; 30. DMA2D->OOR = OffLineDst; 31. 32. /* 前景層,背景層和輸出區都是用的RGB565格式 */ 33. DMA2D->FGPFCCR = LTDC_PIXEL_FORMAT_RGB565 34. | (1UL << 16) 35. | ((uint32_t)Intens << 24); 36. DMA2D->BGPFCCR = LTDC_PIXEL_FORMAT_RGB565; 37. DMA2D->OPFCCR = LTDC_PIXEL_FORMAT_RGB565; 38. 39. DMA2D->NLR = (uint32_t)(xSize << 16) | (uint16_t)ySize; 40. 41. /* 啟動傳輸 */ 42. DMA2D->CR |= DMA2D_CR_START; 43. 44. /* 等待DMA2D傳輸完成 */ 45. while (DMA2D->CR & DMA2D_CR_START) {} 46. }
函數描述:
此函數用於前景層和目標區域的顏色混合,並將混合后的圖像輸出到目標區域。
- 第24行,DMA2D采用存儲器到存儲器模式, 這種模式把前景層和背景層作為DMA2D輸入,且支持顏色格式轉換和顏色混合。
- 第25行,設置前景層數據首地址。
- 第26行,設置背景層數據首地址,這里是把輸出區中的數據作為背景層。
- 第27行,設置輸出存儲器首地址。
- 第28行,設置前景層行偏移。
- 第29行,設置背景層行偏移。
- 第29行,設置輸出區行偏移。
- 第33-37行,前景層,背景層和輸出區都采用RGB565格式,並且為前景層配置一個Alpha值。另外注意,如果是輸出到LCD顯存,那么輸出顏色格式要與LCD顏色格式一致,否則顯示不正常。
- 第39行,高16位用於設置每行的像素數,低16位用於設置行數,合並起來決定總傳輸次數。
- 第42-45行,啟動傳輸,並等待傳輸完成。
注意事項:
- 使用此函數前務必要調用函數__HAL_RCC_DMA2D_CLK_ENABLE使能DMA2D時鍾。
使用舉例:
將大小128*128,顏色格式為RGB565的位圖繪制到LCD起始坐標為(176, 168)的區域,輸出顏色格式也配置為RGB565,透明度設置為200(255表示完全不透明,0表示完全透明)。
1. _DMA2D_MixColorsBulk((uint32_t *)_achuoying, /* 位圖地址 */ 2. 0, /* 位圖行偏移 */ 3. (uint32_t *)(SDRAM_LCD_BUF1 + g_LcdWidth*168*2 + 176*2), /* 顯示起始地址(176, 168) */ 4. g_LcdWidth-128, /* 目標區行偏移 */ 5. 128, /* 目標區長 */ 6. 128, /* 目標區高 */ 7. 200); /* 位圖顯示透明度200 */
- 第1行是位圖首地址。
- 第2行是位圖的行偏移,行偏移的意思就是一行結束到下一行開始的距離,單位像素個數。由於整個位圖都要繪制,所有行偏移就是0。
- 第3行,根據設置的起始坐標,計算起始坐標在LCD顯存中的具體位置。
SDRAM_LCD_BUF1表示LCD顯存首地址。
g_LcdWidth*168*2計算的是行數占用的字節數。
176*2計算的是所在行的具體地址。
乘以2是因為RGB565顏色格式的1個像素占用兩個字節。
- 第4行,輸出行偏移的,由於輸出區需要的長度是128,那么行偏移就是LCD的長度減去128。
56.4.4 函數_DMA2D_AlphaBlendingBulk
函數原型:
1. /* 2. ****************************************************************************************************** 3. * 函 數 名: _DMA2D_AlphaBlendingBulk 4. * 功能說明: 前景層和背景層的顏色混合 5. * 形 參: pColorFG 前景層源數據地址 6. * OffLineSrcFG 前景層源數據行偏移 7. * pColorBG 背景層源數據地址 8. * OffLineSrcBG 背景層源數據行偏移 9. * pColorDst 目標區地址 10. * OffLineDst 目標區行偏移 11. * xSize 目標區域的X軸大小,即每行像素數 12. * ySize 目標區域的Y軸大小,即行數 13. * 返 回 值: 無 14. ****************************************************************************************************** 15. */ 16. static void _DMA2D_AlphaBlendingBulk(uint32_t * pColorFG, 17. uint32_t OffLineSrcFG, 18. uint32_t * pColorBG, 19. uint32_t OffLineSrcBG, 20. uint32_t * pColorDst, 21. uint32_t OffLineDst, 22. uint32_t xSize, 23. uint32_t ySize) 24. { 25. /* DMA2D采用存儲器到存儲器模式, 這種模式前景層和背景層作為DMA2D輸入,且支持顏色格式轉換和顏色混合 */ 26. DMA2D->CR = 0x00020000UL | (1 << 9); 27. DMA2D->FGMAR = (uint32_t)pColorFG; 28. DMA2D->BGMAR = (uint32_t)pColorBG; 29. DMA2D->OMAR = (uint32_t)pColorDst; 30. DMA2D->FGOR = OffLineSrcFG; 31. DMA2D->BGOR = OffLineSrcBG; 32. DMA2D->OOR = OffLineDst; 33. 34. /* 前景層,背景層采用ARGB8888格式,輸出區采用RGB565格式 */ 35. DMA2D->FGPFCCR = LTDC_PIXEL_FORMAT_ARGB8888; 36. DMA2D->BGPFCCR = LTDC_PIXEL_FORMAT_ARGB8888; 37. DMA2D->OPFCCR = LTDC_PIXEL_FORMAT_RGB565; 38. DMA2D->NLR = (uint32_t)(xSize << 16) | (uint16_t)ySize; 39. 40. /* 啟動傳輸 */ 41. DMA2D->CR |= DMA2D_CR_START; 42. 43. /* 等待DMA2D傳輸完成 */ 44. while (DMA2D->CR & DMA2D_CR_START) {} 45. }
函數描述:
此函數用於前景層和背景層的顏色混合,並將混合后的圖像輸出到目標區域。
- 第26行,DMA2D采用存儲器到存儲器模式, 這種模式把前景層和背景層作為DMA2D輸入,且支持顏色格式轉換和顏色混合。
- 第27行,設置前景層數據首地址。
- 第28行,設置背景層數據首地址。
- 第29行,設置輸出存儲器首地址。
- 第30行,設置前景層行偏移。
- 第31行,設置背景層行偏移。
- 第32行,設置輸出區行偏移。
- 第35-37行,前景層,背景層采用ARGB8888格式,輸出區采用RGB565格式。另外注意,如果是輸出到LCD顯存,那么輸出顏色格式要與LCD顏色格式一致,否則顯示不正常。
- 第38行,高16位用於設置每行的像素數,低16位用於設置行數,合並起來決定總傳輸次數。
- 第41-44行,啟動傳輸,並等待傳輸完成。
注意事項:
- 使用此函數前務必要調用函數__HAL_RCC_DMA2D_CLK_ENABLE使能DMA2D時鍾。
使用舉例:
將兩個大小128*128,顏色格式為ARGB8888的位圖混合后繪制到LCD起始坐標為(24, 168)的區域,輸出顏色格式配置為RGB565。
1. _DMA2D_AlphaBlendingBulk((uint32_t *)_aclufei, /* 前景層位圖地址 */ 2. 0, /* 前景層行偏移 */ 3. (uint32_t *)_acsuolong, /* 背景層位圖地址 */ 4. 0, /* 背景層行偏移 */ 5. (uint32_t *)(SDRAM_LCD_BUF1 + g_LcdWidth*168*2 + 24*2), /* 顯示起始地址(24, 168) */ 6. g_LcdWidth-128, /* 目標區行偏移 */ 7. 128, /* 目標區長 */ 8. 128); /* 目標區高 */
- 第1行是前景層位圖首地址。
- 第2行是位圖的行偏移,行偏移的意思就是一行結束到下一行開始的距離,單位像素個數。由於整個位圖都要繪制,所有行偏移就是0。
- 第3行是背景層位圖首地址。
- 第4行是背景層行偏移。
- 第5行,根據設置的起始坐標,計算起始坐標在LCD顯存中的具體位置。
SDRAM_LCD_BUF1表示LCD顯存首地址。
g_LcdWidth*168*2計算的是行數占用的字節數。
24*2計算的是所在行的具體地址。
乘以2是因為RGB565顏色格式的1個像素占用兩個字節。
- 第6行,輸出行偏移的,由於輸出區需要的長度是128,那么行偏移就是LCD的長度減去128。
56.4.5 函數_DMA2D_DrawAlphaBitmap
函數原型:
1. /* 2. ****************************************************************************************************** 3. * 函 數 名: _DMA2D_DrawAlphaBitmap 4. * 功能說明: ARGB8888格式位圖顯示 5. * 形 參: pDst 目標區地址 6. * pSrc 源數據地址,即位圖首地址 7. * xSize 目標區域的X軸大小,即每行像素數 8. * ySize 目標區域的Y軸大小,即行數 9. * OffLineSrc 源數據行偏移 10. * OffLineDst 目標區行偏移 11. * PixelFormat 目標區顏色格式 12. * 返 回 值: 無 13. ****************************************************************************************************** 14. */ 15. static void _DMA2D_DrawAlphaBitmap(void * pDst, 16. void * pSrc, 17. uint32_t xSize, 18. uint32_t ySize, 19. uint32_t OffLineSrc, 20. uint32_t OffLineDst, 21. uint32_t PixelFormat) 22. { 23. /* DMA2D采用存儲器到存儲器模式, 這種模式前景層和背景層作為DMA2D輸入,且支持顏色格式轉換和顏色混合 */ 24. DMA2D->CR = 0x00020000UL | (1 << 9); 25. DMA2D->FGMAR = (uint32_t)pSrc; 26. DMA2D->BGMAR = (uint32_t)pDst; 27. DMA2D->OMAR = (uint32_t)pDst; 28. DMA2D->FGOR = OffLineSrc; 29. DMA2D->BGOR = OffLineDst; 30. DMA2D->OOR = OffLineDst; 31. 32. /* 前景層顏色格式是LTDC_PIXEL_FORMAT_ARGB8888,即位圖的顏色格式,背景層和輸出區顏色格式可配置 */ 33. DMA2D->FGPFCCR = LTDC_PIXEL_FORMAT_ARGB8888; 34. DMA2D->BGPFCCR = PixelFormat; 35. DMA2D->OPFCCR = PixelFormat; 36. DMA2D->NLR = (uint32_t)(xSize << 16) | (uint16_t)ySize; 37. 38. /* 啟動傳輸 */ 39. DMA2D->CR |= DMA2D_CR_START; 40. 41. /* 等待DMA2D傳輸完成 */ 42. while (DMA2D->CR & DMA2D_CR_START) {} 43. }
函數描述:
此函數用於在指定位置顯示ARGB8888格式位圖。
- 第24行,DMA2D采用存儲器到存儲器模式, 這種模式把前景層和背景層作為DMA2D輸入,且支持顏色格式轉換和顏色混合。
- 第25行,設置前景層數據首地址。
- 第26行,設置背景層數據首地址,這里是把輸出區中的數據作為背景層。
- 第27行,設置輸出存儲器首地址。
- 第28行,設置前景層行偏移。
- 第29行,設置背景層行偏移。
- 第30行,設置輸出區行偏移。
- 第33-35行,前景層顏色格式是LTDC_PIXEL_FORMAT_ARGB8888,即位圖的顏色格式。背景層和輸出區顏色格式可配置。另外注意,如果是輸出到LCD顯存,那么輸出顏色格式要與LCD顏色格式一致,否則顯示不正常。
- 第36行,高16位用於設置每行的像素數,低16位用於設置行數,合並起來決定總傳輸次數。
- 第39-42行,啟動傳輸,並等待傳輸完成。
注意事項:
- 使用此函數前務必要調用函數__HAL_RCC_DMA2D_CLK_ENABLE使能DMA2D時鍾。
使用舉例:
將大小128*128,顏色格式為ARGB8888的位圖繪制到LCD起始坐標為(176, 20)的區域,輸出顏色格式配置為RGB565。
1. _DMA2D_DrawAlphaBitmap((void *)(SDRAM_LCD_BUF1 + g_LcdWidth*20*2 + 176*2), /* 顯示起始地址(176, 20) */ 2. (void *)_aclufei, /* 位圖地址 */ 3. 128, /* 位圖長 */ 4. 128, /* 位圖高 */ 5. 0, /* 位圖行偏移 */ 6. g_LcdWidth-128, /* 目標區行偏移 */ 7. LTDC_PIXEL_FORMAT_RGB565); /* 目標區顏色格式 */
- 第1行,根據設置的起始坐標,計算起始坐標在LCD顯存中的具體位置。
SDRAM_LCD_BUF1表示LCD顯存首地址。
g_LcdWidth*20*2計算的是行數占用的字節數。
176*2計算的是所在行的具體地址。
乘以2是因為RGB565顏色格式的1個像素占用兩個字節。
- 第2行是位圖首地址。
- 第5行是位圖的行偏移,行偏移的意思就是一行結束到下一行開始的距離,單位像素個數。由於整個位圖都要繪制,所有行偏移就是0。
- 第6行,輸出行偏移的,由於輸出區需要的長度是128,那么行偏移就是LCD的長度減去128。
56.5 DMA2D驅動移植和使用
DMA2D的驅動移植比較簡單,用戶僅需調用函數__HAL_RCC_DMA2D_CLK_ENABLE使能時鍾,然后用到哪個函數直接復到工程里面調用即可。
56.6 實驗例程設計框架
通過程序設計框架,讓大家先對配套例程有一個全面的認識,然后再理解細節,本次實驗例程的設計框架如下:

第1階段,上電啟動階段:
- 這部分在第14章進行了詳細說明。
第2階段,進入main函數:
- 第1步,硬件初始化,主要是MPU,Cache,HAL庫,系統時鍾,滴答定時器,LED ,LCD,和SDRAM。
- 第2步,DMA2D應用程序設計部分,主要展示DMA2D刷色塊,刷位圖,Alpha混合和圖片混合功能。
56.7 實驗例程說明(MDK)
配套例子:
V7-035_DMA2D功能測試(顯示色塊,位圖,Alpha混合和圖片混合等)
實驗目的:
- 學習DMA2D顯示色塊,位圖,Alpha混合和圖片混合等。
實驗內容:
- 啟動1個200ms的自動重裝定時器,讓LED2每200ms翻轉一次。
- 第1個圖:使用DMA2D刷色塊。
- 第2個圖:顯示ARGB8888位圖。
- 第3個圖:顯示RGB565位圖。
- 第4個圖:兩個位圖混合。
- 第5個圖:Alpha透明度200的位圖顯示。
- 第6個圖:Alpha透明度100的位圖顯示。
LCD界面顯示效果如下:
上電后串口打印的信息:
波特率 115200,數據位 8,奇偶校驗位無,停止位 1
程序設計:
系統棧大小分配:
RAM空間用的DTCM:
硬件外設初始化
硬件外設的初始化是在 bsp.c 文件實現:
/* ********************************************************************************************************* * 函 數 名: bsp_Init * 功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_Init(void) { /* 配置MPU */ MPU_Config(); /* 使能L1 Cache */ CPU_CACHE_Enable(); /* STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鍾: - 調用函數HAL_InitTick,初始化滴答時鍾中斷1ms。 - 設置NVIV優先級分組為4。 */ HAL_Init(); /* 配置系統時鍾到400MHz - 切換使用HSE。 - 此函數會更新全局變量SystemCoreClock,並重新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用於代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。 - 默認不開啟,如果要使能此選項,務必看V7開發板用戶手冊第8章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder並開啟 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart(); #endif bsp_InitKey(); /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */ bsp_InitTimer(); /* 初始化滴答定時器 */ bsp_InitUart(); /* 初始化串口 */ bsp_InitExtIO(); /* 初始化FMC總線74HC574擴展IO. 必須在 bsp_InitLed()前執行 */ bsp_InitLed(); /* 初始化LED */ bsp_InitI2C(); /* 初始化I2C總線 */ TOUCH_InitHard(); /* 初始化觸摸芯片,LCD面板型號的檢查也在此函數,所以要在函數LCD_InitHard前調用 */ LCD_InitHard(); /* 初始化LCD */ }
MPU配置和Cache配置:
數據Cache和指令Cache都開啟。配置了AXI SRAM區(本例子未用到AXI SRAM),FMC的擴展IO區和SDRAM。由於SDRAM要用於LCD的顯存,方便起見,直接將其配置為WT模式。
/* ********************************************************************************************************* * 函 數 名: MPU_Config * 功能說明: 配置MPU * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void MPU_Config( void ) { MPU_Region_InitTypeDef MPU_InitStruct; /* 禁止 MPU */ HAL_MPU_Disable(); /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x24000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /* 配置FMC擴展IO的MPU屬性為Device或者Strongly Ordered */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x60000000; MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER1; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /* 配置SDRAM的MPU屬性為Write through, read allocate,no write allocate */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0xC0000000; MPU_InitStruct.Size = MPU_REGION_SIZE_32MB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER2; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /*使能 MPU */ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); } /* ********************************************************************************************************* * 函 數 名: CPU_CACHE_Enable * 功能說明: 使能L1 Cache * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void CPU_CACHE_Enable(void) { /* 使能 I-Cache */ SCB_EnableICache(); /* 使能 D-Cache */ SCB_EnableDCache(); }
主功能:
主程序實現如下操作:
- 啟動1個200ms的自動重裝定時器,讓LED2每200ms翻轉一次。
- 解碼一張480*272大小的JPEG圖片並顯示。
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: c程序入口 * 形 參: 無 * 返 回 值: 錯誤代碼(無需處理) ********************************************************************************************************* */ int main(void) { uint16_t ucBright; /* 背光亮度(0-255) */ FONT_T tFont; /* 定義一個字體結構體變量,用於設置字體參數 */ /* 設置字體參數 */ { tFont.FontCode = FC_ST_16; /* 字體代碼 16點陣 */ tFont.FrontColor = CL_WHITE; /* 字體顏色 */ tFont.BackColor = CL_BLUE; /* 文字背景顏色 */ tFont.Space = 0; /* 文字間距,單位 = 像素 */ } bsp_Init(); /* 硬件初始化 */ PrintfLogo(); /* 打印例程名稱和版本等信息 */ PrintfHelp(); /* 打印操作提示 */ /* 延遲200ms再點亮背光,避免瞬間高亮 */ bsp_DelayMS(200); LCD_ClrScr(CL_BLUE); /* 界面整體顯示完畢后,再打開背光,設置為缺省亮度 */ bsp_DelayMS(100); ucBright = BRIGHT_DEFAULT; LCD_SetBackLight(ucBright); /* 第1個圖:使用DMA2D刷色塊 ##############################################################*/ LCD_DispStr(24, 2, "DMA2D刷色塊", &tFont); _DMA2D_Fill((void *)(SDRAM_LCD_BUF1 + g_LcdWidth*20*2 + 24*2), /* 顯示起始地址(24, 20) */ 128, /* 色塊長 */ 128, /* 色塊高 */ g_LcdWidth-128, /* 色塊行偏移 */ CL_RED, /* 色塊顏色 */ LTDC_PIXEL_FORMAT_RGB565); /* 色塊顏色格式 */ /* 第2個圖:顯示ARGB8888位圖 ##############################################################*/ LCD_DispStr(176, 2, "刷ARGB8888位圖", &tFont); _DMA2D_DrawAlphaBitmap((void *)(SDRAM_LCD_BUF1 + g_LcdWidth*20*2 + 176*2), /* 顯示起始地址(176, 20) */ (void *)_aclufei, /* 位圖地址 */ 128, /* 位圖長 */ 128, /* 位圖高 */ 0, /* 位圖行偏移 */ g_LcdWidth-128, /* 目標區行偏移 */ LTDC_PIXEL_FORMAT_RGB565); /* 目標區顏色格式 */ /* 第3個圖:顯示RGB565位圖 ##############################################################*/ LCD_DispStr(328, 2, "刷RGB565位圖", &tFont); _DMA2D_Copy((uint32_t *)_acmickey, /* 位圖地址 */ (uint32_t *)(SDRAM_LCD_BUF1 + g_LcdWidth*20*2 + 328*2), /* 顯示起始地址(328, 20) */ 128, /* 位圖長 */ 128, /* 位圖高 */ 0, /* 位圖行偏移 */ g_LcdWidth-128, /* 目標區行偏移 */ LTDC_PIXEL_FORMAT_RGB565); /* 目標區顏色格式 */ /* 第4個圖:兩個位圖混合 ##############################################################*/ LCD_DispStr(24, 150, "兩個位圖混合", &tFont); _DMA2D_AlphaBlendingBulk((uint32_t *)_aclufei, /* 前景層位圖地址 */ 0, /* 前景層行偏移 */ (uint32_t *)_acsuolong, /* 背景層位圖地址 */ 0, /* 背景層行偏移 */ (uint32_t *)(SDRAM_LCD_BUF1 + g_LcdWidth*168*2 + 24*2), /* 顯示起始地址(24, 168) */ g_LcdWidth-128, /* 目標區行偏移 */ 128, /* 目標區長 */ 128); /* 目標區高 */ /* 第5個圖:Alpha透明度200的位圖顯示 #######################################################*/ LCD_DispStr(176, 150, "Alpha透明度200", &tFont); _DMA2D_MixColorsBulk((uint32_t *)_achuoying, /* 位圖地址 */ 0, /* 位圖行偏移 */ (uint32_t *)(SDRAM_LCD_BUF1 + g_LcdWidth*168*2 + 176*2), /* 顯示起始地址(176, 168) */ g_LcdWidth-128, /* 目標區行偏移 */ 128, /* 目標區長 */ 128, /* 目標區高 */ 200); /* 位圖顯示透明度200 */ /* 第6個圖:Alpha透明度100的位圖顯示 ####################################################*/ LCD_DispStr(328, 150, "Alpha透明度100", &tFont); _DMA2D_MixColorsBulk((uint32_t *)_achuoying, /* 位圖地址 */ 0, /* 位圖行偏移 */ (uint32_t *)(SDRAM_LCD_BUF1 + g_LcdWidth*168*2 + 328*2), /* 顯示起始地址(328, 168) */ g_LcdWidth-128, /* 目標區行偏移 */ 128, /* 目標區長 */ 128, /* 目標區高 */ 100); /* 位圖顯示透明度200 */ bsp_StartAutoTimer(0, 200); /* 啟動1個200ms的自動重裝的定時器,軟件定時器0 */ while (1) { bsp_Idle(); /* 判斷軟件定時器0是否超時 */ if(bsp_CheckTimer(0)) { /* 每隔200ms 進來一次 */ bsp_LedToggle(2); } } } /* ********************************************************************************************************* * 函 數 名: main * 功能說明: c程序入口 * 形 參: 無 * 返 回 值: 錯誤代碼(無需處理) ********************************************************************************************************* */ int main(void) { bsp_Init(); /* 硬件初始化 */ PrintfLogo(); /* 打印例程名稱和版本等信息 */ /* 延遲200ms再點亮背光,避免瞬間高亮 */ bsp_DelayMS(200); LCD_ClrScr(CL_BLUE); /* 界面整體顯示完畢后,再打開背光,設置為缺省亮度 */ bsp_DelayMS(100); LCD_SetBackLight(BRIGHT_DEFAULT); TestJpeg(); /* JPEG測試 */ } /* ********************************************************************************************************* * 函 數 名: TestJpeg * 功能說明: 硬件JPEG測試 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void TestJpeg(void) { int iTimeStart, iTimeEnd; FONT_T tFont; /* 定義一個字體結構體變量,用於設置字體參數 */ char buf0[100]; char buf1[100]; /* 設置字體屬性 */ tFont.FontCode = FC_ST_16; /* 字體選擇宋體16點陣,高16x寬15) */ tFont.FrontColor = CL_RED; /* 字體顏色設置為紅色 */ tFont.BackColor = CL_MASK; /* 文字背景顏色,透明 */ tFont.Space = 0; /* 字符水平間距, 單位 = 像素 */ /* 第1步:JPEG初始化 ###########################################*/ JPEG_Handle.Instance = JPEG; HAL_JPEG_Init(&JPEG_Handle); iTimeStart = bsp_GetRunTime(); JPEG_Decode_DMA(&JPEG_Handle, (uint32_t)_ac1, sizeof(_ac1) , SDRAM_APP_BUF); /* 第2步:等待JPEG解碼完成 ###########################################*/ while(Jpeg_HWDecodingEnd == 0){} iTimeEnd = bsp_GetRunTime(); sprintf(buf0, "STM32H7硬件JPEG解碼480*272圖片時間=%dms", iTimeEnd- iTimeStart); /* 第3步:獲取JPEG圖片信息###########################################*/ HAL_JPEG_GetInfo(&JPEG_Handle, &JPEG_Info); /* 第4步:繪制JPEG圖片到顯示屏###########################################*/ iTimeStart = bsp_GetRunTime(); DMA2D_Copy_YCbCr_To_RGB((uint32_t *)SDRAM_APP_BUF, /* JEPG解碼后的數據 */ (uint32_t *)SDRAM_LCD_BUF1, /* 這里是顯存地址 */ 0 , 0, JPEG_Info.ImageWidth, JPEG_Info.ImageHeight, LTDC_PIXEL_FORMAT_RGB565, JPEG_Info.ChromaSubsampling); iTimeEnd = bsp_GetRunTime(); LCD_DispStr(0, 0, buf0, &tFont); sprintf(buf1, "STM32H7硬件JPEG顯示480*272圖片時間=%dms", iTimeEnd- iTimeStart); LCD_DispStr(0, 18, buf1, &tFont); bsp_StartAutoTimer(0, 200); /* 啟動1個200ms的自動重裝的定時器,軟件定時器0 */ /* 進入主程序循環體 */ while (1) { bsp_Idle(); /* 判斷軟件定時器0是否超時 */ if(bsp_CheckTimer(0)) { /* 每隔200ms 進來一次 */ bsp_LedToggle(2); } } }
56.8 實驗例程說明(IAR)
配套例子:
V7-035_DMA2D功能測試(顯示色塊,位圖,Alpha混合和圖片混合等)
實驗目的:
- 學習DMA2D顯示色塊,位圖,Alpha混合和圖片混合等。
實驗內容:
- 啟動1個200ms的自動重裝定時器,讓LED2每200ms翻轉一次。
- 第1個圖:使用DMA2D刷色塊。
- 第2個圖:顯示ARGB8888位圖。
- 第3個圖:顯示RGB565位圖。
- 第4個圖:兩個位圖混合。
- 第5個圖:Alpha透明度200的位圖顯示。
- 第6個圖:Alpha透明度100的位圖顯示。
LCD界面顯示效果如下:
上電后串口打印的信息:
波特率 115200,數據位 8,奇偶校驗位無,停止位 1

程序設計:
系統棧大小分配:
RAM空間用的DTCM:
硬件外設初始化
硬件外設的初始化是在 bsp.c 文件實現:
/* ********************************************************************************************************* * 函 數 名: bsp_Init * 功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_Init(void) { /* 配置MPU */ MPU_Config(); /* 使能L1 Cache */ CPU_CACHE_Enable(); /* STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鍾: - 調用函數HAL_InitTick,初始化滴答時鍾中斷1ms。 - 設置NVIV優先級分組為4。 */ HAL_Init(); /* 配置系統時鍾到400MHz - 切換使用HSE。 - 此函數會更新全局變量SystemCoreClock,並重新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用於代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。 - 默認不開啟,如果要使能此選項,務必看V7開發板用戶手冊第8章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder並開啟 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart(); #endif bsp_InitKey(); /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */ bsp_InitTimer(); /* 初始化滴答定時器 */ bsp_InitUart(); /* 初始化串口 */ bsp_InitExtIO(); /* 初始化FMC總線74HC574擴展IO. 必須在 bsp_InitLed()前執行 */ bsp_InitLed(); /* 初始化LED */ bsp_InitI2C(); /* 初始化I2C總線 */ TOUCH_InitHard(); /* 初始化觸摸芯片,LCD面板型號的檢查也在此函數,所以要在函數LCD_InitHard前調用 */ LCD_InitHard(); /* 初始化LCD */ }
MPU配置和Cache配置:
數據Cache和指令Cache都開啟。配置了AXI SRAM區(本例子未用到AXI SRAM),FMC的擴展IO區和SDRAM。由於SDRAM要用於LCD的顯存,方便起見,直接將其配置為WT模式。
/* ********************************************************************************************************* * 函 數 名: MPU_Config * 功能說明: 配置MPU * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void MPU_Config( void ) { MPU_Region_InitTypeDef MPU_InitStruct; /* 禁止 MPU */ HAL_MPU_Disable(); /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x24000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /* 配置FMC擴展IO的MPU屬性為Device或者Strongly Ordered */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x60000000; MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER1; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /* 配置SDRAM的MPU屬性為Write through, read allocate,no write allocate */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0xC0000000; MPU_InitStruct.Size = MPU_REGION_SIZE_32MB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER2; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /*使能 MPU */ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); } /* ********************************************************************************************************* * 函 數 名: CPU_CACHE_Enable * 功能說明: 使能L1 Cache * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void CPU_CACHE_Enable(void) { /* 使能 I-Cache */ SCB_EnableICache(); /* 使能 D-Cache */ SCB_EnableDCache(); }
主功能:
主程序實現如下操作:
- 啟動1個200ms的自動重裝定時器,讓LED2每200ms翻轉一次。
- LCD第1個圖:使用DMA2D刷色塊。
- LCD第2個圖:顯示ARGB8888位圖。
- LCD第3個圖:顯示RGB565位圖。
- LCD第4個圖:兩個位圖混合。
- LCD第5個圖:Alpha透明度200的位圖顯示。
- LCD第6個圖:Alpha透明度100的位圖顯示。
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: c程序入口 * 形 參: 無 * 返 回 值: 錯誤代碼(無需處理) ********************************************************************************************************* */ int main(void) { uint16_t ucBright; /* 背光亮度(0-255) */ FONT_T tFont; /* 定義一個字體結構體變量,用於設置字體參數 */ /* 設置字體參數 */ { tFont.FontCode = FC_ST_16; /* 字體代碼 16點陣 */ tFont.FrontColor = CL_WHITE; /* 字體顏色 */ tFont.BackColor = CL_BLUE; /* 文字背景顏色 */ tFont.Space = 0; /* 文字間距,單位 = 像素 */ } bsp_Init(); /* 硬件初始化 */ PrintfLogo(); /* 打印例程名稱和版本等信息 */ PrintfHelp(); /* 打印操作提示 */ /* 延遲200ms再點亮背光,避免瞬間高亮 */ bsp_DelayMS(200); LCD_ClrScr(CL_BLUE); /* 界面整體顯示完畢后,再打開背光,設置為缺省亮度 */ bsp_DelayMS(100); ucBright = BRIGHT_DEFAULT; LCD_SetBackLight(ucBright); /* 第1個圖:使用DMA2D刷色塊 ##############################################################*/ LCD_DispStr(24, 2, "DMA2D刷色塊", &tFont); _DMA2D_Fill((void *)(SDRAM_LCD_BUF1 + g_LcdWidth*20*2 + 24*2), /* 顯示起始地址(24, 20) */ 128, /* 色塊長 */ 128, /* 色塊高 */ g_LcdWidth-128, /* 色塊行偏移 */ CL_RED, /* 色塊顏色 */ LTDC_PIXEL_FORMAT_RGB565); /* 色塊顏色格式 */ /* 第2個圖:顯示ARGB8888位圖 ##############################################################*/ LCD_DispStr(176, 2, "刷ARGB8888位圖", &tFont); _DMA2D_DrawAlphaBitmap((void *)(SDRAM_LCD_BUF1 + g_LcdWidth*20*2 + 176*2), /* 顯示起始地址(176, 20) */ (void *)_aclufei, /* 位圖地址 */ 128, /* 位圖長 */ 128, /* 位圖高 */ 0, /* 位圖行偏移 */ g_LcdWidth-128, /* 目標區行偏移 */ LTDC_PIXEL_FORMAT_RGB565); /* 目標區顏色格式 */ /* 第3個圖:顯示RGB565位圖 ##############################################################*/ LCD_DispStr(328, 2, "刷RGB565位圖", &tFont); _DMA2D_Copy((uint32_t *)_acmickey, /* 位圖地址 */ (uint32_t *)(SDRAM_LCD_BUF1 + g_LcdWidth*20*2 + 328*2), /* 顯示起始地址(328, 20) */ 128, /* 位圖長 */ 128, /* 位圖高 */ 0, /* 位圖行偏移 */ g_LcdWidth-128, /* 目標區行偏移 */ LTDC_PIXEL_FORMAT_RGB565); /* 目標區顏色格式 */ /* 第4個圖:兩個位圖混合 ##############################################################*/ LCD_DispStr(24, 150, "兩個位圖混合", &tFont); _DMA2D_AlphaBlendingBulk((uint32_t *)_aclufei, /* 前景層位圖地址 */ 0, /* 前景層行偏移 */ (uint32_t *)_acsuolong, /* 背景層位圖地址 */ 0, /* 背景層行偏移 */ (uint32_t *)(SDRAM_LCD_BUF1 + g_LcdWidth*168*2 + 24*2), /* 顯示起始地址(24, 168) */ g_LcdWidth-128, /* 目標區行偏移 */ 128, /* 目標區長 */ 128); /* 目標區高 */ /* 第5個圖:Alpha透明度200的位圖顯示 #######################################################*/ LCD_DispStr(176, 150, "Alpha透明度200", &tFont); _DMA2D_MixColorsBulk((uint32_t *)_achuoying, /* 位圖地址 */ 0, /* 位圖行偏移 */ (uint32_t *)(SDRAM_LCD_BUF1 + g_LcdWidth*168*2 + 176*2), /* 顯示起始地址(176, 168) */ g_LcdWidth-128, /* 目標區行偏移 */ 128, /* 目標區長 */ 128, /* 目標區高 */ 200); /* 位圖顯示透明度200 */ /* 第6個圖:Alpha透明度100的位圖顯示 ####################################################*/ LCD_DispStr(328, 150, "Alpha透明度100", &tFont); _DMA2D_MixColorsBulk((uint32_t *)_achuoying, /* 位圖地址 */ 0, /* 位圖行偏移 */ (uint32_t *)(SDRAM_LCD_BUF1 + g_LcdWidth*168*2 + 328*2), /* 顯示起始地址(328, 168) */ g_LcdWidth-128, /* 目標區行偏移 */ 128, /* 目標區長 */ 128, /* 目標區高 */ 100); /* 位圖顯示透明度200 */ bsp_StartAutoTimer(0, 200); /* 啟動1個200ms的自動重裝的定時器,軟件定時器0 */ while (1) { bsp_Idle(); /* 判斷軟件定時器0是否超時 */ if(bsp_CheckTimer(0)) { /* 每隔200ms 進來一次 */ bsp_LedToggle(2); } } }
56.9 總結
本章節涉及到的知識點非常重要,望初學者務必熟練掌握,因為后面章節用到LCD加速的地方都要用到DMA2D。
