第6章 ThreadX GUIX上手之STM32F429 DMA2D加速
本章节为大家讲解DMA2D应用中经常用到的刷色块,刷位图,Alpha混合和图片混合的实现。
6.1 初学者重要提示
6.2 DMA2D驱动设计
6.3 制作C文件格式的位图
6.4 DMA2D常用操作(重要)
6.5 DMA2D驱动移植和使用
6.6 实验例程设计框架
6.7 实验例程说明
6.8 总结
6.1 初学者重要提示
- 本章是为ThreadX GUIX的LCD DMA2D加速做准备。
- DMA2D里面有一个重要的概念就是行偏移,这知识点务必要认识到位,详见本章2.2小节。
- DMA2D可以直接绘制ARGB8888,RGB565颜色格式位图,并且可以方便的做各种透明效果和图像混合显示。
- LCD的加速全靠DMA2D,所以务必要熟练掌握其用法。
- 屏蔽MDK AC6使用中文GBK编码的警告方法,让大家可以继续使用GBK编码汉字:http://www.armbbs.cn/forum.php?mod=viewthread&tid=98670 。
6.2 DMA2D驱动设计
6.2.1 DMA2D驱动设计思路
DMA2D的驱动设计比较省事:
- 用户仅需调用函数__HAL_RCC_DMA2D_CLK_ENABLE使能DMA2D即可使用。
- 默认DMA2D的API都是采用阻塞式,这种方式在使用RTOS的时候比较方便,用户可以将GUI任务的优先级设置的仅比空闲任务高即可,这样有高优先级任务要执行,可以及时切换到高优先级任务里,GUI任务等待DMA2D执行完成即可。从而可以充分利用DMA2D和CPU,使芯片性能得到最大发挥。
- 最重要的一条,所有的DMA2D操作,直接采用寄存器方式,不再使用HAL库给的API,让性能得到最大发挥。
6.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的数据。
同理,背景层和输出区的行偏移也是这个意思。
6.3 制作C文件格式的位图
由于DMA2D刷新图片要用到,所以本小节为大家介绍下位图的制作。
6.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通道。
6.3.2 图标下载
这里为大家推荐一个图标下载网址:http://www.easyicon.net/ ,此网站非常好用,需要什么图标直接检索关键字就可以了。这里以关键字cartoon进行检索,最终选择地址:
https://www.easyicon.net/581941-Lufy_cartoon_icon.html里面的图标,我们下载128*128点阵大小的PNG图片(下载的图片已经放在了本章教程配套例子的Doc文件夹里面):
下面我们分两步走,分别将其转换为ARGB8888格式位图和RGB565格式位图。
6.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文件,代码如下:
对于生成的代码仅需用到数组static GUI_CONST_STORAGE unsigned long _aclufy[],这个数值要稍微修改下,将GUI_CONST_STORAGE修改为const即可用到工程里面。const表示数组存储到flash里面。
6.3.4 转换PNG图片为RGB565格式位图
转换方法与56.4.1小节相似,主要下面两个地方不同:
- 第1点不同:使用电脑端的画图小软件将前面下载的图标转换为BMP格式(PNG图片中的透明通道会滤被掉),再用BmpCvt软件打开后的效果如下,已经没有Alpha通道。
- 第2点不同,转换格式选择如下:
转换后生成的代码如下:
这里生成的代码也是仅需用到数组static GUI_CONST_STORAGE unsigned long _aclufy[],并将GUI_CONST_STORAGE修改为const即可用到工程里面。
6.4 DMA2D常用操作(重要)
DMA2D的常用API要熟练掌握,后面的GUI的底层驱动加速,摄像头等部分都要用到。这里为大家介绍如下几个常用API:
- _DMA2D_Fill
- _DMA2D_Copy
- _DMA2D_MixColorsBulk
- _DMA2D_AlphaBlendingBulk
- _DMA2D_DrawAlphaBitmap
6.4.1 函数_DMA2D_Fill
函数原型:
函数描述:
此函数主要用于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行,根据设置的起始坐标,计算起始坐标在LCD显存中的具体位置。
SDRAM_LCD_BUF1表示LCD显存首地址。
g_LcdWidth*20*2计算的是行数占用的字节数。
24*2计算的是所在行的具体地址。
乘以2是因为RGB565颜色格式的1个像素占用两个字节。
- 第4行,输出行偏移的意思就是一行结束到下一行开始的距离,单位像素个数。由于填充区的长度是128,那么行偏移就是LCD的长度减去128。
6.4.2 函数_DMA2D_Copy
函数原型:
函数描述:
此函数用于从前景层复制指定区域的颜色数据到目标区域。
- 第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行是位图首地址。
- 第2行,根据设置的起始坐标,计算起始坐标在LCD显存中的具体位置。
SDRAM_LCD_BUF1表示LCD显存首地址。
g_LcdWidth*20*2计算的是行数占用的字节数。
328*2计算的是所在行的具体地址。
乘以2是因为RGB565颜色格式的1个像素占用两个字节。
- 第5行是位图的行偏移,行偏移的意思就是一行结束到下一行开始的距离,单位像素个数。由于整个位图都要绘制,所有行偏移就是0。
- 第6行,输出行偏移的,由于输出区需要的长度是128,那么行偏移就是LCD的长度减去128。
6.4.3 函数_DMA2D_MixColorsBulk
函数原型:
函数描述:
此函数用于前景层和目标区域的颜色混合,并将混合后的图像输出到目标区域。
- 第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行是位图首地址。
- 第2行是位图的行偏移,行偏移的意思就是一行结束到下一行开始的距离,单位像素个数。由于整个位图都要绘制,所有行偏移就是0。
- 第3行,根据设置的起始坐标,计算起始坐标在LCD显存中的具体位置。
SDRAM_LCD_BUF1表示LCD显存首地址。
g_LcdWidth*168*2计算的是行数占用的字节数。
176*2计算的是所在行的具体地址。
乘以2是因为RGB565颜色格式的1个像素占用两个字节。
- 第4行,输出行偏移的,由于输出区需要的长度是128,那么行偏移就是LCD的长度减去128。
6.4.4 函数_DMA2D_AlphaBlendingBulk
函数原型:
函数描述:
此函数用于前景层和背景层的颜色混合,并将混合后的图像输出到目标区域。
- 第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行是前景层位图首地址。
- 第2行是位图的行偏移,行偏移的意思就是一行结束到下一行开始的距离,单位像素个数。由于整个位图都要绘制,所有行偏移就是0。
- 第3行是背景层位图首地址。
- 第4行是背景层行偏移。
- 第5行,根据设置的起始坐标,计算起始坐标在LCD显存中的具体位置。
SDRAM_LCD_BUF1表示LCD显存首地址。
g_LcdWidth*168*2计算的是行数占用的字节数。
24*2计算的是所在行的具体地址。
乘以2是因为RGB565颜色格式的1个像素占用两个字节。
- 第6行,输出行偏移的,由于输出区需要的长度是128,那么行偏移就是LCD的长度减去128。
6.4.5 函数_DMA2D_DrawAlphaBitmap
函数原型:
函数描述:
此函数用于在指定位置显示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行,根据设置的起始坐标,计算起始坐标在LCD显存中的具体位置。
SDRAM_LCD_BUF1表示LCD显存首地址。
g_LcdWidth*20*2计算的是行数占用的字节数。
176*2计算的是所在行的具体地址。
乘以2是因为RGB565颜色格式的1个像素占用两个字节
- 第2行是位图首地址。
- 第5行是位图的行偏移,行偏移的意思就是一行结束到下一行开始的距离,单位像素个数。由于整个位图都要绘制,所有行偏移就是0。
- 第6行,输出行偏移的,由于输出区需要的长度是128,那么行偏移就是LCD的长度减去128。
6.5 DMA2D驱动移植和使用
DMA2D的驱动移植比较简单,用户仅需调用函数__HAL_RCC_DMA2D_CLK_ENABLE使能时钟,然后用到哪个函数直接复到工程里面调用即可。
6.6 实验例程设计框架
通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:
第1阶段,上电启动阶段:
- 这部分在第14章进行了详细说明。
第2阶段,进入main函数:
- 第1步,硬件初始化,主要是HAL库,系统时钟,滴答定时器,LED ,LCD,和SDRAM。
- 第2步,DMA2D应用程序设计部分,主要展示DMA2D刷色块,刷位图,Alpha混合和图片混合功能。
6.7 实验例程说明
配套例子:
V6-2003_DMA2D
实验目的:
- 学习DMA2D显示色块,位图,Alpha混合和图片混合等。
实验内容:
- 启动1个200ms的自动重装定时器,让LED2每200ms翻转一次。
- 第1个图:使用DMA2D刷色块。
- 第2个图:显示ARGB8888位图。
- 第3个图:显示RGB565位图。
- 第4个图:两个位图混合。
- 第5个图:Alpha透明度200的位图显示。
- 第6个图:Alpha透明度100的位图显示。
LCD界面显示效果如下:
6.8 总结
本章节涉及到的知识点非常重要,望初学者务必熟练掌握,因为后面章节用到LCD加速的地方都要用到DMA2D。
微信公众号:armfly_com