硬汉嵌入式论坛

 找回密码
 立即注册
查看: 2425|回复: 5
收起左侧

[emWin教程入门篇] 【emWin实战教程V2.0】第18章      GIF图片显示

[复制链接]

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
发表于 2017-1-12 16:29:51 | 显示全部楼层 |阅读模式
完整65章+12章附件教程下载地址:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=19834




第18章      GIF图片显示



    本期主要讲emWin支持的GIF图片显示,官方支持的主要有两种显示方法,一种方法是直接从外部存储器读取数据并显示,这种方法的好处就是不需要大的RAM,每次读取一些数据显示一次,缺点就是显示速度稍慢。另一种是从外部存储器读取整个图片到RAM(比如内部SRAM,外部SRAM或者外部SDRAM),然后再显示图片,这种方法的显示速度要稍快些。对于界面效果简单的GIF图片,两种方法区别不大,界面效果稍复杂的GIF图片,推荐后者,即将整个图片读到RAM空间,显示效果要好些。
    18.1 初学者重要提示
    18.2 GIF图片基础知识
    18.3 GIF图片的API函数及其显示方法
    18.4 实验例程说明(RTOS)
    18.5 实验例程说明(裸机)
    18.6 总结



18.1  初学者重要提示

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

18.1.png


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

2、本章教程使用的外部存储器是SD卡,实际项目中使用任何其它类型的存储器都可以的,支不支持文件系统都没有关系的,使用方法与本章教程一样,用户要做的就是把图片从外部存储器读出即可。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-1-12 16:31:33 | 显示全部楼层
18.2 GIF图片基础知识


    关于GIF图片格式方面的知识,推荐大家看wiki百科上面的介绍:
1、https://en.wikipedia.org/wiki/GIF 讲解非常详细。
2、如果觉得英文版读起来比较吃力些,可以看wiki中文版,只是资料没有英文的详细:https://zh.wikipedia.org/wiki/GIF
3、更多GIF文件的知识可以google或者百度进行了解。
    推荐初学者了解一下GIF文件的格式,如果没有了解也是没有任何关系的,直接调用emWin的API函数就可以显示GIF图片了。
----------------------------------------------------------------------------------------------------------
    下面这点小知识还是要知道的:
    GIF(Graphics Interchange Format)的原义是“图像互换格式”,是CompuServe公司在 1987年开发的图像文件格式。GIF文件的数据,是一种基于LZW算法的连续色调的无损压缩格式。其压缩率一般在50%左右,它不属于任何应用程序。目前几乎所有相关软件都支持它,公共领域有大量的软件在使用GIF图像文件。GIF图像文件的数据是经过压缩的,而且是采用了可变长度等压缩算法。GIF格式的另一个特点是其在一个GIF文件中可以存多幅彩色图像,如果把存于一个文件中的多幅图像数据逐幅读出并显示到屏幕上,就可构成一种最简单的动画。
    GIF格式自1987年由CompuServe公司引入后,因其体积小且成像相对清晰,特别适合于初期慢速的互联网,而从此大受欢迎。它采用无损压缩技术,只要图像不多于256色,则可既减少文件的大小,又保持成像的质量。(当然,现在也存在一些hack技术,在一定的条件下克服了256色的限制)然而,256色的限制大大局限了GIF文件的应用范围,如彩色相机等。(当然采用无损压缩技术的彩色相机照片亦不适合通过网络传输。)另一方面,在高彩图片上有着不俗表现的JPG格式却在简单的折线效果上差强人意。因此GIF格式普遍适用于图表,按钮等等只需少量颜色的图像(如黑白照片)。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-1-12 16:36:02 | 显示全部楼层
18.3 GIF图片的API函数及其显示方法


    当前emWin支持的API函数有如下16个:
18.3.png

从上面的表格中可以看出,emWin支持GIF文件显示主要有两种类型的函数,一类是以Ex结尾的函数,这种函数显示GIF图片是一边从外部存储器加载数据一边显示,显示速度相对较慢,适用于内存较小的场合。另一类是不以Ex结尾的函数,这种函数直接从指定的地址读取数据进行显示(注意,这里的地址需是总线式地址,比如外部SDRAM,外部SRAM,内部Flash和内部SRAM都可以),显示速度相对稍快。
    本章教程会对这两种方式都进行说明:
1、intGUI_GIF_DrawSub(const void * pGIF, U32 NumBytes, int x0, int y0, int Index);
    此函数直接从地址pGIF读取第Index张(注意,这里的Index就是指的参数Index所指定的子图片序号,这个参数是从0开始计数的)GIF子图片的数据,将图片显示到用户设置的位置(x0, y0)。
2、intGUI_GIF_DrawSubEx(GUI_GET_DATA_FUNC * pfGetData, void * p, int x0, int y0, intIndex);
    此函数通过其回调函数pfGetData读取第Index张(注意,这里的Index就是指的参数Index所指定的子图片序号,这个参数是从0开始计数的)GIF子图片的数据,从而实现边读取图片数据边显示的功能,将图片显示到用户设置的位置(x0, y0)
   
实现GIF图片的动态显示还要用如下四个函数配合:
(1)intGUI_GIF_GetInfo(const void * pGIF, U32 NumBytes, GUI_GIF_INFO * pInfo);
    此函数直接从地址pGIF读取GIF文件数据,返回GIF图片的长度,宽度以及子图片的张数。返回结果是存储到第三个参数所指向的结构体变量地址。
(2)int GUI_GIF_GetInfoEx(GUI_GET_DATA_FUNC* pfGetData, void * p, GUI_GIF_INFO * pInfo);
    此函数通过其回调函数pfGetData读取图片数据,返回GIF图片的长度,宽度以及子图片的张数。返回结果是存储到第三个参数所指向的结构体变量地址。结构体GUI_GIF_INFO的定义如下(函数GUI_GIF_GetInfo和函数GUI_GIF_GetInfoEx的结构体类型是一样的):
18.4.png

    这个里面有个错误:不是XSize和YSize,而是xSize和ySize(这个错误官方手册一直没有更正)。
(3)intGUI_GIF_GetImageInfo(const void * pGIF, U32 NumBytes, GUI_GIF_IMAGE_INFO *pInfo, int Index);
     此函数直接从地址pGIF读取GIF文件数据,返回上次(注意,这里的上次就是指的参数Index所指定的子图片序号,这个参数是从0开始计数的)所绘制的子图片显示位置,长度,宽度和显示多长时间。其中显示多少时间的参数比较有用,专门用于设置各个子图片之间的时间间隔。返回结果是存储到第三个参数所指向的结构体变量地址。
(4)Int GUI_GIF_GetImageInfoEx(GUI_GET_DATA_FUNC* pfGetData, void * p,
                                                                             GUI_GIF_IMAGE_INFO *pInfo, int Index);

    此函数通过其回调函数pfGetData读取图片数据,返回上次(注意,这里的上次就是指的参数Index所指定的子图片序号,这个参数是从0开始计数的)所绘制的子图片显示位置,长度,宽度和显示多长时间。其中显示多少时间的参数比较有用,专门用于设置时间各个子图片之间的时间间隔。返回结果是存储到第三个参数所指向的结构体变量地址。结构体GUI_GIF_IMAGE_INFO的定义如下(函数GUI_GIF_GetImageInfo和GUI_GIF_GetImageInfoEx的结构体类型是一样的):
18.5.png


18.3.1   绘制已经加载到存储器的GIF图片


    绘制加载到存储器的GIF图片主要是通过函数GUI_GIF_DrawSub来实现,下面我们分2步来说明如何将SD卡中的GIF图片显示到LCD上面。
第1步将GIF图片复制到SD卡的根目录下,然后通过emWin的动态内存管理函数申请动态内存并将GIF文件加载进来, 这里我们用的是外部SDRAM做emWin的动态内存。
  1. char *_acBuffer;
  2.      GUI_HMEM hMem;
  3.      /* 打开文件 */        
  4.      result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS);
  5.      if (result != FR_OK)
  6.      {
  7.          return 0;
  8.      }
  9.       
  10.      /* 申请一块内存空间 并且将其清零 */
  11.      hMem = GUI_ALLOC_AllocZero(file.fsize);
  12.    
  13.      /* 将申请到内存的句柄转换成指针类型 */
  14.      _acBuffer = GUI_ALLOC_h2p(hMem);
  15.      /* 读取文件到动态内存 */
  16.      result = f_read(&file, _acBuffer, file.fsize, &bw);
  17.      if (result != FR_OK)
  18.      {
  19.          return 0;
  20.      }
复制代码
第2步将加载到emWin动态内存的GIF图片直接显示即可,特别注意每张子图片的显示方法及其时间间隔设置,然后结合第1步,完整的代码如下:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: _ShowGIF2
  4. *    功能说明: 显示GIF片
  5. *    形    参: sFilename 要显示的图片名字
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void _ShowGIF2(const char * sFilename)
  10. {
  11.      uint16_t i = 0;
  12.      uint32_t t0, t1;
  13.      char *_acBuffer;
  14.      GUI_HMEM hMem;
  15.      char buf[50];
  16.      /* 打开文件 */         
  17.      result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS);
  18.      if (result != FR_OK)
  19.      {
  20.          return;
  21.      }
  22.       
  23.      /* 申请一块内存空间 并且将其清零 */
  24.      hMem = GUI_ALLOC_AllocZero(file.fsize);
  25.    
  26.      /* 将申请到内存的句柄转换成指针类型 */
  27.      _acBuffer = GUI_ALLOC_h2p(hMem);
  28.      /* 读取文件到动态内存 */
  29.      result = f_read(&file, _acBuffer, file.fsize, &bw);
  30.      if (result != FR_OK)
  31.      {
  32.          return;
  33.      }
  34.   
  35.      /* 获取GIF图片信息 */
  36.    GUI_GIF_GetInfo(_acBuffer, file.fsize, &InfoGif);
  37.    
  38.    while(1)
  39.    {
  40.          /* 变量用来设置当前播放的帧数,InfoGif.NumImages是GIF图片总的帧数 */
  41.          if(i < InfoGif.NumImages)
  42.          {
  43.               /* 获取当前帧GIF图片信息,注意第4个参数是从0开始计数的 */
  44.              GUI_GIF_GetImageInfo(_acBuffer, file.fsize, &ImagInfoGif, i);
  45.             
  46.               /* 如果此帧延迟时间是0,默认是延迟100ms */
  47.               if(ImagInfoGif.Delay == 0)
  48.               {
  49.                    GUI_Delay(100);
  50.               }
  51.               else
  52.               {
  53.                    t0 = GUI_GetTime();
  54.                    /* 显示当前播放的帧数 */
  55.                    sprintf(buf, "     Frame:%d/%d     ", i+1, InfoGif.NumImages);
  56.                    GUI_DispStringHCenterAt(buf, LCD_GetXSize()/2, 0);
  57.                   
  58.                    /* 解码并显示此帧GIF图片,注意第5个参数是从0开始计数的 */
  59.                    GUI_GIF_DrawSub(_acBuffer,
  60.                                      file.fsize,
  61.                                      (LCD_GetXSize() - InfoGif.xSize)/2,
  62.                                      (LCD_GetYSize() - InfoGif.ySize)/2,
  63.                                      i++);
  64.                   
  65.                    /* 获取本次解码和显示消耗的时间 */
  66.                    t1 = GUI_GetTime() - t0;
  67.                   
  68.                    /* 如果GIF的解码和显示的时间超时就不做延迟 */
  69.                    if (t1 < ImagInfoGif.Delay * 10)
  70.                    {
  71.                        GUI_Delay(ImagInfoGif.Delay * 10 - t1);
  72.                    }
  73.               }              
  74.          }
  75.          else
  76.          {
  77.              i = 0;
  78.          }
  79.      }
  80.      /* 实际应用中,根据实际情况释放动态内存 */        
  81. //   GUI_ALLOC_Free(hMem);
  82. //   f_close(&file);
  83. }
复制代码
通过上面两步就完成了GIF图片的动态显示,用户要显示哪个图片,调用函数_ShowGIF2()即可,比如要显示1.gif图片,可以调用_ShowGIF2("1.gif"),这种方式显示GIF图片相对稍快些。实际显示效果参看本章节配套的实验例程说明。

18.3.2   绘制无需加载到存储器的GIF图片


    绘制无需加载到存储器的GIF图片主要是通过函数GUI_GIF_DrawSubEx来实现,这种方式的优点是需要的内存小,但是显示速度稍慢。下面我们分2步来说明如何将SD卡中的GIF图片显示到LCD上面。
第1步将GIF图片复制到SD卡的根目录下,然后直接调用函数GUI_GIF_DrawSubEx就可以显示
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: _GetData
  4. *    功能说明: 被函数GUI_BMP_DrawEx调用
  5. *    形    参:p             FIL类型数据
  6. *             NumBytesReq   请求读取的字节数
  7. *             ppData        数据指针
  8. *             Off           如果Off = 1,那么将重新从起始位置读取               
  9. *    返 回 值: 返回读取的字节数
  10. *********************************************************************************************************
  11. */
  12. int _GetData(void * p, const U8 ** ppData, unsigned NumBytesReq, U32 Off)
  13. {
  14.      static int FileAddress = 0;
  15.      UINT NumBytesRead;
  16.      FIL *PicFile;
  17.      PicFile = (FIL *)p;
  18.      /*
  19.      * 检测缓存大小
  20.      */
  21.      if (NumBytesReq > sizeof(acBuffer)) {
  22.      NumBytesReq = sizeof(acBuffer);
  23.      }
  24.      /*
  25.      * 设置读取位置
  26.      */
  27.      if(Off == 1) FileAddress = 0;
  28.      else FileAddress = Off;
  29.      result =f_lseek(PicFile, FileAddress);
  30.      /*
  31.      * 读取数据到缓存
  32.      */
  33.      result = f_read(PicFile, acBuffer, NumBytesReq, &NumBytesRead);
  34.      /*
  35.      * 让指针ppData指向读取的数据
  36.      */
  37.      *ppData = (const U8 *)acBuffer;
  38.      /*
  39.      * 返回读取的字节数
  40.      */
  41.      return NumBytesRead;
  42. }
  43. /*
  44. *********************************************************************************************************
  45. *    函 数 名: _ShowGIF1
  46. *    功能说明: 显示GIF片
  47. *    形    参: sFilename 要显示的图片名字
  48. *    返 回 值: 无
  49. *********************************************************************************************************
  50. */
  51. void _ShowGIF1(const char * sFilename)
  52. {
  53.      uint16_t i = 0;
  54.      uint32_t t0, t1;
  55.      char buf[50];
  56.      /* 打开文件 */         
  57.      result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS);
  58.      if (result != FR_OK)
  59.      {
  60.          return;
  61.      }
  62.   
  63.      /* 获取GIF图片信息 */
  64.    GUI_GIF_GetInfoEx(_GetData, &file,&InfoGif);
  65.    
  66.    while(1)
  67.    {
  68.          /* 变量用来设置当前播放的帧数,InfoGif.NumImages是GIF图片总的帧数 */
  69.          if(i < InfoGif.NumImages)
  70.          {
  71.               /* 获取当前帧GIF图片信息,注意第4个参数是从0开始计数的 */
  72.               GUI_GIF_GetImageInfoEx(_GetData, &file, &ImagInfoGif, i);
  73.             
  74.               /* 如果此帧延迟时间是0,默认是延迟100ms */
  75.               if(ImagInfoGif.Delay == 0)
  76.               {
  77.                    GUI_Delay(100);
  78.               }
  79.               else
  80.               {
  81.                    t0 = GUI_GetTime();
  82.                    /* 显示当前播放的帧数 */
  83.                    sprintf(buf, "     Frame:%d/%d     ", i+1, InfoGif.NumImages);
  84.                    GUI_DispStringHCenterAt(buf, LCD_GetXSize()/2, 0);
  85.                   
  86.                    /* 解码并显示此帧GIF图片,注意第5个参数是从0开始计数的 */
  87.                    GUI_GIF_DrawSubEx(_GetData,
  88.                                        &file,
  89.                                        (LCD_GetXSize() - InfoGif.xSize)/2,
  90.                                        (LCD_GetYSize() - InfoGif.ySize)/2,
  91.                                        i++);
  92.                   
  93.                    /* 获取本次解码和显示消耗的时间 */
  94.                    t1 = GUI_GetTime() - t0;
  95.                   
  96.                    /* 如果GIF的解码和显示的时间超时就不做延迟 */
  97.                    if (t1 < ImagInfoGif.Delay * 10)
  98.                    {
  99.                        GUI_Delay(ImagInfoGif.Delay * 10 - t1);
  100.                    }
  101.               }              
  102.          }
  103.          else
  104.          {
  105.              i = 0;
  106.          }
  107.      }
  108.      /* 实际应用中,根据实际情况释放动态内存 */        
  109. //   GUI_ALLOC_Free(hMem);
  110. //   f_close(&file);
  111. }
复制代码
第2步用户要显示指定的文件1.gif,调用函数_ShowGIF1("1.gif")即可显示。
    通过上面2步就完成了GIF图片的动态显示,这种方式显示GIF图片速度稍慢,实际显示效果参看本章节配套的实验例程说明。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-1-12 16:43:12 | 显示全部楼层
18.4 实验例程说明(RTOS)


配套例子:
    V6-522_STemWin实验_GIF图片显示(RTOS)
实验目的:
    1.     学习emWin的GIF图片显示。
    2.     emWin功能的实现在MainTask.c文件里面。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2按键按下,实现截图功能,将图片以BMP格式保存到SD卡中。
    3.     各个任务实现的功能如下:
              App Task Start   任务:实现按键和触摸扫描。
              App Task MspPro任务 :实现截图功能,将图片以BMP格式保存到SD卡中。
              App Task UserIF  任务:按键消息处理。
              App Task COM   任务:暂未使用。
App Task GUI    任务:GUI任务。
实验注意:
    1.     本实验主要学习emWin的GIF图片显示功能,实验所需的图片文件1.gif已经存储到本工程的Doc文件夹下,使用此例子前,请务必将此文件存储到SD卡根目录中,并将SD卡插到开发板上面。
μCOS-III任务调试信息(按K1按键,串口打印):
18.6.png

STemWin界面显示效果:
    800*480分辨率界面效果。
18.7.png

STemWin动态内存配置:
    GUIConf.c文件中的配置如下:
  1. #define EX_SRAM   1/*1 used extern sram, 0 used internal sram */
  2. #if EX_SRAM
  3. #define GUI_NUMBYTES  (1024*1024*8)
  4. #else
  5. #define GUI_NUMBYTES  (100*1024)
  6. #endif
复制代码
    通过宏定义来配置使用内部SRAM还是外部的SDRAM做为emWin的动态内存,当配置:
#define EX_SRAM    1 表示使用外部SDRAM作为emWin动态内存,大小8MB。
#define EX_SRAM    0 表示使用内部SRAM作为emWin动态内存,大小100KB。
默认情况下,本教程配套的所有emWin例子都是用外部SDRAM作为emWin动态内存。
STemWin底层接口配置:
    LCDConf_Lin_Template.c文件中共12项emWin配置:
  1. /*
  2. **********************************************************************************************************
  3.                                           用户可以配置的选项
  4. **********************************************************************************************************
  5. */
  6. /* 0. 在官方代码的基础上再做优化,官方的部分函数效率低,耗内存, 0表示优化 */
  7. #define emWin_Optimize   0
  8. /*
  9.   1. 显示屏的物理分辨率,驱动已经做了显示屏自适应,支持4.3寸,5寸和7寸屏
  10.      这里填写自适应显示屏中的最大分辨率。
  11. */
  12. #define XSIZE_PHYS       800
  13. #define YSIZE_PHYS       480
  14. /* 2. 多缓冲 / 虚拟屏,多缓冲和虚拟屏不可同时使用,emWin不支持 */
  15. #define NUM_BUFFERS      3 /* 定义多缓冲个数,仅可以设置1,2和3,也就是最大支持三缓冲 */
  16. #define NUM_VSCREENS     1 /* 定义虚拟屏个数 */
  17. /* 3. 没有图层激活时,背景色设置, 暂时未用到 */
  18. #define BK_COLOR         GUI_DARKBLUE
  19. /*
  20.    4. 重定义图层数,对于STM32F429/439,用户可以选择一个图层或者两个图层,不支持三图层
  21.       (1). 设置GUI_NUM_LAYERS = 1时,即仅使用图层1时,默认触摸值是发送给图层1的。
  22.        (2). 设置GUI_NUM_LAYERS = 2时,即图层1和图层2都已经使能,此时图层2是顶层,
  23.             用户需要根据自己的使用情况设置如下两个地方。
  24.             a. 在bsp_touch.c文件中的函数TOUCH_InitHard里面设置参数State.Layer = 1,1就表示
  25.                给图层2发送触摸值。
  26.             b. 调用GUI_Init函数后,调用函数GUI_SelectLayer(1), 设置当前操作的是图层2。
  27. */
  28. #undef  GUI_NUM_LAYERS
  29. #define GUI_NUM_LAYERS    1
  30. /*
  31.    5. 设置图层1和图层2对应的显存地址
  32.       (1) EXT_SDRAM_ADDR 是SDRAM的首地址。
  33.       (2) LCD_LAYER0_FRAME_BUFFER 是图层1的显存地址。
  34.        (3) LCD_LAYER1_FRAME_BUFFER 是图层2的显存地址。
  35.        (4) 每个图层的显存大小比较考究,这里进行下简单的说明。
  36.            如果用户选择的颜色模式 = 32位色ARGB8888,显存的大小:
  37.            XSIZE_PHYS * YSIZE_PHYS * 4 * NUM_VSCREENS * NUM_BUFFERS
  38.          
  39.            颜色模式 = 24位色RGB888,显存的大小:
  40.            XSIZE_PHYS * YSIZE_PHYS * 3 * NUM_VSCREENS * NUM_BUFFERS
  41.          
  42.            颜色模式 = 16位色RGB566,ARGB1555, ARGB4444,AL88,那么显存的大小就是:
  43.            XSIZE_PHYS * YSIZE_PHYS * 2 * NUM_VSCREENS * NUM_BUFFERS
  44.            颜色模式 = 8位色L8,AL44,那么显存的大小就是:
  45.            XSIZE_PHYS * YSIZE_PHYS * 1 * NUM_VSCREENS * NUM_BUFFERS  
  46.      
  47.       这里为了方便起见,将开发板配套的16MB的SDRAM前8MB分配给LCD显存使用,后8MB用于emWin动态内存。
  48.        对于24位色,16位色,8位色,用户可以对其使能三缓冲,并且使能双图层。但是32位色也使能三缓冲和双
  49.        图层的话会超出8MB,所以用户根据自己的情况做显存和emWin动态内存的分配调整。
  50.          举一个例子,对于800*480分辨率的显示屏,使能32位色,三缓冲,那么最终一个图层需要的大小就是
  51.       800 * 480 * 4 * 3  = 4.394MB的空间,如果是双图层,已经超出8MB的分配范围。
  52.      
  53.       (5)为了方便起见,图层2的宏定义LCD_LAYER1_FRAME_BUFFER中的参数4是按照32位色设置的,如果用户的图层1
  54.          使用的是8位色,这里填数字1,如果是16位色,这里填2,如果是24位色,这里填3。
  55. */
  56. #define LCD_LAYER0_FRAME_BUFFER  EXT_SDRAM_ADDR
  57. #define LCD_LAYER1_FRAME_BUFFER  (LCD_LAYER0_FRAME_BUFFER + XSIZE_PHYS * YSIZE_PHYS * 4 * NUM_VSCREENS *
  58. NUM_BUFFERS)
  59. /*
  60.    6. STM32F429/439支持的颜色模式,所有模式都支持,用户可任意配置。
  61.       特别注意如下两个问题:
  62.        (1) 如果用户选择了ARGB8888或者RGB888模式,LCD闪烁比较厉害的话,
  63.            请降低LTDC的时钟大小,在文件bsp_tft_429.c的函数LCD_ConfigLTDC里面设置。
  64.            a. 一般800*480分辨率的显示屏,ARGB8888或者RGB888模式LTDC时钟选择10-20MHz即可。
  65.            b. 480*272分辨率的可以高些,取20MHz左右即可。
  66.        (2) 16位色或者8位色模式,LTDC的时钟频率一般可以比24位色或者32位色的高一倍。
  67. */
  68. #define _CM_ARGB8888      1
  69. #define _CM_RGB888        2
  70. #define _CM_RGB565        3
  71. #define _CM_ARGB1555      4
  72. #define _CM_ARGB4444      5
  73. #define _CM_L8            6
  74. #define _CM_AL44          7
  75. #define _CM_AL88          8
  76. /* 7. 配置图层1的颜色模式和分辨率大小 */
  77. #define COLOR_MODE_0      _CM_RGB565
  78. #define XSIZE_0           XSIZE_PHYS
  79. #define YSIZE_0           YSIZE_PHYS
  80. /* 8. 配置图层2的的颜色模式和分辨率大小 */
  81. #define COLOR_MODE_1      _CM_RGB565
  82. #define XSIZE_1           XSIZE_PHYS
  83. #define YSIZE_1           YSIZE_PHYS
  84. /* 9. 单图层情况下,根据用户选择的颜色模式可自动选择图层1的emWin的驱动和颜色模式 */
  85. #if   (COLOR_MODE_0 == _CM_ARGB8888)
  86.   #define COLOR_CONVERSION_0 GUICC_M8888I
  87.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_32
  88. #elif (COLOR_MODE_0 == _CM_RGB888)
  89.   #define COLOR_CONVERSION_0 GUICC_M888
  90.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_24
  91. #elif (COLOR_MODE_0 == _CM_RGB565)
  92.   #define COLOR_CONVERSION_0 GUICC_M565
  93.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_16
  94. #elif (COLOR_MODE_0 == _CM_ARGB1555)
  95.   #define COLOR_CONVERSION_0 GUICC_M1555I
  96.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_16
  97. #elif (COLOR_MODE_0 == _CM_ARGB4444)
  98.   #define COLOR_CONVERSION_0 GUICC_M4444I
  99.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_16
  100. #elif (COLOR_MODE_0 == _CM_L8)
  101.   #define COLOR_CONVERSION_0 GUICC_8666
  102.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_8
  103. #elif (COLOR_MODE_0 == _CM_AL44)
  104.   #define COLOR_CONVERSION_0 GUICC_1616I
  105.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_8
  106. #elif (COLOR_MODE_0 == _CM_AL88)
  107.   #define COLOR_CONVERSION_0 GUICC_88666I
  108.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_16
  109. #else
  110.   #error Illegal color mode 0!
  111. #endif
  112. /* 10. 双图层情况下,根据用户选择的颜色模式可自动选择图层2的emWin的驱动和颜色模式 */
  113. #if (GUI_NUM_LAYERS > 1)
  114. #if   (COLOR_MODE_1 == _CM_ARGB8888)
  115.   #define COLOR_CONVERSION_1 GUICC_M8888I
  116.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_32
  117. #elif (COLOR_MODE_1 == _CM_RGB888)
  118.   #define COLOR_CONVERSION_1 GUICC_M888
  119.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_24
  120. #elif (COLOR_MODE_1 == _CM_RGB565)
  121.   #define COLOR_CONVERSION_1 GUICC_M565
  122.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_16
  123. #elif (COLOR_MODE_1 == _CM_ARGB1555)
  124.   #define COLOR_CONVERSION_1 GUICC_M1555I
  125.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_16
  126. #elif (COLOR_MODE_1 == _CM_ARGB4444)
  127.   #define COLOR_CONVERSION_1 GUICC_M4444I
  128.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_16
  129. #elif (COLOR_MODE_1 == _CM_L8)
  130.   #define COLOR_CONVERSION_1 GUICC_8666
  131.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_8
  132. #elif (COLOR_MODE_1 == _CM_AL44)
  133.   #define COLOR_CONVERSION_1 GUICC_1616I
  134.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_8
  135. #elif (COLOR_MODE_1 == _CM_AL88)
  136.   #define COLOR_CONVERSION_1 GUICC_88666I
  137.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_16
  138. #else
  139.   #error Illegal color mode 1!
  140. #endif
  141. #else
  142. #undef XSIZE_0
  143. #undef YSIZE_0
  144. #define XSIZE_0       XSIZE_PHYS
  145. #define YSIZE_0       YSIZE_PHYS
  146. #endif
  147. /*11. 配置选项检测,防止配置错误或者某些选项没有配置 */
  148. #ifndef   XSIZE_PHYS
  149.   #error Physical X size of display is not defined!
  150. #endif
  151. #ifndef   YSIZE_PHYS
  152.   #error Physical Y size of display is not defined!
  153. #endif
  154. #ifndef   NUM_VSCREENS
  155.   #define NUM_VSCREENS 1
  156. #else
  157.   #if (NUM_VSCREENS <= 0)
  158.     #error At least one screeen needs to be defined!
  159.   #endif
  160. #endif
  161. #if (NUM_VSCREENS > 1) && (NUM_BUFFERS > 1)
  162.   #error Virtual screens and multiple buffers are not allowed!
  163. #endif
复制代码
    对于这12个配置选项,注释说明已经比较详细。默认情况下,本教程配套的emWin例子都是用的三缓冲,RGB565格式,且仅使用单图层。
程序设计:
任务栈大小分配:
    μCOS-III任务栈大小在os_cfg.h文件中配置:
         #define  APP_CFG_TASK_START_STK_SIZE                     512u
         #define  APP_CFG_TASK_MsgPro_STK_SIZE                    512u
         #define  APP_CFG_TASK_COM_STK_SIZE                       512u
        #define  APP_CFG_TASK_USER_IF_STK_SIZE                    512u
        #define  APP_CFG_TASK_GUI_STK_SIZE                        1024u
   任务栈大小的单位是4字节,那么每个任务的栈大小如下:
         App Task Start   任务:2048字节。
               App Task MspPro任务 :2048字节。
               App Task UserIF  任务:2048字节。
               App Task COM   任务:2048字节。
         App Task GUI    任务:4096字节。
系统栈大小分配:
    μCOS-III的系统栈大小在os_cfg_app.h文件中配置:
       #define  OS_CFG_ISR_STK_SIZE                      512u      
       系统栈大小的单位是4字节,那么这里就是配置系统栈大小为2KB。
μCOS-III初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.     OS_ERR  err;
  12.    
  13.      /* 初始化uC/OS-III 内核 */
  14.     OSInit(&err);
  15.      /* 创建一个启动任务(也就是主任务)。启动任务会创建所有的应用程序任务 */
  16.      OSTaskCreate((OS_TCB       *)&AppTaskStartTCB,  /* 任务控制块地址 */           
  17.                  (CPU_CHAR     *)"App Task Start",  /* 任务名 */
  18.                  (OS_TASK_PTR   )AppTaskStart,      /* 启动任务函数地址 */
  19.                  (void         *)0,                 /* 传递给任务的参数 */
  20.                  (OS_PRIO       )APP_CFG_TASK_START_PRIO, /* 任务优先级 */
  21.                  (CPU_STK      *)&AppTaskStartStk[0],     /* 堆栈基地址 */
  22.                  (CPU_STK_SIZE  )APP_CFG_TASK_START_STK_SIZE / 10, /* 堆栈监测区,这里表示后10%作为监测区 */
  23.                  (CPU_STK_SIZE  )APP_CFG_TASK_START_STK_SIZE,      /* 堆栈空间大小 */
  24.                  (OS_MSG_QTY    )0,  /* 本任务支持接受的最大消息数 */
  25.                  (OS_TICK       )0,  /* 设置时间片 */
  26.                  (void         *)0,  /* 堆栈空间大小 */
  27.                  (OS_OPT        )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
  28.         
  29.                     /*  定义如下:
  30.                        OS_OPT_TASK_STK_CHK      使能检测任务栈,统计任务栈已用的和未用的
  31.                        OS_OPT_TASK_STK_CLR      在创建任务时,清零任务栈
  32.                        OS_OPT_TASK_SAVE_FP      如果CPU有浮点寄存器,则在任务切换时保存浮点寄存器的内容
  33.                     */
  34.                  (OS_ERR       *)&err);
  35.      /* 启动多任务系统,控制权交给uC/OS-III */
  36.     OSStart(&err);                                             
  37.    
  38.     (void)&err;
  39.    
  40.     return (0);
  41. }
复制代码
硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 使能CRC 因为使用STemWin前必须要使能 */
  17.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_CRC, ENABLE);
  18.    
  19.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  20.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  21.    
  22.      SystemCoreClockUpdate();    /* 根据PLL配置更新系统时钟频率变量 SystemCoreClock */
  23.      bsp_InitUart();        /* 初始化串口 */
  24.      bsp_InitKey();         /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */
  25.    
  26.      bsp_InitExtIO();       /* FMC总线上扩展了32位输出IO, 操作LED等外设必须初始化 */
  27.      bsp_InitLed();         /* 初始LED指示灯端口 */
  28.    
  29.      bsp_InitI2C();         /* 配置I2C总线 */
  30.    
  31.      bsp_InitExtSDRAM();   /* 初始化SDRAM */
  32.      bsp_DetectLcdType();  /* 检测触摸板和LCD面板型号, 结果存在全局变量 g_TouchType, g_LcdType */
  33.    
  34.      TOUCH_InitHard();    /* 初始化配置触摸芯片 */
  35.      LCD_ConfigLTDC();     /* 初始化配置LTDC */
  36.    
  37.      result = f_mount(&fs, "0:/", 0);     /* 挂载文件系统 */
  38. }
复制代码
五个μCOS-III任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskStart
  4. *    功能说明: 这是一个启动任务,在多任务系统启动后,必须初始化滴答计数器。本任务主要实现按键和触摸检测。
  5. *    形    参: p_arg 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7.      优 先 级: 2
  8. *********************************************************************************************************
  9. */
  10. static  void  AppTaskStart (void *p_arg)
  11. {
  12.      OS_ERR      err;
  13.      uint8_t  ucCount = 0;
  14.      uint8_t  ucCount1 = 0;
  15.    
  16.     /* 仅用于避免编译器告警,编译器不会产生任何目标代码 */  
  17.     (void)p_arg;
  18.    
  19.      /* BSP 初始化。 BSP = Board Support Package 板级支持包,可以理解为底层驱动。*/
  20.      CPU_Init();  /* 此函数要优先调用,因为外设驱动中使用的us和ms延迟是基于此函数的 */
  21.      bsp_Init();      
  22.      BSP_Tick_Init();
  23.    
  24. #if OS_CFG_STAT_TASK_EN > 0u
  25.      OSStatTaskCPUUsageInit(&err);  
  26. #endif
  27. #ifdef CPU_CFG_INT_DIS_MEAS_EN
  28.     CPU_IntDisMeasMaxCurReset();
  29. #endif
  30.         
  31.      /* 创建应用程序的任务 */
  32.      AppTaskCreate();
  33.    
  34.      /* 创建任务通信 */
  35.      AppObjCreate();
  36.    
  37.     while(1)
  38.     {
  39.          /* 1ms一次触摸扫描,电阻触摸屏 */
  40.          if(g_tTP.Enable == 1)
  41.          {
  42.               TOUCH_Scan();
  43.             
  44.               /* 按键扫描 */
  45.               ucCount++;
  46.               if(ucCount == 10)
  47.               {
  48.                    ucCount = 0;
  49.                    bsp_KeyScan();
  50.               }
  51.              OSTimeDly(1, OS_OPT_TIME_DLY, &err);         
  52.          }
  53.         
  54.          /* 20ms一次触摸扫描,电容触摸屏GT811
  55.             GT811取20ms比较稳定,取10ms偶尔会有跳动。
  56.          */
  57.          if(g_GT811.Enable == 1)
  58.          {
  59.               bsp_KeyScan();
  60.               ucCount1++;
  61.               if(ucCount1 == 2)
  62.               {
  63.                    ucCount1 = 0;
  64.                    GT811_OnePiontScan();
  65.               }
  66.              OSTimeDly(10, OS_OPT_TIME_DLY, &err);     
  67.          }
  68.         
  69.          /* 10ms一次触摸扫描,电容触摸屏FT5X06 */
  70.          if(g_tFT5X06.Enable == 1)
  71.          {
  72.               bsp_KeyScan();
  73.               FT5X06_OnePiontScan();
  74.              OSTimeDly(10, OS_OPT_TIME_DLY, &err);
  75.          }
  76.     }   
  77. }
  78. /*
  79. *********************************************************************************************************
  80. *    函 数 名: AppTaskMsgPro
  81. *    功能说明: 实现截图功能,将图片以BMP格式保存到SD卡中
  82. *    形    参: p_arg 是在创建该任务时传递的形参
  83. *    返 回 值: 无
  84.      优 先 级: 3
  85. *********************************************************************************************************
  86. */
  87. static void AppTaskMsgPro(void *p_arg)
  88. {
  89.      uint32_t ulStart, ulEnd;
  90.      OS_ERR      err;
  91.      uint8_t       Pic_Name = 0;
  92.      char buf[20];
  93.      (void)p_arg;
  94.          
  95.      while(1)
  96.      {   
  97.           /* 等待获取信号量同步消息,接收到后执行串口打印 */
  98.          OSSemPend((OS_SEM *)&SEM_SYNCH,
  99.                      (OS_TICK )0,
  100.                      (OS_OPT  )OS_OPT_PEND_BLOCKING,
  101.                      (CPU_TS  )0,
  102.                      (OS_ERR *)&err);
  103.         
  104.          if(err == OS_ERR_NONE)
  105.          {   
  106.               sprintf(buf,"0:/PicSave/%d.bmp",Pic_Name);
  107.             
  108.               /* 记录截图前起始时间 */
  109.               ulStart = OSTimeGet(&err);
  110.             
  111.               /* 开启调度锁 */
  112.               OSSchedLock(&err);
  113.             
  114.               /* 如果SD卡中没有PicSave文件,会进行创建 */
  115.               result = f_mkdir("0:/PicSave");
  116.             
  117.               /* 创建截图 */
  118.               result = f_open(&file,buf, FA_WRITE|FA_CREATE_ALWAYS);
  119.             
  120.               /* 向SD卡绘制BMP图片 */
  121.               GUI_BMP_Serialize(_WriteByte2File, &file);
  122.             
  123.               /* 创建完成后关闭file */
  124.              result = f_close(&file);
  125.             
  126.               /* 开启调度锁 */
  127.               OSSchedUnlock(&err);
  128.             
  129.               /* 记录截图后时间并获取截图过程耗时 */
  130.               ulEnd = OSTimeGet(&err);
  131.               ulEnd -= ulStart;
  132.             
  133.               App_Printf("截图完成,耗时 = %dms\\r\\n", ulEnd);
  134.               Pic_Name++;      
  135.          }                                                                                                                  
  136.      }  
  137. }
  138. /*
  139. *********************************************************************************************************
  140. *    函 数 名: AppTaskUserIF
  141. *    功能说明: 按键消息处理
  142. *    形    参: p_arg 是在创建该任务时传递的形参
  143. *    返 回 值: 无
  144.      优 先 级: 4
  145. *********************************************************************************************************
  146. */
  147. static void AppTaskUserIF(void *p_arg)
  148. {
  149.      OS_ERR      err;
  150.      uint8_t  ucKeyCode;
  151.    
  152.      (void)p_arg;                /* 避免编译器报警 */
  153.    
  154.      while (1)
  155.      {      
  156.          ucKeyCode = bsp_GetKey();
  157.         
  158.          if (ucKeyCode != KEY_NONE)
  159.          {
  160.               switch (ucKeyCode)
  161.               {
  162.                    case KEY_DOWN_K1:             /* K1键按下 打印任务执行情况 */
  163.                        DispTaskInfo();        
  164.                        break;
  165.                   
  166.                    case KEY_DOWN_K2:             /* K2键按下 向消息队列发送数据 */
  167.                        OSSemPost((OS_SEM *)&SEM_SYNCH,
  168.                                   (OS_OPT  )OS_OPT_POST_1,
  169.                                   (OS_ERR *)&err);
  170.                        break;
  171.                   
  172.                    default:                     /* 其他的键值不处理 */
  173.                        break;
  174.               }
  175.          }
  176.         
  177.          OSTimeDly(20, OS_OPT_TIME_DLY, &err);
  178.      }
  179. }
  180. /*
  181. *********************************************************************************************************
  182. *    函 数 名: AppTaskCom
  183. *    功能说明: 暂未使用
  184. *    形    参: p_arg 是在创建该任务时传递的形参
  185. *    返 回 值: 无
  186.      优 先 级: 5
  187. *********************************************************************************************************
  188. */
  189. static void AppTaskCOM(void *p_arg)
  190. {
  191.      OS_ERR  err;      
  192.    
  193.      (void)p_arg;
  194.    
  195.      while(1)
  196.      {   
  197.          OSTimeDly(500, OS_OPT_TIME_DLY, &err);
  198.      }                                                                                                
  199. }
  200. /*
  201. *********************************************************************************************************
  202. *    函 数 名: AppTaskGUI
  203. *    功能说明: GUI任务,最低优先级                        
  204. *    形    参:p_arg 是在创建该任务时传递的形参
  205. *    返 回 值: 无
  206. *   优 先 级:OS_CFG_PRIO_MAX - 4u
  207. *********************************************************************************************************
  208. */
  209. static void AppTaskGUI(void *p_arg)
  210. {
  211.     (void)p_arg;       /* 避免编译器告警 */
  212.         
  213.      while (1)
  214.      {
  215.          MainTask();
  216.      }
  217. }
复制代码
emWin任务的具体实现(在MainTask.c文件里面):
  1. #include "MainTask.h"
  2. #include "includes.h"
  3. /*
  4. *********************************************************************************************************
  5. *                                      宏定义
  6. *********************************************************************************************************
  7. */
  8. /* 边加载边显示的方式选择      :Method1
  9.    直接加载到SDRAM进行显示选择 :Method2
  10. */
  11. #define Method2
  12. /*
  13. *********************************************************************************************************
  14. *                                    变量
  15. *********************************************************************************************************
  16. */
  17. static char acBuffer[8192];
  18. static GUI_GIF_INFO InfoGif;
  19. static GUI_GIF_IMAGE_INFO ImagInfoGif;
  20. /*
  21. *********************************************************************************************************
  22. *    函 数 名: _GetData
  23. *    功能说明: 被函数GUI_JPEG_DrawEx调用
  24. *    形    参:p             FIL类型数据
  25. *             NumBytesReq   请求读取的字节数
  26. *             ppData        数据指针
  27. *             Off           如果Off = 1,那么将重新从起始位置读取               
  28. *    返 回 值: 返回读取的字节数
  29. *********************************************************************************************************
  30. */
  31. int _GetData(void * p, const U8 ** ppData, unsigned NumBytesReq, U32 Off)
  32. {
  33.      static int FileAddress = 0;
  34.      UINT NumBytesRead;
  35.      FIL *PicFile;
  36.      PicFile = (FIL *)p;
  37.      /*
  38.      * 检测缓存大小
  39.      */
  40.      if (NumBytesReq > sizeof(acBuffer)) {
  41.      NumBytesReq = sizeof(acBuffer);
  42.      }
  43.      /*
  44.      * 设置读取位置
  45.      */
  46.      if(Off == 1) FileAddress = 0;
  47.      else FileAddress = Off;
  48.      result =f_lseek(PicFile, FileAddress);
  49.      /*
  50.      * 读取数据到缓存
  51.      */
  52.      result = f_read(PicFile, acBuffer, NumBytesReq, &NumBytesRead);
  53.      /*
  54.      * 让指针ppData指向读取的数据
  55.      */
  56.      *ppData = (const U8 *)acBuffer;
  57.      /*
  58.      * 返回读取的字节数
  59.      */
  60.      return NumBytesRead;
  61. }
  62. /*
  63. *********************************************************************************************************
  64. *    函 数 名: _ShowGIF1
  65. *    功能说明: 显示GIF片
  66. *    形    参: sFilename 要显示的图片名字
  67. *    返 回 值: 无
  68. *********************************************************************************************************
  69. */
  70. void _ShowGIF1(const char * sFilename)
  71. {
  72.      uint16_t i = 0;
  73.      uint32_t t0, t1;
  74.      char buf[50];
  75.      /* 打开文件 */        
  76.      result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS);
  77.      if (result != FR_OK)
  78.      {
  79.          return;
  80.      }
  81.   
  82.      /* 获取GIF图片信息 */
  83.    GUI_GIF_GetInfoEx(_GetData, &file,&InfoGif);
  84.    
  85.    while(1)
  86.    {
  87.          /* 变量用来设置当前播放的帧数,InfoGif.NumImages是GIF图片总的帧数 */
  88.          if(i < InfoGif.NumImages)
  89.          {
  90.               /* 获取当前帧GIF图片信息,注意第4个参数是从0开始计数的 */
  91.               GUI_GIF_GetImageInfoEx(_GetData, &file, &ImagInfoGif, i);
  92.             
  93.               /* 如果此帧延迟时间是0,默认是延迟100ms */
  94.               if(ImagInfoGif.Delay == 0)
  95.               {
  96.                    GUI_Delay(100);
  97.               }
  98.               else
  99.               {
  100.                    t0 = GUI_GetTime();
  101.                    /* 显示当前播放的帧数 */
  102.                    sprintf(buf, "     Frame:%d/%d     ", i+1, InfoGif.NumImages);
  103.                    GUI_DispStringHCenterAt(buf, LCD_GetXSize()/2, 0);
  104.                   
  105.                    /* 解码并显示此帧GIF图片,注意第5个参数是从0开始计数的 */
  106.                    GUI_GIF_DrawSubEx(_GetData,
  107.                                        &file,
  108.                                        (LCD_GetXSize() - InfoGif.xSize)/2,
  109.                                        (LCD_GetYSize() - InfoGif.ySize)/2,
  110.                                        i++);
  111.                   
  112.                    /* 获取本次解码和显示消耗的时间 */
  113.                    t1 = GUI_GetTime() - t0;
  114.                   
  115.                    /* 如果GIF的解码和显示的时间超时就不做延迟 */
  116.                    if (t1 < ImagInfoGif.Delay * 10)
  117.                    {
  118.                        GUI_Delay(ImagInfoGif.Delay * 10 - t1);
  119.                    }
  120.               }              
  121.          }
  122.          else
  123.          {
  124.              i = 0;
  125.          }
  126.      }
  127.      /* 实际应用中,根据实际情况释放动态内存 */         
  128. //   GUI_ALLOC_Free(hMem);
  129. //   f_close(&file);
  130. }
  131. /*
  132. *********************************************************************************************************
  133. *    函 数 名: _ShowGIF2
  134. *    功能说明: 显示GIF片
  135. *    形    参: sFilename 要显示的图片名字
  136. *    返 回 值: 无
  137. *********************************************************************************************************
  138. */
  139. void _ShowGIF2(const char * sFilename)
  140. {
  141.      uint16_t i = 0;
  142.      uint32_t t0, t1;
  143.      char *_acBuffer;
  144.      GUI_HMEM hMem;
  145.      char buf[50];
  146.      /* 打开文件 */        
  147.      result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS);
  148.      if (result != FR_OK)
  149.      {
  150.          return;
  151.      }
  152.       
  153.      /* 申请一块内存空间 并且将其清零 */
  154.      hMem = GUI_ALLOC_AllocZero(file.fsize);
  155.    
  156.      /* 将申请到内存的句柄转换成指针类型 */
  157.      _acBuffer = GUI_ALLOC_h2p(hMem);
  158.      /* 读取文件到动态内存 */
  159.      result = f_read(&file, _acBuffer, file.fsize, &bw);
  160.      if (result != FR_OK)
  161.      {
  162.          return;
  163.      }
  164.   
  165.      /* 获取GIF图片信息 */
  166.    GUI_GIF_GetInfo(_acBuffer, file.fsize, &InfoGif);
  167.    
  168.    while(1)
  169.    {
  170.          /* 变量用来设置当前播放的帧数,InfoGif.NumImages是GIF图片总的帧数 */
  171.          if(i < InfoGif.NumImages)
  172.          {
  173.               /* 获取当前帧GIF图片信息,注意第4个参数是从0开始计数的 */
  174.              GUI_GIF_GetImageInfo(_acBuffer, file.fsize, &ImagInfoGif, i);
  175.             
  176.               /* 如果此帧延迟时间是0,默认是延迟100ms */
  177.               if(ImagInfoGif.Delay == 0)
  178.               {
  179.                    GUI_Delay(100);
  180.               }
  181.               else
  182.               {
  183.                    t0 = GUI_GetTime();
  184.                    /* 显示当前播放的帧数 */
  185.                    sprintf(buf, "     Frame:%d/%d     ", i+1, InfoGif.NumImages);
  186.                    GUI_DispStringHCenterAt(buf, LCD_GetXSize()/2, 0);
  187.                   
  188.                    /* 解码并显示此帧GIF图片,注意第5个参数是从0开始计数的 */
  189.                    GUI_GIF_DrawSub(_acBuffer,
  190.                                      file.fsize,
  191.                                      (LCD_GetXSize() - InfoGif.xSize)/2,
  192.                                      (LCD_GetYSize() - InfoGif.ySize)/2,
  193.                                      i++);
  194.                   
  195.                    /* 获取本次解码和显示消耗的时间 */
  196.                    t1 = GUI_GetTime() - t0;
  197.                   
  198.                    /* 如果GIF的解码和显示的时间超时就不做延迟 */
  199.                    if (t1 < ImagInfoGif.Delay * 10)
  200.                    {
  201.                        GUI_Delay(ImagInfoGif.Delay * 10 - t1);
  202.                    }
  203.               }              
  204.          }
  205.          else
  206.          {
  207.              i = 0;
  208.          }
  209.      }
  210.      /* 实际应用中,根据实际情况释放动态内存 */         
  211. //   GUI_ALLOC_Free(hMem);
  212. //   f_close(&file);
  213. }
  214. /*
  215. *********************************************************************************************************
  216. *    函 数 名: MainTask
  217. *    功能说明: GUI主函数
  218. *    形    参: 无
  219. *    返 回 值: 无
  220. *********************************************************************************************************
  221. */
  222. void MainTask(void)
  223. {
  224.      /* 初始化 */
  225.      GUI_Init();
  226.    
  227.      /* 设置字体,文本模式和前景色 */
  228.      GUI_SetFont(&GUI_Font24B_ASCII);
  229.      GUI_SetColor(GUI_WHITE);   
  230.    
  231.      /* 设置背景色 */
  232.      GUI_SetBkColor(GUI_BLUE);
  233.      GUI_Clear();
  234.    
  235.      /*
  236.       关于多缓冲和窗口内存设备的设置说明
  237.         1. 使能多缓冲是调用的如下函数,用户要在LCDConf_Lin_Template.c文件中配置了多缓冲,调用此函数才有效:
  238.            WM_MULTIBUF_Enable(1);
  239.         2. 窗口使能使用内存设备是调用函数:WM_SetCreateFlags(WM_CF_MEMDEV);
  240.         3. 如果emWin的配置多缓冲和窗口内存设备都支持,二选一即可,且务必优先选择使用多缓冲,实际使用
  241.            STM32F429BIT6 + 32位SDRAM + RGB565/RGB888平台测试,多缓冲可以有效的降低窗口移动或者滑动时的撕裂
  242.            感,并有效的提高流畅性,通过使能窗口使用内存设备是做不到的。
  243.         4. 所有emWin例子默认是开启三缓冲。
  244.      */
  245.      WM_MULTIBUF_Enable(1);
  246.    
  247.      /*
  248.        触摸校准函数默认是注释掉的,电阻屏需要校准,电容屏无需校准。如果用户需要校准电阻屏的话,执行
  249.         此函数即可,会将触摸校准参数保存到EEPROM里面,以后系统上电会自动从EEPROM里面加载。
  250.      */
  251.     //TOUCH_Calibration();
  252.      /* GIF图片显示方式一:实际项目不推荐,会用即可 */
  253. #if defined Method1
  254.      _ShowGIF1("1.gif");
  255.    
  256.     /* GIF图片显示方式二:实际项目推荐 */
  257. #elif defined Method2
  258.      _ShowGIF2("1.gif");   
  259.    
  260. #endif
  261.    
  262.      /* 如果进入到这里,说明GIF图片加载失败 */
  263.      GUI_DispStringAt("GIF Load failed!!", 0, 0);
  264.    
  265.      while(1)
  266.      {
  267.          GUI_Delay(10);
  268.      }
  269. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-1-12 16:46:59 | 显示全部楼层
18.5 实验例程说明(裸机)


配套例子:
    V6-523_STemWin实验_GIF图片显示(裸机)
实验目的:
    1.     学习emWin的GIF图片显示。
    2.     emWin功能的实现在MainTask.c文件里面。
实验注意:
    1.     本实验主要学习emWin的GIF图片显示功能,实验所需的图片文件1.gif已经存储到本工程的Doc文件夹下,使用此例子前,请务必将此文件存储到SD卡根目录中,并将SD卡插到开发板上面。
STemWin界面显示效果:
    800*480分辨率界面效果。
18.8.png


STemWin动态内存配置:
    GUIConf.c文件中的配置如下:
  1. #define EX_SRAM   1/*1 used extern sram, 0 used internal sram */
  2. #if EX_SRAM
  3. #define GUI_NUMBYTES  (1024*1024*8)
  4. #else
  5. #define GUI_NUMBYTES  (100*1024)
  6. #endif
复制代码
    通过宏定义来配置使用内部SRAM还是外部的SDRAM做为emWin的动态内存,当配置:
#define EX_SRAM    1 表示使用外部SDRAM作为emWin动态内存,大小8MB。
#define EX_SRAM    0 表示使用内部SRAM作为emWin动态内存,大小100KB。
默认情况下,本教程配套的所有emWin例子都是用外部SDRAM作为emWin动态内存。
STemWin底层接口配置:
    LCDConf_Lin_Template.c文件中共12项emWin配置:
  1. /*
  2. **********************************************************************************************************
  3.                                           用户可以配置的选项
  4. **********************************************************************************************************
  5. */
  6. /* 0. 在官方代码的基础上再做优化,官方的部分函数效率低,耗内存, 0表示优化 */
  7. #define emWin_Optimize   0
  8. /*
  9.   1. 显示屏的物理分辨率,驱动已经做了显示屏自适应,支持4.3寸,5寸和7寸屏
  10.      这里填写自适应显示屏中的最大分辨率。
  11. */
  12. #define XSIZE_PHYS       800
  13. #define YSIZE_PHYS       480
  14. /* 2. 多缓冲 / 虚拟屏,多缓冲和虚拟屏不可同时使用,emWin不支持 */
  15. #define NUM_BUFFERS      3 /* 定义多缓冲个数,仅可以设置1,2和3,也就是最大支持三缓冲 */
  16. #define NUM_VSCREENS     1 /* 定义虚拟屏个数 */
  17. /* 3. 没有图层激活时,背景色设置, 暂时未用到 */
  18. #define BK_COLOR         GUI_DARKBLUE
  19. /*
  20.    4. 重定义图层数,对于STM32F429/439,用户可以选择一个图层或者两个图层,不支持三图层
  21.       (1). 设置GUI_NUM_LAYERS = 1时,即仅使用图层1时,默认触摸值是发送给图层1的。
  22.        (2). 设置GUI_NUM_LAYERS = 2时,即图层1和图层2都已经使能,此时图层2是顶层,
  23.             用户需要根据自己的使用情况设置如下两个地方。
  24.             a. 在bsp_touch.c文件中的函数TOUCH_InitHard里面设置参数State.Layer = 1,1就表示
  25.                给图层2发送触摸值。
  26.             b. 调用GUI_Init函数后,调用函数GUI_SelectLayer(1), 设置当前操作的是图层2。
  27. */
  28. #undef  GUI_NUM_LAYERS
  29. #define GUI_NUM_LAYERS    1
  30. /*
  31.    5. 设置图层1和图层2对应的显存地址
  32.       (1) EXT_SDRAM_ADDR 是SDRAM的首地址。
  33.       (2) LCD_LAYER0_FRAME_BUFFER 是图层1的显存地址。
  34.        (3) LCD_LAYER1_FRAME_BUFFER 是图层2的显存地址。
  35.        (4) 每个图层的显存大小比较考究,这里进行下简单的说明。
  36.            如果用户选择的颜色模式 = 32位色ARGB8888,显存的大小:
  37.            XSIZE_PHYS * YSIZE_PHYS * 4 * NUM_VSCREENS * NUM_BUFFERS
  38.          
  39.            颜色模式 = 24位色RGB888,显存的大小:
  40.            XSIZE_PHYS * YSIZE_PHYS * 3 * NUM_VSCREENS * NUM_BUFFERS
  41.          
  42.            颜色模式 = 16位色RGB566,ARGB1555, ARGB4444,AL88,那么显存的大小就是:
  43.            XSIZE_PHYS * YSIZE_PHYS * 2 * NUM_VSCREENS * NUM_BUFFERS
  44.            颜色模式 = 8位色L8,AL44,那么显存的大小就是:
  45.            XSIZE_PHYS * YSIZE_PHYS * 1 * NUM_VSCREENS * NUM_BUFFERS  
  46.      
  47.       这里为了方便起见,将开发板配套的16MB的SDRAM前8MB分配给LCD显存使用,后8MB用于emWin动态内存。
  48.        对于24位色,16位色,8位色,用户可以对其使能三缓冲,并且使能双图层。但是32位色也使能三缓冲和双
  49.        图层的话会超出8MB,所以用户根据自己的情况做显存和emWin动态内存的分配调整。
  50.          举一个例子,对于800*480分辨率的显示屏,使能32位色,三缓冲,那么最终一个图层需要的大小就是
  51.       800 * 480 * 4 * 3  = 4.394MB的空间,如果是双图层,已经超出8MB的分配范围。
  52.      
  53.       (5)为了方便起见,图层2的宏定义LCD_LAYER1_FRAME_BUFFER中的参数4是按照32位色设置的,如果用户的图层1
  54.          使用的是8位色,这里填数字1,如果是16位色,这里填2,如果是24位色,这里填3。
  55. */
  56. #define LCD_LAYER0_FRAME_BUFFER  EXT_SDRAM_ADDR
  57. #define LCD_LAYER1_FRAME_BUFFER  (LCD_LAYER0_FRAME_BUFFER + XSIZE_PHYS * YSIZE_PHYS * 4 * NUM_VSCREENS *
  58. NUM_BUFFERS)
  59. /*
  60.    6. STM32F429/439支持的颜色模式,所有模式都支持,用户可任意配置。
  61.       特别注意如下两个问题:
  62.        (1) 如果用户选择了ARGB8888或者RGB888模式,LCD闪烁比较厉害的话,
  63.            请降低LTDC的时钟大小,在文件bsp_tft_429.c的函数LCD_ConfigLTDC里面设置。
  64.            a. 一般800*480分辨率的显示屏,ARGB8888或者RGB888模式LTDC时钟选择10-20MHz即可。
  65.            b. 480*272分辨率的可以高些,取20MHz左右即可。
  66.        (2) 16位色或者8位色模式,LTDC的时钟频率一般可以比24位色或者32位色的高一倍。
  67. */
  68. #define _CM_ARGB8888      1
  69. #define _CM_RGB888        2
  70. #define _CM_RGB565        3
  71. #define _CM_ARGB1555      4
  72. #define _CM_ARGB4444      5
  73. #define _CM_L8            6
  74. #define _CM_AL44          7
  75. #define _CM_AL88          8
  76. /* 7. 配置图层1的颜色模式和分辨率大小 */
  77. #define COLOR_MODE_0      _CM_RGB565
  78. #define XSIZE_0           XSIZE_PHYS
  79. #define YSIZE_0           YSIZE_PHYS
  80. /* 8. 配置图层2的的颜色模式和分辨率大小 */
  81. #define COLOR_MODE_1      _CM_RGB565
  82. #define XSIZE_1           XSIZE_PHYS
  83. #define YSIZE_1           YSIZE_PHYS
  84. /* 9. 单图层情况下,根据用户选择的颜色模式可自动选择图层1的emWin的驱动和颜色模式 */
  85. #if   (COLOR_MODE_0 == _CM_ARGB8888)
  86.   #define COLOR_CONVERSION_0 GUICC_M8888I
  87.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_32
  88. #elif (COLOR_MODE_0 == _CM_RGB888)
  89.   #define COLOR_CONVERSION_0 GUICC_M888
  90.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_24
  91. #elif (COLOR_MODE_0 == _CM_RGB565)
  92.   #define COLOR_CONVERSION_0 GUICC_M565
  93.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_16
  94. #elif (COLOR_MODE_0 == _CM_ARGB1555)
  95.   #define COLOR_CONVERSION_0 GUICC_M1555I
  96.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_16
  97. #elif (COLOR_MODE_0 == _CM_ARGB4444)
  98.   #define COLOR_CONVERSION_0 GUICC_M4444I
  99.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_16
  100. #elif (COLOR_MODE_0 == _CM_L8)
  101.   #define COLOR_CONVERSION_0 GUICC_8666
  102.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_8
  103. #elif (COLOR_MODE_0 == _CM_AL44)
  104.   #define COLOR_CONVERSION_0 GUICC_1616I
  105.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_8
  106. #elif (COLOR_MODE_0 == _CM_AL88)
  107.   #define COLOR_CONVERSION_0 GUICC_88666I
  108.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_16
  109. #else
  110.   #error Illegal color mode 0!
  111. #endif
  112. /* 10. 双图层情况下,根据用户选择的颜色模式可自动选择图层2的emWin的驱动和颜色模式 */
  113. #if (GUI_NUM_LAYERS > 1)
  114. #if   (COLOR_MODE_1 == _CM_ARGB8888)
  115.   #define COLOR_CONVERSION_1 GUICC_M8888I
  116.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_32
  117. #elif (COLOR_MODE_1 == _CM_RGB888)
  118.   #define COLOR_CONVERSION_1 GUICC_M888
  119.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_24
  120. #elif (COLOR_MODE_1 == _CM_RGB565)
  121.   #define COLOR_CONVERSION_1 GUICC_M565
  122.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_16
  123. #elif (COLOR_MODE_1 == _CM_ARGB1555)
  124.   #define COLOR_CONVERSION_1 GUICC_M1555I
  125.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_16
  126. #elif (COLOR_MODE_1 == _CM_ARGB4444)
  127.   #define COLOR_CONVERSION_1 GUICC_M4444I
  128.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_16
  129. #elif (COLOR_MODE_1 == _CM_L8)
  130.   #define COLOR_CONVERSION_1 GUICC_8666
  131.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_8
  132. #elif (COLOR_MODE_1 == _CM_AL44)
  133.   #define COLOR_CONVERSION_1 GUICC_1616I
  134.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_8
  135. #elif (COLOR_MODE_1 == _CM_AL88)
  136.   #define COLOR_CONVERSION_1 GUICC_88666I
  137.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_16
  138. #else
  139.   #error Illegal color mode 1!
  140. #endif
  141. #else
  142. #undef XSIZE_0
  143. #undef YSIZE_0
  144. #define XSIZE_0       XSIZE_PHYS
  145. #define YSIZE_0       YSIZE_PHYS
  146. #endif
  147. /*11. 配置选项检测,防止配置错误或者某些选项没有配置 */
  148. #ifndef   XSIZE_PHYS
  149.   #error Physical X size of display is not defined!
  150. #endif
  151. #ifndef   YSIZE_PHYS
  152.   #error Physical Y size of display is not defined!
  153. #endif
  154. #ifndef   NUM_VSCREENS
  155.   #define NUM_VSCREENS 1
  156. #else
  157.   #if (NUM_VSCREENS <= 0)
  158.     #error At least one screeen needs to be defined!
  159.   #endif
  160. #endif
  161. #if (NUM_VSCREENS > 1) && (NUM_BUFFERS > 1)
  162.   #error Virtual screens and multiple buffers are not allowed!
  163. #endif
复制代码
    对于这12个配置选项,注释说明已经比较详细。默认情况下,本教程配套的emWin例子都是用的三缓冲,RGB565格式,且仅使用单图层。
程序设计:
栈大小分配:
    系统栈大小在startup_stm32f429_439xx.s文件中配置:
18.9.png

    栈大小的单位是字节,那么这里配置的系统栈大小就是8192字节。
主函数初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main (void)
  10. {   
  11.      /* 初始化外设 */
  12.      bsp_Init();
  13.      /* 进入emWin主函数 */
  14.      MainTask();
  15. }
复制代码
硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 使能CRC 因为使用STemWin前必须要使能 */
  17.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_CRC, ENABLE);
  18.    
  19.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  20.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  21.    
  22.      SystemCoreClockUpdate();    /* 根据PLL配置更新系统时钟频率变量 SystemCoreClock */
  23.      bsp_InitUart();        /* 初始化串口 */
  24.      bsp_InitKey();         /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */
  25.    
  26.      bsp_InitExtIO();       /* FMC总线上扩展了32位输出IO, 操作LED等外设必须初始化 */
  27.      bsp_InitLed();         /* 初始LED指示灯端口 */
  28.    
  29.      bsp_InitI2C();         /* 配置I2C总线 */
  30.    
  31.      bsp_InitExtSDRAM();   /* 初始化SDRAM */
  32.      bsp_DetectLcdType();  /* 检测触摸板和LCD面板型号, 结果存在全局变量 g_TouchType, g_LcdType */
  33.    
  34.      TOUCH_InitHard();    /* 初始化配置触摸芯片 */
  35.      LCD_ConfigLTDC();     /* 初始化配置LTDC */
  36.    
  37.      result = f_mount(&fs, "0:/", 0);     /* 挂载文件系统 */
  38. }
复制代码
emWin功能的具体实现(在MainTask.c文件里面):
  1. #include "MainTask.h"
  2. #include "bsp.h"
  3. /*
  4. *********************************************************************************************************
  5. *                                      宏定义
  6. *********************************************************************************************************
  7. */
  8. /* 边加载边显示的方式选择      :Method1
  9.    直接加载到SDRAM进行显示选择 :Method2
  10. */
  11. #define Method2
  12. /*
  13. *********************************************************************************************************
  14. *                                    变量
  15. *********************************************************************************************************
  16. */
  17. static char acBuffer[8192];
  18. static GUI_GIF_INFO InfoGif;
  19. static GUI_GIF_IMAGE_INFO ImagInfoGif;
  20. /*
  21. *********************************************************************************************************
  22. *    函 数 名: _GetData
  23. *    功能说明: 被函数GUI_JPEG_DrawEx调用
  24. *    形    参:p             FIL类型数据
  25. *             NumBytesReq   请求读取的字节数
  26. *             ppData        数据指针
  27. *             Off           如果Off = 1,那么将重新从起始位置读取               
  28. *    返 回 值: 返回读取的字节数
  29. *********************************************************************************************************
  30. */
  31. int _GetData(void * p, const U8 ** ppData, unsigned NumBytesReq, U32 Off)
  32. {
  33.      static int FileAddress = 0;
  34.      UINT NumBytesRead;
  35.      FIL *PicFile;
  36.      PicFile = (FIL *)p;
  37.      /*
  38.      * 检测缓存大小
  39.      */
  40.      if (NumBytesReq > sizeof(acBuffer)) {
  41.      NumBytesReq = sizeof(acBuffer);
  42.      }
  43.      /*
  44.      * 设置读取位置
  45.      */
  46.      if(Off == 1) FileAddress = 0;
  47.      else FileAddress = Off;
  48.      result =f_lseek(PicFile, FileAddress);
  49.      /*
  50.      * 读取数据到缓存
  51.      */
  52.      result = f_read(PicFile, acBuffer, NumBytesReq, &NumBytesRead);
  53.      /*
  54.      * 让指针ppData指向读取的数据
  55.      */
  56.      *ppData = (const U8 *)acBuffer;
  57.      /*
  58.      * 返回读取的字节数
  59.      */
  60.      return NumBytesRead;
  61. }
  62. /*
  63. *********************************************************************************************************
  64. *    函 数 名: _ShowGIF1
  65. *    功能说明: 显示GIF片
  66. *    形    参: sFilename 要显示的图片名字
  67. *    返 回 值: 无
  68. *********************************************************************************************************
  69. */
  70. void _ShowGIF1(const char * sFilename)
  71. {
  72.      uint16_t i = 0;
  73.      uint32_t t0, t1;
  74.      char buf[50];
  75.      /* 打开文件 */        
  76.      result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS);
  77.      if (result != FR_OK)
  78.      {
  79.          return;
  80.      }
  81.   
  82.      /* 获取GIF图片信息 */
  83.    GUI_GIF_GetInfoEx(_GetData, &file,&InfoGif);
  84.    
  85.    while(1)
  86.    {
  87.          /* 变量用来设置当前播放的帧数,InfoGif.NumImages是GIF图片总的帧数 */
  88.          if(i < InfoGif.NumImages)
  89.          {
  90.               /* 获取当前帧GIF图片信息,注意第4个参数是从0开始计数的 */
  91.               GUI_GIF_GetImageInfoEx(_GetData, &file, &ImagInfoGif, i);
  92.             
  93.               /* 如果此帧延迟时间是0,默认是延迟100ms */
  94.               if(ImagInfoGif.Delay == 0)
  95.               {
  96.                    GUI_Delay(100);
  97.               }
  98.               else
  99.               {
  100.                    t0 = GUI_GetTime();
  101.                    /* 显示当前播放的帧数 */
  102.                    sprintf(buf, "     Frame:%d/%d     ", i+1, InfoGif.NumImages);
  103.                    GUI_DispStringHCenterAt(buf, LCD_GetXSize()/2, 0);
  104.                   
  105.                    /* 解码并显示此帧GIF图片,注意第5个参数是从0开始计数的 */
  106.                    GUI_GIF_DrawSubEx(_GetData,
  107.                                        &file,
  108.                                        (LCD_GetXSize() - InfoGif.xSize)/2,
  109.                                        (LCD_GetYSize() - InfoGif.ySize)/2,
  110.                                        i++);
  111.                   
  112.                    /* 获取本次解码和显示消耗的时间 */
  113.                    t1 = GUI_GetTime() - t0;
  114.                   
  115.                    /* 如果GIF的解码和显示的时间超时就不做延迟 */
  116.                    if (t1 < ImagInfoGif.Delay * 10)
  117.                    {
  118.                        GUI_Delay(ImagInfoGif.Delay * 10 - t1);
  119.                   }
  120.               }              
  121.          }
  122.          else
  123.          {
  124.              i = 0;
  125.          }
  126.      }
  127.      /* 实际应用中,根据实际情况释放动态内存 */         
  128. //   GUI_ALLOC_Free(hMem);
  129. //   f_close(&file);
  130. }
  131. /*
  132. *********************************************************************************************************
  133. *    函 数 名: _ShowGIF2
  134. *    功能说明: 显示GIF片
  135. *    形    参: sFilename 要显示的图片名字
  136. *    返 回 值: 无
  137. *********************************************************************************************************
  138. */
  139. void _ShowGIF2(const char * sFilename)
  140. {
  141.      uint16_t i = 0;
  142.      uint32_t t0, t1;
  143.      char *_acBuffer;
  144.      GUI_HMEM hMem;
  145.      char buf[50];
  146.      /* 打开文件 */        
  147.      result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS);
  148.      if (result != FR_OK)
  149.      {
  150.          return;
  151.      }
  152.       
  153.      /* 申请一块内存空间 并且将其清零 */
  154.      hMem = GUI_ALLOC_AllocZero(file.fsize);
  155.    
  156.      /* 将申请到内存的句柄转换成指针类型 */
  157.      _acBuffer = GUI_ALLOC_h2p(hMem);
  158.      /* 读取文件到动态内存 */
  159.      result = f_read(&file, _acBuffer, file.fsize, &bw);
  160.      if (result != FR_OK)
  161.      {
  162.          return;
  163.      }
  164.   
  165.      /* 获取GIF图片信息 */
  166.    GUI_GIF_GetInfo(_acBuffer, file.fsize, &InfoGif);
  167.    
  168.    while(1)
  169.    {
  170.          /* 变量用来设置当前播放的帧数,InfoGif.NumImages是GIF图片总的帧数 */
  171.          if(i < InfoGif.NumImages)
  172.          {
  173.               /* 获取当前帧GIF图片信息,注意第4个参数是从0开始计数的 */
  174.              GUI_GIF_GetImageInfo(_acBuffer, file.fsize, &ImagInfoGif, i);
  175.             
  176.               /* 如果此帧延迟时间是0,默认是延迟100ms */
  177.               if(ImagInfoGif.Delay == 0)
  178.               {
  179.                    GUI_Delay(100);
  180.               }
  181.               else
  182.               {
  183.                    t0 = GUI_GetTime();
  184.                    /* 显示当前播放的帧数 */
  185.                    sprintf(buf, "     Frame:%d/%d     ", i+1, InfoGif.NumImages);
  186.                    GUI_DispStringHCenterAt(buf, LCD_GetXSize()/2, 0);
  187.                   
  188.                    /* 解码并显示此帧GIF图片,注意第5个参数是从0开始计数的 */
  189.                    GUI_GIF_DrawSub(_acBuffer,
  190.                                      file.fsize,
  191.                                      (LCD_GetXSize() - InfoGif.xSize)/2,
  192.                                      (LCD_GetYSize() - InfoGif.ySize)/2,
  193.                                      i++);
  194.                   
  195.                    /* 获取本次解码和显示消耗的时间 */
  196.                    t1 = GUI_GetTime() - t0;
  197.                   
  198.                    /* 如果GIF的解码和显示的时间超时就不做延迟 */
  199.                    if (t1 < ImagInfoGif.Delay * 10)
  200.                    {
  201.                        GUI_Delay(ImagInfoGif.Delay * 10 - t1);
  202.                    }
  203.               }              
  204.          }
  205.          else
  206.          {
  207.              i = 0;
  208.          }
  209.      }
  210.      /* 实际应用中,根据实际情况释放动态内存 */         
  211. //   GUI_ALLOC_Free(hMem);
  212. //   f_close(&file);
  213. }
  214. /*
  215. *********************************************************************************************************
  216. *    函 数 名: MainTask
  217. *    功能说明: GUI主函数
  218. *    形    参: 无
  219. *    返 回 值: 无
  220. *********************************************************************************************************
  221. */
  222. void MainTask(void)
  223. {
  224.      /* 初始化 */
  225.      GUI_Init();
  226.    
  227.      /* 设置字体,文本模式和前景色 */
  228.      GUI_SetFont(&GUI_Font24B_ASCII);
  229.      GUI_SetColor(GUI_WHITE);   
  230.    
  231.      /* 设置背景色 */
  232.      GUI_SetBkColor(GUI_BLUE);
  233.      GUI_Clear();
  234.    
  235.      /*
  236.       关于多缓冲和窗口内存设备的设置说明
  237.         1. 使能多缓冲是调用的如下函数,用户要在LCDConf_Lin_Template.c文件中配置了多缓冲,调用此函数才有效:
  238.            WM_MULTIBUF_Enable(1);
  239.         2. 窗口使能使用内存设备是调用函数:WM_SetCreateFlags(WM_CF_MEMDEV);
  240.         3. 如果emWin的配置多缓冲和窗口内存设备都支持,二选一即可,且务必优先选择使用多缓冲,实际使用
  241.            STM32F429BIT6 + 32位SDRAM + RGB565/RGB888平台测试,多缓冲可以有效的降低窗口移动或者滑动时的撕裂
  242.            感,并有效的提高流畅性,通过使能窗口使用内存设备是做不到的。
  243.         4. 所有emWin例子默认是开启三缓冲。
  244.      */
  245.      WM_MULTIBUF_Enable(1);
  246.    
  247.      /*
  248.        触摸校准函数默认是注释掉的,电阻屏需要校准,电容屏无需校准。如果用户需要校准电阻屏的话,执行
  249.         此函数即可,会将触摸校准参数保存到EEPROM里面,以后系统上电会自动从EEPROM里面加载。
  250.      */
  251.     //TOUCH_Calibration();
  252.      /* GIF图片显示方式一:实际项目不推荐,会用即可 */
  253. #if defined Method1
  254.      _ShowGIF1("1.gif");
  255.    
  256.     /* GIF图片显示方式二:实际项目推荐 */
  257. #elif defined Method2
  258.      _ShowGIF2("1.gif");   
  259.    
  260. #endif
  261.    
  262.      /* 如果进入到这里,说明GIF图片加载失败 */
  263.      GUI_DispStringAt("GIF Load failed!!", 0, 0);
  264.    
  265.      while(1)
  266.      {
  267.          GUI_Delay(10);
  268.      }
  269. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-1-12 16:47:23 | 显示全部楼层
18.6总结


    对于比较简单的GIF图片,使用本章节提供的两种方法都是可以的,显示效果差不多,但是对于界面效果稍复杂些的GIF图片,推荐将整个GIF图片加载的SDRAM或者SRAM中再显示,效果比较好。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-16 20:51 , Processed in 0.276278 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表