zoukankan      html  css  js  c++  java
  • 【第3版emWin教程】第24章 emWin6.x的JPEG图片显示(软件解码)

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

    第24章       emWin6.x的JPEG图片显示(软件解码)

    本期主要讲emWin支持的JPEG图片显示,官方支持的主要有两种显示方法,一种方法是直接从外部存储器读取数据并显示,这种方法的好处就是不需要大的RAM,每次读取一些数据显示一次,缺点就是显示速度比较慢。另一种是从外部存储器读取整个图片到RAM(比如内部SRAM,外部SRAM或者外部SDRAM),然后再显示图片,这种方法的显示速度要稍快些。但是由于JPEG解码比较耗时间,仅加载到RAM还是不行的,需要用户将JPEG图片解码到内存设备中,然后再调用内存设备的API函数来显示,此时的显示速度非常快。

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

    24.1 初学者重要提示

    24.2 JPEG图片基础知识

    24.3 JPEG图片的API函数及其显示方法

    24.4 实验例程说明(RTOS)

    24.5 实验例程说明(裸机)

    24.6 总结

    24.1 初学者重要提示

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

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

     

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

     

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

    24.2 JPEG图片基础知识

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

    推荐初学者了解一下JPEG文件的格式,如果没有了解也是没有任何关系的,直接调用emWin的API函数就可以显示JPEG图片了。

    ----------------------------------------------------------------------------------------------------------

    下面这点小知识还是要知道的:

    JPEG 是Joint Photographic Experts Group(联合图像专家小组)的缩写,是第一个国际图像压缩标准。JPEG图像压缩算法能够在提供良好的压缩性能的同时,具有比较好的重建质量,被广泛应用于图像、视频处理领域。由于JPEG优良的品质,使其在短短几年内获得了成功,被广泛应用于互联网和数码相机领域,网站上80%的图像都采用了JPEG压缩标准。

    这里有一点要特别的注意:出于法律原因,不得分发JPEG编码的代码。JPEG编码似乎归属于IBM、AT&T和Mitsubishi所有的专利。因此,从法律上讲,如未获得一个或多个许可,则不能使用JPEG编码。因此,emWin的API函数仅支持解码,不支持编码。

    24.3 JPEG图片的API函数及其显示方法

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

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

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

    •   int GUI_JPEG_Draw(const void * pFileData, int DataSize, int x0, int y0);

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

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

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

    另外还有一个知识点需要初学者了解,emWin解码一张JPEG图片需要多少RAM?这主要有两部分组成,JPEG解码本身需要大约33KB的RAM,外加图片的不同长度对RAM需求的影响,具体公式如下:

    大约RAM大小 = 图像的X大小* 80字节 +  33KB。

    不同长度的JPEG图片的RAM需求取决于JPEG图片压缩类型,比如下面三种压缩类型:

    JPEG图片解码所需的内存由emWin动态分配。绘制JPEG图像后,将释放整个RAM。这里举一个例子:比如要显示800*480的JPEG图片大约需要 800*80 字节+ 33KB ,即97KB的内存。

    24.3.1   绘制已经加载到存储器的JPEG图片

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

    •   第1步:将JPEG图片复制到SD卡的根目录下,然后通过emWin的动态内存管理函数申请动态内存并将JPEG文件加载进来, 这里我们用的是外部SDRAM做emWin的动态内存。
    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.fsize, &bw);
        if (result != FR_OK)
        {
            return 0;
        }
    •   第2步:将加载到emWin动态内存的JPEG图片绘制到内存设备里面,关于内存设备,我们在第20章已经专门讲解了。绘制到内存设备后,再调用内存设备的API函数绘制此JPEG图片,此时的绘制速度将大大加快。然后结合第1步,完整的代码如下:
    /*
    *********************************************************************************************************
    *    函 数 名: _ShowJPEG2
    *    功能说明: 显示JPEG图片
    *    形    参: sFilename  要读取的文件名
    *                     x  要显示的x轴坐标位置
    *                     y  要显示的y轴坐标位置
    *    返 回 值: 返回绘制了JPEG图片的内存设备句柄。
    *********************************************************************************************************
    */
    GUI_HMEM _ShowJPEG2(const char *sFilename, int x, int y) 
    {
        char *_acBuffer;
        GUI_HMEM hMem;
        GUI_MEMDEV_Handle hMemJPEG;
    
        /* 打开文件 */        
        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;
        }
        
        GUI_JPEG_GetInfo(_acBuffer, file.obj.objsize, &JpegInfo);
        
        /* 创建内存设备,并将JPEG图片绘制到此内存设备里面,此内存设备要在主程序中用到
           所以退出此函数前,不要释放。
        */
        hMemJPEG = GUI_MEMDEV_CreateEx(0, 0, JpegInfo.XSize, JpegInfo.YSize, GUI_MEMDEV_HASTRANS);
        GUI_MEMDEV_Select(hMemJPEG);
        GUI_JPEG_Draw(_acBuffer, file.obj.objsize, x, y);
        GUI_MEMDEV_Select(0);
    
        /* 释放动态内存hMem */
        GUI_ALLOC_Free(hMem);
        
        /* 关闭文件 */
        f_close(&file);
        
        return hMemJPEG;
    }
    •   第3步:通过函数GUI_MEMDEV_WriteAt绘制内存设备中的图片,比如我们要绘制的图片1.jpg文件已经存储到了SD卡根目录下,显示方法如下:
        GUI_MEMDEV_Handle hMemJPEG;
                
        
        /* 加载JPEG图片到内存设备 */
        hMemBMP = _ShowJPEG2("1.bmp", 0, 0);
    
        /* 用到JPEG图片的时候,调用此函数即可 */
    GUI_MEMDEV_WriteAt(hMemBMP, 0, 0);

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

    24.3.2   绘制无需加载到存储器的JPEG图片

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

    •   第1步:将JPEG图片复制到SD卡的根目录下,然后直接调用函数GUI_JPEG_DrawEx就可以显示。
    /*
    *********************************************************************************************************
    *    函 数 名: _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;
    }
    
    
    /*
    *********************************************************************************************************
    *    函 数 名: _ShowJPEG1
    *    功能说明: 显示JPEG图片
    *    形    参: sFilename  要读取的文件名
    *    返 回 值: 无
    *********************************************************************************************************
    */
    void _ShowJPEG1(const char *sFilename) 
    {
        /* 打开文件 */        
        result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS);
        if (result != FR_OK)
        {
            return;
        }
        
        /* 绘制JPEG图片 */
        GUI_JPEG_DrawEx(_GetData, &file, 0, 0);
        
        /* 关闭文件 */
        f_close(&file);
    }
    •  第2步:用户要显示指定的文件1.jpg,调用函数_ShowJPEG1("1.jpg")即可显示。

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

    24.3.3   将JPEG格式的图片转换成C文件

    使用这种方法可以方便的将较小的PNG格式图片存到内部Flash。将PNG图片转换成C文件需要用到Bin2C.exe小软件。比如,我们将1.jpg图片(此图片在本章教程配套例子的的Doc文件夹里面)转换成C文件,生成的代码如下:

    static const unsigned char _ac1[103357UL + 1] = {
      0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      /* 其余数据省略 */
    
    }

    24.4 实验例程说明(RTOS)

    配套例子:

    V7-528_emWin6.x实验_JPEG图片显示(RTOS软解方式)

    实验目的:

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

    实验内容:

    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文件中的配置如下:

    #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分辨率界面效果。

     

    24.5 实验例程说明(裸机)

    配套例子:

    V7-527_emWin6.x实验_JPEG图片显示(裸机软解方式)

    实验目的:

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

    emWin界面显示效果:

    800*480分辨率界面效果。

     

    emWin动态内存配置:

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

    #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动态内存。

    24.6 总结

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

    另外,由于JPEG图片比较小,且V7板子使用的STM32H743XI有2MB的内部flash,所以使用Bin2C.exe软件将JPEG图片转换成C文件添加到MDK或者IAR工程里面再下载到内部flash也是很方便的。

    微信公众号:armfly_com 安富莱论坛:www.armbbs.cn 安富莱淘宝:https://armfly.taobao.com
  • 相关阅读:
    leetcode@ [279]Perfect Squares
    leetcode@ [160]Intersection of Two Linked Lists
    leetcode@ [209]Minimum Size Subarray Sum
    leetcode@ [22]Generate Parentheses (递归 + 卡特兰数)
    jmeter—JDBC request动态参数设置
    Jmeter JDBC Request--测试数据库连接 拒绝解决方案
    转JMeter ----数据库 not allowed to connect to this MySQL
    jmeter ---监控服务器CPU, 内存,网络数据
    在free bsd上跑JMeter 的 plugin "PerfMon Server Agent"
    解决Jmeter插件ERROR: java.io.IOException: Agent is unreachable via TCP的错误
  • 原文地址:https://www.cnblogs.com/armfly/p/14959490.html
Copyright © 2011-2022 走看看