ALIENTEK MINISTM32 扩展实验13 内部FLASH图片显示实验_AN1103(告诉你如何用LCD显示image2lcd生成的图像数据)

2019-07-20 22:07发布

 AN1103 内部FLASH图片显示 本应用文档(AN1103)将向大家展示,如何将直接存放在STM32内部FLASH的图片数据(解码后的数据)显示到LCD.以及介绍图片数据生成工具image2lcd V2.9的一些用法。本实验以ALIENTEK MiniSTM32开发板为实验对象。 本文档分为如下几部分: 1,              图片显示原理。 2,              Image2lcd简介。 3,              软件实现。   一,图片显示原理。 LCD上显示图片无非就是画点。原理就这么简单。画点需要2个要素:1,坐标。2,颜 {MOD}。知道了这两个要素,画图就简单了,一副图片在LCD上显示出来,我们只需要在正确的位置写入正确的颜 {MOD}即可。就像你用铅笔在本子上画图一样,本子就是LCD,铅笔就是你的画点函数。 图片显示另外一个重要的特点就是他的数据量很大,比如画一幅320*240的图像,以16位 {MOD}计算,那么光颜 {MOD}的数据量就有:320*240*2=153600字节。这其中还不包括设置坐标的过程,如果加上坐标设置,数据量就是颜 {MOD}数据量的5倍(每次坐标设置需要发送5次命令/数据)以上。所以尽量优化画点过程,才能使你的图片显示得流畅。 单纯的画点,显然无法做太多优化,因为坐标设置是必须的。幸好,我们ALIENTEK所使用的液晶都是支持开窗显示以及坐标自增显示的。这样,我们只需要设置一次窗口,然后设置一次坐标,就可以不停的往LCD写颜 {MOD}数据,而不需要再做地址设置了。这样可以使得速度比单纯的画点显示要快至少5倍以上。 开窗也有几个条件:1,窗大小。2GRAM自增方向(就是扫描方向)。 开窗的概念:如图1所示:


1 我们本来的液晶是分辨率是240*320.对图10X00~0XEFx坐标),0X00~0X13Fy坐标)。图一中我们开辟了一个灰 {MOD}区域的窗口,它的范围为XSTA~XENDYSTA~YEND。这样我们开辟窗口以后,再往LCD写数据,它就只会在这个窗口范围内地址按照设定的方向自增。比如从左到右,从上到下的扫描方式,规律为:从(XSTA,YSTA)开始,x坐标递增,每次遇到XEND地址则y坐标增1,同时x坐标重设为XSTA,直到y坐标递增到大于YEND,此时坐标又变为 (XSTA,YSTA) 所以,只要我们预先知道图片数据的生成格式,以及图片尺寸,那么我们就可以采用开窗方式来画图,从而提高效率。
  二,Image2lcd简介。 Image2Lcd 是一款非常好的图像工具软件,它能把各种来源的图片转换成特定的数据格式以用来匹配单片机系统所需要的显示数据格式。Image2Lcd支持的输入图像格式包括: BMP, WBMP, JPG, GIF, WMF, EMF, ICO, 等等。Image2Lcd的输出数据类型包括定制的二进制类型、C语言数组类型和标准的BMP格式、WBMP格式。Image2Lcd能可视调节输入图象的数据扫描方式、灰度(颜 {MOD}数)、图像数据排列方式、亮度、对比度、等等。对于包含了图像头数据保存的图像数据文件,Image2Lcd能重新打开作为输入图像。 因为image2lcd能生成带图像数据头的数据文件,使得我们处理起来方便很多,这里我们仅以16位真彩 {MOD}为例进行说明。 在该软件的帮助文件查到对“4096 {MOD}/16位真彩 {MOD}/18位真彩 {MOD}/24位真彩 {MOD}/32位真彩 {MOD}”图片,其生成的图像数据头的结构为: typedef struct _HEADCOLOR { unsigned char scan; unsigned char gray; unsigned short w; unsigned short h; unsigned char is565; unsigned char rgb; }HEADCOLOR; 各个成员的功能描述如下: scan: 扫描模式          Bit7: 0:自左至右扫描,1:自右至左扫描。 Bit6: 0:自顶至底扫描,1:自底至顶扫描。 Bit5: 0:字节内象素数据从高位到低位排列,1:字节内象素数据从低位到高位排列。 Bit4: 0:WORD类型高低位字节顺序与PC相同,1:WORD类型高低位字节顺序与PC相反。 Bit3~2: 保留。 Bit1~0: [00]水平扫描,[01]垂直扫描,[10]数据水平,字节垂直,[11]数据垂直,字节水平。 gray: 灰度值    灰度值,1:单 {MOD},2:四灰,4:十六灰,8:256 {MOD},12:4096 {MOD},16:16位彩 {MOD},24:24位彩 {MOD},32:32位彩 {MOD}。 w: 图像的宽度。  h: 图像的高度。 is565: 4096 {MOD}模式下为0表示使用[16bits(WORD)]格式,此时图像数据中每个WORD表示一个象素;为1表示使用[12bits(连续字节流)]格式,此时连续排列的每12Bits代表一个象素。 16位彩 {MOD}模式下为0表示R G B颜 {MOD}分量所占用的位数都为5Bits,为1表示R G B颜 {MOD}分量所占用的位数分别为5Bits,6Bits,5Bits 18位彩 {MOD}模式下为0表示"6Bits in Low Byte",为1表示"6Bits in High Byte" 24位彩 {MOD}和32位彩 {MOD}模式下is565无效。     rgb: 描述R G B颜 {MOD}分量的排列顺序,rgb中每2Bits表示一种颜 {MOD}分量,[00]表示空白,[01]表示Red[10]表示Green[11]表示Blue          HEADCOLOR中,scanwh这三个参数对我们的图片显示尤为有用。直到了wh,就可以直到开窗的大小。而scan的最高两位,则代表了图片数据生成时的扫描方向,也就是我们开窗后地址自增的方向。直到了这几个参数,我们就可以很方便的解析各种大小,各种扫描方式的图片数据了。          下面我们以图2为例,按从左到右,从上到下的扫描方式,生成16位真彩(RGB:565)格式的图像数据。


2 该图片的尺寸为200*168。我们用image2lcd V2.9打开此图片,设置如图3所示:


3 3中,我们设置如上图。我们要生成的的图像数据为16位真彩,所以在2处选择16位真彩 {MOD},然后扫描方式为水平扫描,在3处选中包含头像数据头选项(注意:不能选择“高位在前(MSB First)”这个选项!!!)。在5处选中16位彩 {MOD}选项卡,然后在颜 {MOD}位数(4处)选择RGB565(因为我们的液晶刚好也是RGB565格式)。然后点击保存,命名为image1,可以得到图像数组如下: const unsigned char gImage_image1[67208] = { 0X00,0X10,0XC8,0X00,0XA8,0X00,0X01,0X1B, 0X6B,0X6E,0XD1,0X9F,0XF5,0XB7,0XD3,0XAF,0XF3,0XAF,0X0F,0X8F,0XCE,0X86,0XF3,0XAF, …… 0X73,0XB7,0XF6,0XCF,0XF9,0XD7,0X98,0XCF,0X71,0XAE,0XD6,0XDF,0XFA,0XE7,0XF8,0XCF, 0XF6,0XC7,0X10,0X9F,0X53,0XB7,0XD5,0XC7,0XF6,0XCF,0X74,0XBF,0XD1,0XA6,0XF7,0XD7, };          其中红 {MOD}数字为图像头数据,一共是8个字节,刚好是HEADCOLOR的大小。紧随其后的就是按设定的方向顺序存放的图像数据(颜 {MOD}数据)。这样我们只需要在软件上对这个数组(gImage_image1)的数据进行解析,就可以还原图像了。  
  三,软件实现。 在第二节的介绍中,我们得到了一个数组(gImage_image1),而从第一节的介绍,我们需要一个开窗函数,以及一个扫描方向设置函数,这里提供这两个函数的代码如下: //设置LCD的自动扫描方向 //0~7:代表8个方向(具体定义见lcd.h) //9320/9325/9328/4531/1505/b505/8989IC已经实际测试      void LCD_Scan_Dir(u8 dir) {          u16 regval=0;          u8 dirreg=0; #if USE_HORIZONTAL//使用横屏          switch(dir)//方向转换          {                    case 0:dir=6;break;                    case 1:dir=7;break;                    case 2:dir=4;break;                    case 3:dir=5;break;                    case 4:dir=1;break;                    case 5:dir=0;break;                    case 6:dir=3;break;                    case 7:dir=2;break;               } #endif        if(DeviceCode==0x8989)//8989 IC          {                    dirreg=0X11;                    regval=0X6040;//65K            }else//其他驱动IC                      {                    dirreg=0X03;                    regval=1<<12;           }          switch(dir)          {                    case L2R_U2D://从左到右,从上到下                             regval|=(1<<5)|(1<<4)|(0<<3);                             break;                    case L2R_D2U://从左到右,从下到上                             regval|=(0<<5)|(1<<4)|(0<<3);                             break;                    case R2L_U2D://从右到左,从上到下                             regval|=(1<<5)|(0<<4)|(0<<3);                             break;                    case R2L_D2U://从右到左,从下到上                             regval|=(0<<5)|(0<<4)|(0<<3);                             break;                          case U2D_L2R://从上到下,从左到右                             regval|=(1<<5)|(1<<4)|(1<<3);                             break;                    case U2D_R2L://从上到下,从右到左                             regval|=(1<<5)|(0<<4)|(1<<3);                             break;                    case D2U_L2R://从下到上,从左到右                             regval|=(0<<5)|(1<<4)|(1<<3);                             break;                    case D2U_R2L://从下到上,从右到左                             regval|=(0<<5)|(0<<4)|(1<<3);                             break;                }                 LCD_WriteReg(dirreg,regval); }  //设置窗口 //sx,sy,ex,ey窗口坐标 //窗口大小ex-sx+1)*(ey-ex+1) //注意,确保ex>=sx;ey>=sy!!!! //9320/9325/9328/4531/1505/b505/8989IC已经实际测试      void LCD_Set_Window(u16 sx,u16 sy,u16 ex,u16 ey)        {          u8 hsareg,heareg,vsareg,veareg;          u16 hsaval,heaval,vsaval,veaval;  #if USE_HORIZONTAL  //使用横屏                 //窗口值          hsaval=sy;                                             heaval=ey;          vsaval=319-ex;          veaval=319-sx;         #else                                     //竖屏          //窗口值          hsaval=sx;                                             heaval=ex;          vsaval=sy;          veaval=ey;        #endif         if(DeviceCode==0X8989)//8989 IC          {                    hsareg=0X44;heareg=0X44;//水平方向窗口寄存器 (1289的由一个寄存器控制)                    hsaval|=(heaval<<8);       //得到寄存器值.                    heaval=hsaval;                    vsareg=0X45;veareg=0X46;//垂直方向窗口寄存器                 }else  //其他驱动IC          {                    hsareg=0X50;heareg=0X51;//水平方向窗口寄存器                    vsareg=0X52;veareg=0X53;//垂直方向窗口寄存器                 }                                                                                    //设置寄存器值          LCD_WriteReg(hsareg,hsaval);          LCD_WriteReg(heareg,heaval);          LCD_WriteReg(vsareg,vsaval);          LCD_WriteReg(veareg,veaval); }          这两个函数已经添加到ILI93xx.c的源码中,具体请看本应用文档的对应扩展实验(ALIENTEK MINISTM32 扩展实验13 内部FLASH图片显示实验)。更新后的ILI93xx.c版本为V1.6。同时该实验的USMART部分也有了更新,最新版本的USMART V2.6          扩展实验13的源码是在标准实验10的基础上修改而来的,加入了usmart组建以及新建了IMAG2LCD的组。见图4


4          有了LCD_Set_WindowLCD_Scan_Dir这两个函数,做起来就方便多了(通过画点的方式也可以实现),根据第一节的原理,编写出的flash->lcd函数部分即image2lcd.c的内容如下: //8位数据获得16位颜 {MOD} //mode:0,低位在前,高位在后. //     1,高位在前,低位在后. //str:数据 u16 image_getcolor(u8 mode,u8 *str) {          u16 color;          if(mode)          {                    color=((u16)*str++)<<8;
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。