技术标签: 第3版emWin教程
教程不断更新中:http://www.armbbs.cn/forum.php?mod=viewthread&tid=98429
本期主要讲emWin支持的GIF图片显示,官方支持的主要有两种显示方法,一种方法是直接从外部存储器读取数据并显示,这种方法的好处就是不需要大的RAM,每次读取一些数据显示一次,缺点就是显示速度稍慢。另一种是从外部存储器读取整个图片到RAM(比如内部SRAM,外部SRAM或者外部SDRAM),然后再显示图片,这种方法的显示速度要稍快些。对于界面效果简单的GIF图片,两种方法区别不大,界面效果稍复杂的GIF图片,推荐后者,即将整个图片读到RAM空间,显示效果要好些。
目录
1、 GIF图片显示的所有API函数在emWin手册中都有讲解,下图是中文版手册里面API函数的位置
下图是英文版手册里面API函数的位置:
2、 本章教程使用的外部存储器是SD卡,实际项目中使用任何其它类型的存储器都可以的,支不支持文件系统都没有关系的,使用方法与本章教程一样,用户要做的就是把图片从外部存储器读出即可。
关于GIF图片格式方面的知识,推荐大家看wiki百科上面的介绍:
推荐初学者了解一下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格式普遍适用于图表,按钮等等只需少量颜色的图像(如黑白照片)。
当前emWin支持的API函数有如下16个:
从上面的表格中可以看出,emWin支持GIF文件显示主要有两种类型的函数,一类是以Ex结尾的函数,这种函数显示GIF图片是一边从外部存储器加载数据一边显示,显示速度相对较慢,适用于内存较小的场合。另一类是不以Ex结尾的函数,这种函数直接从指定的地址读取数据进行显示(注意,这里的地址需是总线式地址,比如外部SDRAM,外部SRAM,内部Flash和内部SRAM都可以),显示速度相对稍快。
本章教程会对这两种方式都进行说明:
此函数直接从地址pGIF读取第Index张(注意,这里的Index就是指的参数Index所指定的子图片序号,这个参数是从0开始计数的)GIF子图片的数据,将图片显示到用户设置的位置(x0, y0)。
此函数通过其回调函数pfGetData读取第Index张(注意,这里的Index就是指的参数Index所指定的子图片序号,这个参数是从0开始计数的)GIF子图片的数据,从而实现边读取图片数据边显示的功能,将图片显示到用户设置的位置(x0, y0)。
实现GIF图片的动态显示还要用如下四个函数配合:
此函数直接从地址pGIF读取GIF文件数据,返回GIF图片的长度,宽度以及子图片的张数。返回结果是存储到第三个参数所指向的结构体变量地址。
此函数通过其回调函数pfGetData读取图片数据,返回GIF图片的长度,宽度以及子图片的张数。返回结果是存储到第三个参数所指向的结构体变量地址。结构体GUI_GIF_INFO的定义如下(函数GUI_GIF_GetInfo和函数GUI_GIF_GetInfoEx的结构体类型是一样的):
这个里面有个错误:不是XSize和YSize,而是xSize和ySize(这个错误官方手册一直没有更正)。
此函数直接从地址pGIF读取GIF文件数据,返回上次(注意,这里的上次就是指的参数Index所指定的子图片序号,这个参数是从0开始计数的)所绘制的子图片显示位置,长度,宽度和显示多长时间。其中显示多少时间的参数比较有用,专门用于设置各个子图片之间的时间间隔。返回结果是存储到第三个参数所指向的结构体变量地址。
GUI_GIF_IMAGE_INFO * pInfo, int Index);
此函数通过其回调函数pfGetData读取图片数据,返回上次(注意,这里的上次就是指的参数Index所指定的子图片序号,这个参数是从0开始计数的)所绘制的子图片显示位置,长度,宽度和显示多长时间。其中显示多少时间的参数比较有用,专门用于设置时间各个子图片之间的时间间隔。返回结果是存储到第三个参数所指向的结构体变量地址。结构体GUI_GIF_IMAGE_INFO的定义如下(函数GUI_GIF_GetImageInfo和GUI_GIF_GetImageInfoEx的结构体类型是一样的):
绘制加载到存储器的GIF图片主要是通过函数GUI_GIF_DrawSub来实现,下面我们分2步来说明如何将SD卡中的GIF图片显示到LCD上面。
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; }
/* ********************************************************************************************************* * 函 数 名: _ShowGIF2 * 功能说明: 显示GIF片 * 形 参: sFilename 要显示的图片名字 * 返 回 值: 无 ********************************************************************************************************* */ void _ShowGIF2(const char * sFilename) { uint16_t i = 0; uint32_t t0, t1; char *_acBuffer; GUI_HMEM hMem; char buf[50]; /* 打开文件 */ result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS); if (result != FR_OK) { return; } /* 申请一块内存空间 并且将其清零 */ 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; } /* 获取GIF图片信息 */ GUI_GIF_GetInfo(_acBuffer, file.obj.objsize, &InfoGif); while(1) { /* 变量用来设置当前播放的帧数,InfoGif.NumImages是GIF图片总的帧数 */ if(i < InfoGif.NumImages) { /* 获取当前帧GIF图片信息,注意第4个参数是从0开始计数的 */ GUI_GIF_GetImageInfo(_acBuffer, file.obj.objsize, &ImagInfoGif, i); /* 如果此帧延迟时间是0,默认是延迟100ms */ if(ImagInfoGif.Delay == 0) { GUI_Delay(100); } else { t0 = GUI_GetTime(); /* 显示当前播放的帧数 */ sprintf(buf, " Frame:%d/%d ", i+1, InfoGif.NumImages); GUI_DispStringHCenterAt(buf, LCD_GetXSize()/2, 0); /* 解码并显示此帧GIF图片,注意第5个参数是从0开始计数的 */ GUI_GIF_DrawSub(_acBuffer, file.obj.objsize, (LCD_GetXSize() - InfoGif.xSize)/2, (LCD_GetYSize() - InfoGif.ySize)/2, i++); /* 获取本次解码和显示消耗的时间 */ t1 = GUI_GetTime() - t0; /* 如果GIF的解码和显示的时间超时就不做延迟 */ if (t1 < ImagInfoGif.Delay * 10) { GUI_Delay(ImagInfoGif.Delay * 10 - t1); } } } else { i = 0; } } /* 实际应用中,根据实际情况释放动态内存 */ // GUI_ALLOC_Free(hMem); // f_close(&file); }
通过上面两步就完成了GIF图片的动态显示,用户要显示哪个图片,调用函数_ShowGIF2()即可,比如要显示1.gif图片,可以调用_ShowGIF2("1.gif"),这种方式显示GIF图片相对稍快些。实际显示效果参看本章节配套的实验例程说明。
绘制无需加载到存储器的GIF图片主要是通过函数GUI_GIF_DrawSubEx来实现,这种方式的优点是需要的内存小,但是显示速度稍慢。下面我们分2步来说明如何将SD卡中的GIF图片显示到LCD上面。
/* ********************************************************************************************************* * 函 数 名: _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; } /* ********************************************************************************************************* * 函 数 名: _ShowGIF1 * 功能说明: 显示GIF片 * 形 参: sFilename 要显示的图片名字 * 返 回 值: 无 ********************************************************************************************************* */ void _ShowGIF1(const char * sFilename) { uint16_t i = 0; uint32_t t0, t1; char buf[50]; /* 打开文件 */ result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS); if (result != FR_OK) { return; } /* 获取GIF图片信息 */ GUI_GIF_GetInfoEx(_GetData, &file,&InfoGif); while(1) { /* 变量用来设置当前播放的帧数,InfoGif.NumImages是GIF图片总的帧数 */ if(i < InfoGif.NumImages) { /* 获取当前帧GIF图片信息,注意第4个参数是从0开始计数的 */ GUI_GIF_GetImageInfoEx(_GetData, &file, &ImagInfoGif, i); /* 如果此帧延迟时间是0,默认是延迟100ms */ if(ImagInfoGif.Delay == 0) { GUI_Delay(100); } else { t0 = GUI_GetTime(); /* 显示当前播放的帧数 */ sprintf(buf, " Frame:%d/%d ", i+1, InfoGif.NumImages); GUI_DispStringHCenterAt(buf, LCD_GetXSize()/2, 0); /* 解码并显示此帧GIF图片,注意第5个参数是从0开始计数的 */ GUI_GIF_DrawSubEx(_GetData, &file, (LCD_GetXSize() - InfoGif.xSize)/2, (LCD_GetYSize() - InfoGif.ySize)/2, i++); /* 获取本次解码和显示消耗的时间 */ t1 = GUI_GetTime() - t0; /* 如果GIF的解码和显示的时间超时就不做延迟 */ if (t1 < ImagInfoGif.Delay * 10) { GUI_Delay(ImagInfoGif.Delay * 10 - t1); } } } else { i = 0; } } /* 实际应用中,根据实际情况释放动态内存 */ // GUI_ALLOC_Free(hMem); // f_close(&file); }
通过上面2步就完成了GIF图片的动态显示,这种方式显示GIF图片速度稍慢,实际显示效果参看本章节配套的实验例程说明。
配套例子:
V7-526_emWin6.x实验_GIF图片显示(RTOS)
实验目的:
实验注意:
实验内容:
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分辨率界面效果。
配套例子:
V7-525_emWin6.x实验_GIF图片显示(裸机)
实验目的:
实验注意:
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动态内存。
对于比较简单的GIF图片,使用本章节提供的两种方法都是可以的,显示效果差不多,但是对于界面效果稍复杂些的GIF图片,推荐将整个GIF图片加载的SDRAM或者SRAM中再显示,效果比较好。
文章浏览阅读1k次。题目描述定义并调用函数countdigit(number, digit),它的功能是统计整数number中数字digit的个数,如countdigit(10090, 0)的返回值是3. 在主函数中定义并调用该函数,统计任意一个输入整数中某数字的个数。输入输入为多组测试数据。输入2个非负整数,分别是n(0 <= n <= 10000000000)和m(0 <= m <=9)输出统计出数字n中m数字的个数样例输入Copy102 2样例输出Copy.._零基础学c/c++168——统计数字
文章浏览阅读4.6k次,点赞9次,收藏45次。CanSM状态机在一级层面上分为:NO_COMMUNICATION, PRE_NO_COMMUNICATION, WAKEUP_VALIDATION,PRE_FULL_COMMUNICATION,FULL_COMMUNICATION,SLIENT_COMMUNICATION,CHANGE_BAUDRATE,7个子状态;CanSM的作用就是根据发生的事件或者根据API调用触发状态机流转,需要时并通知ComM、BswM等模块网络状态;CanSM关于状态机的触发流转,全部记住是几乎不可能的,只要了解即可。_cansm
文章浏览阅读329次。输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数。_输入一行字符,统计英文字母、空格、数字
文章浏览阅读3.1k次,点赞3次,收藏14次。通过此篇可以了解EBGP邻居关系的建立以及了解wireark对EBGP邻居报文的分析。默认情况下,eBGP(外部 BGP)需要两台路由器相互直接连接,以建立邻居邻接关系,这是因为 eBGP 路由器对其 BGP 数据包使用的TTL 为 1,当 BGP 邻居超过一跳时,TTL 将递减为 0 并被丢弃。当这两个路由器没有直接连接时,仍然可以使其工作,但必须使用multihop,此情形不适用于iBGP。下面是一个例子:上面我们将尝试在 R1 和 R3 之间配置 eBGP,由于 R2 位于中间,这_bgp多跳
文章浏览阅读2.3k次,点赞14次,收藏11次。由于被认为是客户端对错误(例如:畸形的请求语法、无效的请求信息帧或者虚拟的请求路由),服务器无法或不会处理当前请求。发现其中存在日期类型属性,所以可能是由于ajax传递的参数与数据库中的类型发生冲突,导致访问错误。(1)语义有误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。在实现向数据库中添加记录时,请求发送无效,参数也未传递到控制类。当然,其他属性也可能存在格式冲突的问题,对其进行相应的标注即可。经过检查,相应的路径映射确实没有问题。(2)请求参数有误。至此,问题成功解决。_由于被认为是客户端对错误(例如:畸形的请求语法、无效的请求信息帧或者虚拟的请求
文章浏览阅读379次。python学习:闭包教程_python闭包管理
文章浏览阅读5.9k次,点赞7次,收藏21次。给树莓派pip更换一个国内的源,下载python库起来快一点。网上有很多博客都是互相抄,而且全是错的。无奈去pip官网找到了更改源的方法,在此记录下来,方便新手查看。官方网址如下:https://pip.pypa.io/en/stable/user_guide/#searching-for-packages首先可以安装一个查看当前源的工具pip install pipsource查看当前有..._pip source
文章浏览阅读420次。Python字符串的插入操作传送门:http://acm.tzc.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=3448时间限制(普通/Java):1000MS/3000MS 内存限制:65536KByte描述给定用等号连接的两个整数,如“1234=127”。问能否在左边的整数中间某个位置..._python趣味填空1234=127
文章浏览阅读2.2k次。http://www.hello-code.com/news/daily/201607/18939.htmlDocker在上周的DockerCon技术大会上发布了1.12版核心产品Docker Engine,最大的新特性是Docker Swarm已经被整合到了Docker Engine里面而不再是一个单独的工具了,这样就可以更容易的把多个Docker主机组合成一整个规模更大可靠性更高的逻辑_docker engine 比较
文章浏览阅读2.8k次,点赞5次,收藏31次。小目标检测的定义、难点及解决方法_目标检测小目标定义
文章浏览阅读298次。fl studio 21中文版具备直观的界面和强大的编辑功能,使您能够轻松地调整和精确控制音频轨道。您可以实时录制、编辑和处理多个音轨,轻松实现混音和编曲。无论您是专业音乐制作人还是初学者,都能满足您的需求,并帮助您实现创作梦想。该软件还支持多种音频格式的导入和导出,方便与其他音频软件进行兼容和交互。您可以将您的作品保存为常见的音频文件格式,如MP3、WAV、FLAC等,方便在不同平台上分享和播放。_fl studio21
文章浏览阅读2.2k次,点赞2次,收藏6次。最近在学习ES数据库,所以将一些东西记录一下。以下所有的都是基于es7.8.0版本进行的下载安装ES数据库安装本体下载地址 :linuxmac oswindowses的安装非常简单,基本都是解压然后运行就行了。这里我们就以linux版本为例子# 新建一个文件夹mkdir elasticSearch# 进入文件夹cd elasticSearch/# 下载安装包wget https://artifacts.elastic.co/downloads/elasticsearch/elas_wget 127.0.0.1:9200