9. 基于rom的vga图像显示

在前面两个章节中,我们详细讲解了VGA的相关理论知识,通过“VGA彩条显示”和“VGA字符显示”两个实验工程分别实现了VGA等宽十色彩条显示和字符显示。但不论是彩条显示和字符显示,图像的色彩信息都是通过代码生成,那如果想要显示一幅已存在的图片,应该怎么做呢?

在本章节,我们就实现这一想法,在VGA显示器上实现已存在图片的显示,具体实现方法详见下文。

9.1. 理论学习

详见“VGA显示器驱动设计与验证”章节的“理论学习”小结。

9.2. 实战演练

9.2.1. 实验目标

实验目标:以十色等宽彩条做背景,将存储于ROM中的图片显示在VGA显示器的中心位置。图片分辨率为100*100,VGA显示模式为640x480@60。

实验效果,具体见图 38‑1。

ROMVGA002

图 38‑1 VGA彩条实验效果图

9.2.2. 硬件资源

详见“VGA显示器驱动设计与验证”章节的“硬件资源”介绍。

9.2.3. 程序设计

由实验目标可知,本实验想要将事先缓存在ROM中的图片显示在VGA显示器的中心位置,显示图片大小为100*100,VGA显示模式为640x480@60。实验的具体实现方法,我们会在本小节详细讲解。

本实验工程是在“VGA显示器驱动设计与验证”章节的实验工程基础上修改得到,功能模块中只修改了图像数据生成模块vga_pic。

在本小结的讲解中,我们先来说一下图片在存入ROM之前需要进行的预处理操作,再对工程整体架构进行说明,然后讲解一下改动较大的vga_pic模块,其他未改动或改动较小的模块,不再讲解。

9.2.3.1. 图片预处理

我们先来说一下图片在存入ROM之前需要进行的预处理操作。在本次实验中,我们用来存储图片的ROM是通过调用IP核,利用FPGA片上资源生成的只读存储器,但FPGA片上资源有限,生成ROM的存储空间也会受限,存储于ROM的图片大小也受到限制。

FPGA开发板上使用的芯片为Altera公司的EP4CE10F17C8芯片,存储空间为414Kbit;本次实验显示图片分辨率为100*100,色彩格式为RGB565,存储于ROM所占空间为100*100*16=160000bit=156.25Kbit。通过比较发现,芯片可生成满足要求的ROM 用来存储图片。

ROM作为只读存储器,在进行IP核设置时需要指定初始化文件, 即写入存储器中的图片数据,图片要以规定的格式才能正确写入ROM,这种格式就是MIF文件。MIF是Quartus规定的一种文件格式,文件格式示意图,具体见图 38‑2。

ROMVGA003

图 38‑2 MIF文件格式示意图

若存储数据量较小,我们可以参照上图,手动写入数据。但如果存储量较大,手动输入不太现实。本次实验中要写入100*100个数据,不可能使用手动输入,在这里我们使用Matlab软件将图片转化为MIF文件,具体步骤如下。

1、调整图片大小

考虑到ROM存储空间大小的问题,本次实验显示图片大小为100*100,但并不一定读者想要显示的图片恰好满足这一要求。若图片大小不满足要求,我们可以使用系统自带的画图软件进行调整。

以本次要显示的图片为例,进行讲解。

首先查看图片属性,检验图片大小是否满足要求,具体见图 38‑3。

ROMVGA004

图 38‑3 图片属性

由图可知,图片分辨率为103*103,不满足要求,需要进行大小调整;若图片大小满足要求,跳过此步骤。图片中的位深度位24,表示图片像素点数据位宽。

然后,右键选中图片,使用系统自带的画图工具打开图片,点击“重新调整大小”,在“调整大小和扭曲”的窗口中,去掉“保持纵横比”勾选,以“像素”为依据调整图片大小为实验要求的100*100,点击“确定”,保存图片并退出,具体见图 38‑4、图 38‑5。

ROMVGA005

图 38‑4 使用画图打开图片

ROMVGA006

图 38‑5 重新调整图片大小

2、使用Matlab软件将图片转化为MIF文件

图片大小调整完毕后,使用使用Matlab软件将图片转化为MIF文件。

首先,在工程目录下新建一个matlab文件夹,将调整好大小的图片复制到文件夹下。打开Matlab软件,新建一个*.m的脚本文件,保存到matlab文件夹中,文件名自定义,具体见图 38‑6。

ROMVGA007

图 38‑6 创建新的脚本文件

然后,在新建的脚本文件中写入以下代码,参考代码具体见代码清单 38‑1。代码中注释较为详细,理解较为容易,不再逐句讲解。

代码清单 38‑1 MIF生成代码(mif_gen.m)

1 clear %清理命令行窗口

2 clc %清理工作区

3

4 % 使用imread函数读取图片,并转化为三维矩阵

5 image_array = imread(‘logo.bmp’);

6

7 % 使用size函数计算图片矩阵三个维度的大小

8 % 第一维为图片的高度,第二维为图片的宽度,第三维为图片维度

9 [height,width,z]=size(image_array); % 100*100*3

10 red = image_array(:,:,1); % 提取红色分量,数据类型为uint8

11 green = image_array(:,:,2); % 提取绿色分量,数据类型为uint8

12 blue = image_array(:,:,3); % 提取蓝色分量,数据类型为uint8

13

14 % 使用reshape函数将各个分量重组成一个一维矩阵

15 %为了避免溢出,将uint8类型的数据扩大为uint32类型

16 r = uint32(reshape(red’ , 1 ,height*width));

17 g = uint32(reshape(green’ , 1 ,height*width));

18 b = uint32(reshape(blue’ , 1 ,height*width));

19

20 % 初始化要写入.mif文件中的RGB颜色矩阵

21 rgb=zeros(1,height*width);

22

23 % 导入的图片为24bit真彩色图片,每个像素占用24bit,RGB888

24 % 将RGB888转换为RGB565

25 % 红色分量右移3位取出高5位,左移11位作为ROM中RGB数据的第15bit到第11bit

26 % 绿色分量右移2位取出高6位,左移5位作为ROM中RGB数据的第10bit到第5bit

27 % 蓝色分量右移3位取出高5位,左移0位作为ROM中RGB数据的第4bit到第0bit

28 for i = 1:height*width

29 rgb(i) = bitshift(bitshift(r(i),-3),11)

30 + bitshift(bitshift(g(i),-2),5)

31 + bitshift(bitshift(b(i),-3),0);

32 end

33

34 fid = fopen( ‘image.mif’, ‘w+’ );

35

36 % .mif文件字符串打印

37 fprintf( fid, ‘WIDTH=16;n’);

38 fprintf( fid, ‘DEPTH=%d;nn’,height*width);

39

40 fprintf( fid, ‘ADDRESS_RADIX=UNS;n’);

41 fprintf( fid, ‘DATA_RADIX=HEX;nn’);

42

43 fprintf(fid,’%snt’,’CONTENT’);

44 fprintf(fid,’%sn’,’BEGIN’);

45

46 % 写入图片数据

47 for i = 1:height*width

48 fprintf(fid,’tt%dt:%xt;n’,i-1,rgb(i));

49 end

50

51 % 打印结束字符串

52 fprintf(fid,’tEND;’);

53

54 fclose( fid ); % 关闭文件指针

代码编写完成并保存,运行脚本文件后,会在matlab文件夹下生成一个MIF文件,查看文件,图片数据已按格式要求写入。

在调用IP核生成ROM时,将生成的MIF文件导入即可,具体方法在前面章节有详细介绍,在此不再过多叙述。但读者要注意的是,ROM深度一定要大于等于图片包含像素点个数。

9.2.3.2. 整体说明

在本小节,我们先要对整个实验工程有一个整体认识,首先来看一下基于ROM的VGA图像显示实验工程的整体框图,具体见图 38‑7。

ROMVGA008

图 38‑7 基于ROM的VGA图像显示实验整体框图

由上图可知,本实验工程包括4个模块,各模块简介,具体见表格 38‑1。

表格 38‑1 基于ROM的VGA图像显示工程模块简介

模块名称

功能描述

vga_rom_pic

顶层模块

clk_gen

时钟生成模块,生成VGA驱动时钟

vga_ctrl

VGA时序控制模块,控制VGA图像显示

vga_pic

图像数据生成模块,生成VGA显示图像

结合图 38‑7和表格 38‑1,我们来说一下基于ROM的VGA图像显示工程的工作流程。

  1. 系统上电后,板卡传入系统时钟(sys_clk)和复位信号(sys_rst_n)到顶层模块;

  2. 系统时钟直接传入时钟生成模块(clk_gen),分频产生VGA工作时钟(vga_clk),作为图像数据生成模块(vga_pic)和VGA时序控制模块(vga_ctrl)的工作时钟;

  3. 图像数据生成模块以VGA时序控制模块传入的像素点坐标(pix_x,pix_y)为约束条件,生成背景信息的待显示图像的色彩信息(pix_data);在图片显示区域读取存储于ROM的图片数据;两者数据结合生成VGA显示图像数据pix_data_out传入VGA时序控制模块。

  4. 图像数据生成模块生成的图像色彩信息pix_data_out传入VGA时序控制模块,在模块内部使用使能信号滤除掉非图像显示有效区域的图像数据,产生RGB色彩信息(rgb),在行、场同步信号(hsync、vsync)的同步作用下,将RGB色彩信息扫描显示到VGA显示器,实现图片显示。

本小节以全局视角,对整个实验工程进行了概括,对各子功能模块做了简单介绍,简要说明了实验工程的工作流程。

9.2.3.3. 图像数据生成模块

本小节中我们开始图像数据生成模块(vga_pic)的介绍,接下来我们会通过模块框图、波形图绘制、代码编写、仿真分析这几个部分,对本模块的设计、实现、仿真验证过程做一下详细介绍。

模块框图

图像数据生成模块,设计本模块的目的是产生VGA彩条背景像素点色彩信息和读出ROM存储的图片数据。模块框图,具体见图 38‑8。

ROMVGA009

图 38‑8 图像数据生成模块框图

由图 38‑8可知,图像数据生成模块包含4路输入、1路输出,共5路信号。输入输出信号简介,具体见表格 38‑2。

表格 38‑2 图像数据生成模块输入输出端口功能描述

信号

位宽

类型

功能描述

vga_clk

1Bit

Input

工作时钟,频率25MHz

sys_rst_n

1Bit

Input

复位信号,低电平有效

pix_x

10Bit

Input

VGA有效显示区域像素点X轴坐标

pix_y

10Bit

Input

VGA有效显示区域像素点Y轴坐标

pix_data_out

16Bit

Output

图像像素点色彩信息

输入信号中,时钟信号vga_clk,频率为25MHz,为VGA显示器工作时钟,由分频模块产生并输入;复位信号sys_rst_n为顶层模块的rst_n信号输入,低电平有效;(pix_x,pix_y)为VGA有效显示区域像素点坐标,由VGA时序控制模块生并输入。

输出信号pix_data_out为图像像素点色彩信息,在VGA有效显示区域像素点坐标(pix_x,pix_y)约束下生成,传输到VGA时序控制模块。

模块内部实例化ROM IP核,有三路输入,一路输出,输入信号为时钟、数据读使能和数据地址信号,输出为数据地址对应图片数据。

波形图绘制

在模块框图部分,我们介绍了图像数据生成模块的具体功能,对输入输出信号做了简单介绍,那么如何利用模块输入信号实现模块功能,输出我们想要得到的数据信号呢?在波形图绘制部分,我们会通过绘制波形图,并对各信号做详细讲解,带领读者学习掌握模块功能的实现方法。

图像数据生成模块波形图,具体见图 38‑9。

ROMVGA010

图 38‑9 图像数据生成模块波形图

图 38‑9是我们最终绘制生成的图像数据生成模块波形图,下面我们讲解一下波形图绘制的具体思路。

第一部分:彩条背景色彩信息(pi_data)波形图绘制思路

根据输入像素点坐标(pix_x,pix_y),在有效显示区域,将pix_x计数范围十等分,在不同的计数部分给pix_data赋值对应的色彩信息,因为采用时序逻辑的赋值方式,pix_data滞后pix_x、pix_y信号一个时钟周期。信号波形图如下:

ROMVGA011

图 38‑10 pix_data信号波形图

第二部分:ROM读使能(rd_en)、ROM地址(rom_addr)波形图绘制思路

我们将要显示的的图片数据是事先写入ROM,ROM为调用IP核生成,写入照片分辨率为100*100。要想将写入ROM的图片读取出来,使能信号和地址信号必不可少,所以模块内部要声明ROM读使能信号(rd_en)和ROM地址(rom_addr)信号。

我们可以在图片显示区域拉高使能信号,将要读取数据地址写入ROM地址端口,读取地址对应图像数据。但有一点要注意,自ROM读取的数据是滞后使能信号和地址信号一个时钟周期的,比如,当使能信号为高电平,地址写入为999,但与地址999同步输出的数据为地址998的数据,所以ROM读使能信号(rd_en)和RO M地址(rom_addr)信号均要超前图片显示区域一个时钟周期,信号波形图绘制如下:

ROMVGA012

图 38‑11 rd_en、rom_addr信号

第三部分:图片显示有效信号(pic_valid)、待显示图像数据(pic_data_out)波形图绘制思路

想要在彩条图像背景上显示要显示的图片,我们需要在图片显示区域,使用图片数据覆盖彩条背景,那么如何确定图像显示区域呢?我们需要声明一个内部信号,那就是图片显示有效信号(pic_valid),在有效信号为高电平时,将自ROM读出的图片数据赋值给待显示图像数据(pic_data_out),覆盖彩条背景。

上文中我们提到ROM读使能信号(rd_en)超前图片显示区域一个时钟周期,可以利用此信号 延迟一个时钟周期生成图片显示有效信号(pic_valid);在有效信号为高电平时,将自ROM读出的图片数据赋值给待显示图像数据(pic_data_out),覆盖彩条背景。信号波形图如下:

ROMVGA013

图 38‑12 pic_valid、pic_data_out信号波形图

各信号波形绘制思路讲解完毕,将所有信号整合后,就是本小节开始部分展示的模块波形图。

本设计思路只做参考,并非唯一方法,读者可利用所学知识,按照自己思路进行设计。

代码编写

模块波形图绘制完毕后,参照绘制波形图进行参考代码的编写。模块参考代码,具体见代码清单 38‑2。

代码清单 38‑2 图像数据生成模块参考代码(vga_pic.v)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
module vga_pic
(
input wire vga_clk , //输入工作时钟,频率25MHz
input wire sys_rst_n , //输入复位信号,低电平有效
input wire [9:0] pix_x , //输入有效显示区域像素点X轴坐标
input wire [9:0] pix_y , //输入有效显示区域像素点Y轴坐标

output wire [15:0] pix_data_out //输出VGA显示图像数据

);

////
//\* Parameter and Internal Signal \//
////

parameter H_VALID = 10'd640 , //行有效数据
V_VALID = 10'd480 ; //场有效数据

parameter H_PIC = 10'd100 , //图片长度
W_PIC = 10'd100 , //图片宽度
PIC_SIZE= 14'd10000 ; //图片像素个数

parameter RED = 16'hF800 , //红色
ORANGE = 16'hFC00 , //橙色
YELLOW = 16'hFFE0 , //黄色
GREEN = 16'h07E0 , //绿色
CYAN = 16'h07FF , //青色
BLUE = 16'h001F , //蓝色
PURPPLE = 16'hF81F , //紫色
BLACK = 16'h0000 , //黑色
WHITE = 16'hFFFF , //白色
GRAY = 16'hD69A ; //灰色

//wire define
wire rd_en ; //ROM读使能
wire [15:0] pic_data ; //自ROM读出的图片数据

//reg define
reg [13:0] rom_addr ; //读ROM地址
reg pic_valid ; //图片数据有效信号
reg [15:0] pix_data ; //背景色彩信息

////
//\* Main Code \//
////

//rd_en:ROM读使能
assign rd_en = (((pix_x >= (((H_VALID - H_PIC)/2) - 1'b1))
&& (pix_x < (((H_VALID - H_PIC)/2) + H_PIC - 1'b1)))
&&((pix_y >= ((V_VALID - W_PIC)/2))
&& ((pix_y < (((V_VALID - W_PIC)/2) + W_PIC)))));

//pic_valid:图片数据有效信号
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pic_valid <= 1'b1;
else
pic_valid <= rd_en;

//pix_data_out:输出VGA显示图像数据
assign pix_data_out = (pic_valid == 1'b1) ? pic_data : pix_data;

//根据当前像素点坐标指定当前像素点颜色数据,在屏幕上显示彩条
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pix_data <= 16'd0;
else if((pix_x >= 0) && (pix_x < (H_VALID/10)*1))
pix_data <= RED;
else if((pix_x >= (H_VALID/10)*1) && (pix_x < (H_VALID/10)*2))
pix_data <= ORANGE;
else if((pix_x >= (H_VALID/10)*2) && (pix_x < (H_VALID/10)*3))
pix_data <= YELLOW;
else if((pix_x >= (H_VALID/10)*3) && (pix_x < (H_VALID/10)*4))
pix_data <= GREEN;
else if((pix_x >= (H_VALID/10)*4) && (pix_x < (H_VALID/10)*5))
pix_data <= CYAN;
else if((pix_x >= (H_VALID/10)*5) && (pix_x < (H_VALID/10)*6))
pix_data <= BLUE;
else if((pix_x >= (H_VALID/10)*6) && (pix_x < (H_VALID/10)*7))
pix_data <= PURPPLE;
else if((pix_x >= (H_VALID/10)*7) && (pix_x < (H_VALID/10)*8))
pix_data <= BLACK;
else if((pix_x >= (H_VALID/10)*8) && (pix_x < (H_VALID/10)*9))
pix_data <= WHITE;
else if((pix_x >= (H_VALID/10)*9) && (pix_x < H_VALID))
pix_data <= GRAY;
else
pix_data <= BLACK;

//rom_addr:读ROM地址
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rom_addr <= 14'd0;
else if(rom_addr == (PIC_SIZE - 1'b1))
rom_addr <= 14'd0;
else if(rd_en == 1'b1)
rom_addr <= rom_addr + 1'b1;

////
 //\* Instantiation \//
 ////

 //------------- rom_pic_inst -------------
 rom_pic rom_pic_inst
 (
 .address (rom_addr ), //输入读ROM地址,14bit
 .clock (vga_clk ), //输入读时钟,vga_clk,频率25MHz,1bit
 .rden (rd_en ), //输入读使能,1bit

 .q (pic_data ) //输出读数据,16bit
 );

 endmodule

模块参考代码是参照绘制波形图进行编写的,在波形图绘制小节已经对模块各信号有了详细的说明,对各信号介绍不再过多叙述。

仿真代码编写

不再对模块进行单独仿真,直接对实验工程进行整体仿真,仿真参考代码,具体见代码清单 38‑3。

代码清单 38‑3 顶层模块仿真参考代码(tb_vga_rom_pic.v)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
\`timescale 1ns/ 1ns
module tb_vga_rom_pic();

////
//\* Parameter and Internal Signal \//
////
//wire define
wire hsync ;
wire [15:0] rgb ;
 wire vsync ;

 //reg define
 reg sys_clk ;
 reg sys_rst_n ;

 ////
 //\* Clk And Rst \//
 ////

 //sys_clk,sys_rst_n初始赋值
 initial
 begin
 sys_clk = 1'b1;
 sys_rst_n <= 1'b0;
 #200
 sys_rst_n <= 1'b1;
 end

 //sys_clk:产生时钟
 always #10 sys_clk = ~sys_clk ;

 ////
 //\* Instantiation \//
 ////

 //------------- vga_rom_pic_inst -------------
 vga_rom_pic vga_rom_pic_inst
 (
 .sys_clk (sys_clk ), //输入晶振时钟,频率50MHz,1bit
 .sys_rst_n (sys_rst_n ), //输入复位信号,低电平有效,1bit

 .hsync (hsync ), //输出行同步信号,1bit
 .vsync (vsync ), //输出场同步信号,1bit
 .rgb (rgb ) //输出RGB图像信息,16bit
 );

 endmodule

顶层模块仿真参考代码内部实例化顶层模块,模拟产生50MHz时钟信号和复位信号,理解较为容易,不再讲解。

仿真波形分析

使用ModelSim软件对代码进行仿真,我们只经过改动的vga_pic模块的相关信号,仿真结果如下。

ROMVGA014

图 38‑13 图像数据生成模块整体仿真波形图

ROMVGA015

图 38‑14 图像数据生成模块局部仿真波形图(一)

ROMVGA016

图 38‑15 图像数据生成模块局部仿真波形图(二)

由仿真波形可以看出,图像数据生成模块各信号波形与绘制的参考波形图波形一致,模块通过仿真验证。

9.2.3.4. RTL视图

实验工程通过仿真验证后,使用Quartus软件对实验工程进行编译,编译完成后,我们查看一下RTL视图, RTL视图展示信息与顶层模块框图一致,各信号连接正确,具体见图 38‑16。

ROMVGA017

图 38‑16 实验工程RTL视图

9.3. 上板验证

9.3.1. 引脚约束

仿真验证通过后,准备上板验证,上板验证之前先要进行引脚约束。工程中各输入输出信号与开发板引脚对应关系如表格 38‑3所示。

表格 38‑3 引脚分配表

信号名

信号类型

对应引脚

备注

sys_clk

Input

E1

输入系统时钟

sys_rst_n

Input

M15

复位信号

hsync

Output

C2

行同步信号

vsync

Output

D1

场同步信号

rgb[15]

Output

A5

RGB色彩信息(红)

rgb[14]

Output

E6

RGB色彩信息(红)

rgb[13]

Output

E7

RGB色彩信息(红)

rgb[12]

Output

B8

RGB色彩信息(红)

rgb[11]

Output

A8

RGB色彩信息(红)

rgb[10]

Output

F8

RGB色彩信息(绿)

rgb[9]

Output

E8

RGB色彩信息(绿)

rgb[8]

Output

B7

RGB色彩信息(绿)

rgb[7]

Output

A7

RGB色彩信息(绿)

rgb[6]

Output

F7

RGB色彩信息(绿)

rgb[5]

Output

F6

RGB色彩信息(绿)

rgb[4]

Output

B6

RGB色彩信息(蓝)

rgb[3]

Output

A6

RGB色彩信息(蓝)

rgb[2]

Output

B5

RGB色彩信息(蓝)

rgb[1]

Output

A2

RGB色彩信息(蓝)

rgb[0]

Output

B4

RGB色彩信息(蓝)

下面进行管脚分配,管脚的分配方法在前面章节已有所讲解,在此就不再过多叙述,管脚的分配如下图 38‑17所示。

ROMVGA018

图 38‑17 管脚分配

9.3.1.1. 结果验证

如图 38‑18所示,开发板连接12V直流电源、USB-Blaster下载器JTAG端口以及VGA显示器。线路正确连接后,打开开关为板卡上电。

ROMVGA019

图 38‑18 程序下载连线图

如图 38‑19所示,使用“Programmer”为开发板下载程序。

ROMVGA020

图 38‑19 程序下载图

程序下载完成后,如图 38‑20所示,VGA显示器正中间显示出野火标志,背景为十色彩条,和预期实验效果一致。

ROMVGA021

图 38‑20 基于ROM的VGA图像显示效果图

9.4. 章末总结

到这里,本章节讲解完毕,相信读者已经掌握了基于ROM的VGA图像显示方法。不过读者有一点需要注意,就是章节中提到的,普通ROM读出数据会滞后ROM读使能信号和地址信号一个时钟周期。读者在以后调用ROM时要记得这一点,防止出现错误。

9.5. 拓展训练

1、修改代码,将ROM存储图片显示在屏幕的左上角、右下角或读者想要显示的位置。

2、尝试显示较大分辨率的图片,若实验工程不能编译通过,查看报错信息。