专栏首页安富莱嵌入式技术分享【STM32H7教程】第56章 STM32H7的DMA2D应用之刷色块,位图和Alpha混合

【STM32H7教程】第56章 STM32H7的DMA2D应用之刷色块,位图和Alpha混合

完整教程下载地址: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 初学者重要提示

  1.   学习本章节前,务必优先学习第55章,需要对DMA2D的基础知识有个认识。
  2.   DMA2D里面有一个重要的概念就是行偏移,这知识点务必要认识到位,详见本章2.2小节。
  3.   DMA2D可以直接绘制ARGB8888,RGB565颜色格式位图,并且可以方便的做各种透明效果和图像混合显示。
  4.   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行,启动传输,并等待传输完成。

注意事项:

  1. 使用此函数前务必要调用函数__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行,启动传输,并等待传输完成。

注意事项:

  1. 使用此函数前务必要调用函数__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行,启动传输,并等待传输完成。

注意事项:

  1. 使用此函数前务必要调用函数__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行,启动传输,并等待传输完成。

注意事项:

  1. 使用此函数前务必要调用函数__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行,启动传输,并等待传输完成。

注意事项:

  1. 使用此函数前务必要调用函数__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混合和图片混合等)

实验目的:

  1. 学习DMA2D显示色块,位图,Alpha混合和图片混合等。

实验内容:

  1. 启动1个200ms的自动重装定时器,让LED2每200ms翻转一次。
  2. 第1个图:使用DMA2D刷色块。
  3. 第2个图:显示ARGB8888位图。
  4. 第3个图:显示RGB565位图。
  5. 第4个图:两个位图混合。
  6. 第5个图:Alpha透明度200的位图显示。
  7. 第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混合和图片混合等)

实验目的:

  1. 学习DMA2D显示色块,位图,Alpha混合和图片混合等。

实验内容:

  1. 启动1个200ms的自动重装定时器,让LED2每200ms翻转一次。
  2. 第1个图:使用DMA2D刷色块。
  3. 第2个图:显示ARGB8888位图。
  4. 第3个图:显示RGB565位图。
  5. 第4个图:两个位图混合。
  6. 第5个图:Alpha透明度200的位图显示。
  7. 第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。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【STM32H7教程】第55章 STM32H7的图形加速器DMA2D的基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

    armfly
  • 【STM32H7教程】第53章 STM32H7的LTDC应用之汉字小字库和全字库制作

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

    armfly
  • 【STM32H7的DSP教程】第9章 Matlab的串口通信实现

    完整版教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=94547

    armfly
  • 使用python 爬梨视频

    刚开始学习python 勿喷 第一步 去官网下载python3^ 版本 下载链接 https://www.python.org/downlo... ...

    py3study
  • 信息量,熵,交叉熵,相对熵与代价函数

    如果有⼈告诉我们⼀个相当不可能的事件发⽣了,我们收到的信息要多于我们被告知某个很可能发⽣的事件发⽣时收到的信息。如果我们知道某件事情⼀定会发⽣,那么我们就不会接...

    llhthinker
  • SpringBoot使用CORS解决跨域请求问题

    同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。 同源策略是浏览器安全的基石。

    朝雨忆轻尘
  • uni-app请求新闻接口api,渲染新闻列表

    在网上找了一个免费的新闻api http://v.juhe.cn/toutiao/index?type=top&key=3dc86b09a2ee2477a5b...

    祈澈菇凉
  • VarGFaceNet:地平线提出轻量级、有效可变组卷积的人脸识别网络

    作者 | Mengjia Yan、Mengao Zhao、Zining Xu、Qian Zhang、Guoli Wang、Zhizhong Su

    AI科技大本营
  • 三星自动驾驶再曝进展,3辆车获加州路测许可

    李根 假装发自 一号公路 量子位 报道 | 公众号 QbitAI ? △ 三星圣何塞办公楼 虽然继承人李在镕面临牢狱之灾,但对于韩国科技巨头三星来说:生活要继...

    量子位
  • asp dotnet core 支持客户端上传文件

    新建一个 asp dotnet core 程序,创建一个新的类,用于给客户端上传文件的信息

    林德熙

扫码关注云+社区

领取腾讯云代金券