【第3版emWin教程】第21章 emWin6.x的BMP图片显示

2021-07-08 15:45:44 浏览数 (1)

教程不断更新中:http://www.armbbs.cn/forum.php?mod=viewthread&tid=98429

第21章 emWin6.x的BMP图片显示

本章主要讲emWin支持的BMP图片显示,官方支持的主要有两种显示方法,一种是从外部存储器读取数据到内部存储器(比如内部SRAM,外部SRAM或者SDRAM)然后来显示图片,这种方式的显示速度要快些,另一种方法是直接从外部存储器读取数据并显示,这种方法的好处就是不需要大的内存空间,每读取一部分数据就显示一次,缺点就是显示速度比较慢。

学习本章节前,请务必学习第20章存储设备之基本函数。

21.1 初学者重要提示

21.2 BMP图片基础知识

21.3 BMP图片的API函数及其显示方法

21.4 实验例程说明(RTOS)

21.5 实验例程说明(裸机)

21.6 总结

21.1 初学者重要提示

1、 实际项目中强烈建议将BMP图片加载到emWin动态内存并解码到存储设备里面再显示,性能相当给力,测试V7开发板,H7 32位SDRAM,LTDC颜色格式配置为RGB565,刷新800*480分辨率图片可以达到8-11ms一帧,基本满足大部分嵌入式GUI项目。本章教程配套的例子就是采用这种方式。

2、 BMP图片显示的所有API函数在emWin手册中都有讲解,下图是中文版手册里面API函数的位置

下图是英文版手册里面API函数的位置:

3、 本章教程使用的外部存储器是SD卡,实际项目中使用任何其它类型的存储器都可以的,支不支持文件系统都没有关系,使用方法与本章教程一样,用户要做的就是把图片从外部存储器读出即可。

21.2 BMP图片基础知识

关于BMP图片格式方面的知识,推荐大家看wiki百科上面的介绍:

  • https://en.wikipedia.org/wiki/BMP_file_format 讲解非常详细。
  • 如果觉得英文版读起来比较吃力些,可以看wiki中文版,只是资料没有英文的详细:https://zh.wikipedia.org/wiki/BMP 。
  • 更多BMP文件的知识可以google或者百度进行了解。

推荐初学者了解一下BMP文件的格式,如果没有了解也是没有任何关系的,直接调用emWin的API

函数就可以显示BMP图片了。BMP图片的优势就是无损的,真实反映图像效果,缺点是图片比较大。

21.3 BMP图片的API函数及其显示方法

当前emWin支持的API函数有如下11个:

从上面的表格中可以看出,emWin支持BMP文件显示主要有两种类型的函数,一类是以Ex结尾的函数,这种函数显示BMP图片是一边从外部存储器加载数据一边显示,显示速度相对较慢,适用于内存较小的场合。另一类是不以Ex结尾的函数,这种函数直接从指定的地址读取数据进行显示(注意,这里的地址需是总线式地址,比如外部SDRAM,外部SRAM,内部Flash和内部SRAM都可以),显示速度相对较快。

本章教程会对这两种方式都进行说明:

  • int GUI_BMP_Draw(const void * pFileData, int x0, int y0)

此函数直接从地址pFileData读取BMP文件数据,将图片显示到用户设置的位置(x0, y0)。

  • int GUI_BMP_DrawEx(GUI_GET_DATA_FUNC * pfGetData, void * p, int x0, int y0)

此函数通过其回调函数pfGetData实现边读取图片数据边显示的功能,将图片显示到用户设置的位置(x0, y0)。

21.3.1 绘制已经加载到存储器的BMP图片

绘制加载到存储器的BMP图片主要是通过函数GUI_BMP_Draw来实现,下面我们分3步来说明如何将SD卡中的BMP图片显示到LCD上面。

  • 第1步:将BMP图片复制到SD卡的根目录下,然后通过emWin的动态内存管理函数申请动态内存并将BMP文件加载进来, 这里我们用的是外部SDRAM做emWin的动态内存。
代码语言:javascript复制
char *_acBuffer;
    GUI_HMEM hMem;

    /* 打开文件 */        
    result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS);
    if (result != FR_OK)
    {
        return 0;
    }
     
    /* 申请一块内存空间 并且将其清零 */
    hMem = GUI_ALLOC_AllocZero(file.obj.objsize);
    
    /* 将申请到内存的句柄转换成指针类型 */
    _acBuffer = GUI_ALLOC_h2p(hMem);

    /* 读取文件到动态内存 */
    result = f_read(&file, _acBuffer, file.obj.objsize, &bw);
    if (result != FR_OK)
    {
        return 0;
    }
  • 第2步:将加载到emWin动态内存的BMP图片绘制到内存设备里面,关于内存设备,我们在上个章节已经专门讲解了。绘制到内存设备后,再调用内存设备的API函数绘制此BMP图片,此时的绘制速度将大大加快。然后结合第1步,完整的代码如下:
代码语言:javascript复制
/*
*********************************************************************************************************
*    函 数 名: _ShowBMP2
*    功能说明: 显示BMP图片
*    形    参: sFilename  要读取的文件名
*                     x  要显示的x轴坐标位置
*                     y  要显示的y轴坐标位置
*    返 回 值: 返回绘制了BMP图片的内存设备句柄。
*********************************************************************************************************
*/
GUI_HMEM _ShowBMP2(const char *sFilename, int x, int y) 
{
    char *_acBuffer;
    int XSize, YSize;
    GUI_HMEM hMem;
    GUI_MEMDEV_Handle hMemBMP;

    /* 打开文件 */        
    result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS);
    if (result != FR_OK)
    {
        return 0;
    }
     
    /* 申请一块内存空间 并且将其清零 */
    hMem = GUI_ALLOC_AllocZero(file.obj.objsize);
    
    /* 将申请到内存的句柄转换成指针类型 */
    _acBuffer = GUI_ALLOC_h2p(hMem);

    /* 读取文件到动态内存 */
    result = f_read(&file, _acBuffer, file.obj.objsize, &bw);
    if (result != FR_OK)
    {
        return 0;
    }
    
    XSize = GUI_BMP_GetXSize(_acBuffer);
    YSize = GUI_BMP_GetYSize(_acBuffer);
    
    /* 创建内存设备,并将BMP图片绘制到此内存设备里面,此内存设备要在主程序中用到
       所以退出此函数前,不要释放。
    */
    hMemBMP = GUI_MEMDEV_CreateEx(0, 0, XSize, YSize, GUI_MEMDEV_HASTRANS);
    GUI_MEMDEV_Select(hMemBMP);
    GUI_BMP_Draw(_acBuffer, 0, 0);
    GUI_MEMDEV_Select(0);

    /* 释放动态内存hMem */
    GUI_ALLOC_Free(hMem);
    
    /* 关闭文件 */
    f_close(&file);
    
    return hMemBMP;
}
  • 第3步:通过函数GUI_MEMDEV_WriteAt绘制内存设备中的图片,比如我们要绘制的图片1.bmp文件已经存储到了SD卡根目录下,显示方法如下:
代码语言:javascript复制
    GUI_MEMDEV_Handle hMemBMP;
            
    
    /* 加载BMP图片到内存设备 */
    hMemBMP = _ShowBMP2("1.bmp", 0, 0);

    /* 用到BMP图片的时候,调用此函数即可 */
GUI_MEMDEV_WriteAt(hMemBMP, 0, 0);

通过上面三步就完成了BMP图片的绘制操作,这种方式绘制BMP图片速度非常快,后面有用到此BMP图片的地方调用函数GUI_MEMDEV_WriteAt即可。实际显示效果参看本章节配套的实验例程说明。

21.3.2 绘制无需加载到存储器的BMP图片

绘制无需加载到存储器的BMP图片主要是通过函数GUI_BMP_DrawEx来实现,这种方式的优点是需要的内存小,但是显示速度很慢,用于STM32H7系列不实用,实际项目中不推荐,用户知道怎么使用即可。下面我们分2步来说明如何将SD卡中的BMP图片显示到LCD上面。

  • 第1步:将BMP图片复制到SD卡的根目录下,然后直接调用函数GUI_BMP_DrawEx就可以显示。
代码语言:javascript复制
/*
*********************************************************************************************************
*    函 数 名: _GetData
*    功能说明: 被函数GUI_BMP_DrawEx调用
*    形    参:p             FIL类型数据
*             NumBytesReq   请求读取的字节数
*             ppData        数据指针
*             Off           如果Off = 1,那么将重新从起始位置读取                 
*    返 回 值: 返回读取的字节数
*********************************************************************************************************
*/
int _GetData(void * p, const U8 ** ppData, unsigned NumBytesReq, U32 Off) 
{
    static int FileAddress = 0;
    UINT NumBytesRead;
    FIL *PicFile;

    PicFile = (FIL *)p;

    /*
    * 检测缓存大小
    */
    if (NumBytesReq > sizeof(acBuffer)) {
    NumBytesReq = sizeof(acBuffer);
    }

    /*
    * 设置读取位置
    */
    if(Off == 1) FileAddress = 0;
    else FileAddress = Off;
    result =f_lseek(PicFile, FileAddress);

    /*
    * 读取数据到缓存
    */
    result = f_read(PicFile, acBuffer, NumBytesReq, &NumBytesRead);

    /*
    * 让指针ppData指向读取的数据
    */
    *ppData = (const U8 *)acBuffer;

    /*
    * 返回读取的字节数
    */
    return NumBytesRead;
}

/*
*********************************************************************************************************
*    函 数 名: _ShowBMP1
*    功能说明: 显示BMP图片
*    形    参: sFilename  要读取的文件名
*    返 回 值: 无
*********************************************************************************************************
*/
void _ShowBMP1(const char *sFilename) 
{
    /* 打开文件 */        
    result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS);
    if (result != FR_OK)
    {
        return;
    }
    
    /* 绘制BMP图片 */
    GUI_BMP_DrawEx(_GetData, &file, 0, 0);
    
    /* 关闭文件 */
    f_close(&file);
}
  • 第2步:用户要显示指定的文件1.bmp,调用函数_ShowBMP1("1.bmp")即可显示。

通过上面2步就完成了BMP图片的绘制操作,这种方式绘制BMP图片速度比较慢,项目应用中不推荐这种方式。实际显示效果参看本章节配套的实验例程说明。

21.4 实验例程说明(RTOS)

配套例子:

V7-522_emWin6.x实验_BMP图片显示(RTOS)

实验目的:

  1. 学习emWin的BMP图片显示。
  2. emWin功能的实现在MainTask.c文件里面。

实验注意:

  1. 本实验主要学习emWin的BMP图片显示功能,实验所需的图片文件1.bmp已经存储到本工程的Doc文件夹下,使用此例子前,请务必将此文件存储到SD卡根目录中,并将SD卡插到开发板上面。

实验内容:

1、K1按键按下,串口或者RTT打印任务执行情况(串口波特率115200,数据位8,奇偶校验位无,停止位1)。

2、(1) 凡是用到printf函数的全部通过函数App_Printf实现。

(2) App_Printf函数做了信号量的互斥操作,解决资源共享问题。

3、默认上电是通过串口打印信息,如果使用RTT打印信息:

MDK AC5,MDK AC6或IAR通过使能bsp.h文件中的宏定义为1即可

#define Enable_RTTViewer 1

4、各个任务实现的功能如下:

App Task Start 任务 :启动任务,这里用作BSP驱动包处理。

App Task MspPro任务 :消息处理,这里用作LED闪烁。

App Task UserIF 任务 :按键消息处理。

App Task COM 任务 :暂未使用。

App Task GUI 任务 :GUI任务。

μCOS-III任务调试信息(按K1按键,串口打印):

RTT 打印信息方式:

程序设计:

任务栈大小分配:

μCOS-III任务栈大小在app_cfg.h文件中配置:

#define APP_CFG_TASK_START_STK_SIZE 512u

#define APP_CFG_TASK_MsgPro_STK_SIZE 2048u

#define APP_CFG_TASK_COM_STK_SIZE 512u

#define APP_CFG_TASK_USER_IF_STK_SIZE 512u

#define APP_CFG_TASK_GUI_STK_SIZE 2048u

任务栈大小的单位是4字节,那么每个任务的栈大小如下:

App Task Start 任务 :2048字节。

App Task MspPro任务 :8192字节。

App Task UserIF 任务 :2048字节。

App Task COM 任务 :2048字节。

App Task GUI 任务 :8192字节。

系统栈大小分配:

μCOS-III的系统栈大小在os_cfg_app.h文件中配置:

#define OS_CFG_ISR_STK_SIZE 512u

系统栈大小的单位是4字节,那么这里就是配置系统栈大小为2KB

emWin动态内存配置:

GUIConf.c文件中的配置如下:

代码语言:javascript复制
#define EX_SRAM   1/*1 used extern sram, 0 used internal sram */

#if EX_SRAM
#define GUI_NUMBYTES  (1024*1024*24)
#else
#define GUI_NUMBYTES  (100*1024)
#endif

通过宏定义来配置使用内部SRAM还是外部的SDRAM做为emWin的动态内存,当配置:

#define EX_SRAM 1 表示使用外部SDRAM作为emWin动态内存,大小24MB。

#define EX_SRAM 0 表示使用内部SRAM作为emWin动态内存,大小100KB。

默认情况下,本教程配套的所有emWin例子都是用外部SDRAM作为emWin动态内存。

emWin界面显示效果:

800*480分辨率界面效果。

21.5 实验例程说明(裸机)

配套例子:

V7-521_emWin6.x实验_BMP图片显示(裸机)

实验目的:

  1. 学习emWin的BMP图片显示。
  2. emWin功能的实现在MainTask.c文件里面。

实验注意:

  1. 本实验主要学习emWin的BMP图片显示功能,实验所需的图片文件1.bmp已经存储到本工程的Doc文件夹下,使用此例子前,请务必将此文件存储到SD卡根目录中,并将SD卡插到开发板上面。

emWin界面显示效果:

800*480分辨率界面效果。

emWin动态内存配置:

GUIConf.c文件中的配置如下:

代码语言:javascript复制
#define EX_SRAM   1/*1 used extern sram, 0 used internal sram */

#if EX_SRAM
#define GUI_NUMBYTES  (1024*1024*24)
#else
#define GUI_NUMBYTES  (100*1024)
#endif

通过宏定义来配置使用内部SRAM还是外部的SDRAM做为emWin的动态内存,当配置:

#define EX_SRAM 1 表示使用外部SDRAM作为emWin动态内存,大小24MB。

#define EX_SRAM 0 表示使用内部SRAM作为emWin动态内存,大小100KB。

默认情况下,本教程配套的所有emWin例子都是用外部SDRAM作为emWin动态内存。

21.6 总结

总的来说,STM32H7 32位SDRAM绘制BMP图片的性能已经比较给力,实际项目中推荐将BMP图片加载到emWin动态内存,然后绘制到内存设备中,再通过内存设备函数显示此BMP图片的速度非常快,推荐项目中使用。

0 人点赞