引言
LCD和CRT显示器作为一种通用型显示设备,如今已广泛应用于我们的工作和生活中。与嵌入式系统中常用的显示器件相比,它具有显示面积大、色彩丰富、承载信息量大、接口简单等优点,如果将其应用到嵌入式系统中,可以显著提升产品的视觉效果。
Altera公司生产的Cyclone II系列FPGA具有很高的性价比,作为该系列中最便宜的一款FPGA,EP2C5T144C8提供了4608个逻辑单元、26个M4K RAM块、13个18*18嵌入乘法器、2个PLL和89个用户I/O引脚。这个VGA显示系统可以在NIOS内核的控制下,在屏幕上显示出任意的文字或图形。
VGA接口概述
VGA接口为显示器提供两类信号,一类是数据信号,一类是控制信号。数据信号包括红(Red)、绿(Green)、蓝(Blue)信号,简称RGB信号,控制信号包括水平同步信号和垂直同步信号。输出不同分辨率时,水平同步信号和垂直同步信号的频率也不同。下表是在部分分辨率条件下使用的相应频率。
当分辨率为640*480时,显示器每行占用840(31.5MHz/37.5kHz)个数据位,其中640个数据位用于显示像素,另外200(840-640)个数据位用于输出水平同步信号和水平消隐信号。垂直方向有500(37.5kHz/75Hz)行,其中480行用于显示像素,其余20(500-480)行用于输出垂直同步信号和垂直消隐信号。水平同步信号和垂直同步信号都采用低电平同步方式。水平同步信号低电平约保持2μs,相当于63个数据位长度。垂直同步信号低电平约保持80μs,相当于3行数据位长度。
系统实现
本设计参考了PC机中显卡的工作方式,即在NIOS和VGA显示系统之间增设一个双口RAM(DPRAM)作为显示存储器(VRAM)。NIOS每次把要显示的内容写入VRAM中,VGA显示系统根据显示时序依次读取VRAM中的数据发送给显示器。因为每个像素在VRAM中都有对应的地址,所以NIOS只要修改VRAM的内容就可以实现控制显示输出的目的。
THCII-1实验板的VGA输出颜色数为256(2 8 )色,正好可以用VRAM的一个字节表示一个像素的颜色。全屏幕共有307200(640*480)个像素,需要300k字节的VRAM。这么大的VRAM在实验板上很难用FPGA(EP2C5)单独来实现,但是,如果用屏幕上10*10个像素所组成的矩形面积来 表示一个逻辑像素,人感受到的分辨率则变为64*48,此时VRAM仅占3072个字节,这在EP2C5内部实现起来就容易多了。当然,还应再增加一个永远为0x00的字节,当VGA电路输出同步信号和消隐信号时,输出地址都指向这个特殊字节,使RGB输出信号为0电平。按照以上的设计要求,VRAM地址范围从0x000至0xC00,共占用12条地址线。
VHDL程序设计
1)实体部分——实体部分定义了VGA显示系统的所有接口
entity VGA is
Port
(
Fosc : in std_logic; -- 36MHz时钟信号。
Data : in std_logic_vector(7 downto 0);
--VRAM数据线
Address : in std_logic_vector(11 downto 0);
--VRAM写地址
CE : in std_logic; --片选信号,
高电平有效
WREn : in std_logic; --写允许信号,
高电平有效
HS : out std_logic; --水平同步信号
VS : out std_logic; --垂直同步信号
RGB : out std_logic_vector(7 downto 0)
--色彩信号
);
end entity;
2)常量和信号
constant HSMax : integer := 840;
--每行840个数据位
constant HSNegative : integer := 63;
--水平同步信号低电平占63个数据位
constant VSMax : integer := 500;
--每帧有500行
constant VSNegative : integer := 3;
--垂直同步信号低电平占3行数据位
constant XMax : integer := 640;
--水平640个像素
constant YMax : integer := 480;
--垂直480个像素
constant Left : integer:= 184;
--每行首像素位置
constant Right : integer := Left + XMax -1;
--每行末像素位置
constant Top : integer := 16;--首行位置
constant Bottom : integer := Top + (YMax - 1);
--末行位置
constant PixelWide : integer := XMax / 64;
--逻辑像素宽度(10pixel)
constant PixelHigh : integer := YMax / 48;
--逻辑像素高度(10pixel)
signal Fvga : std_logic;
--31.5MHz时钟信号
signal H : std_logic;
--垂直同步计数允许信号
signal XAddress : std_logic_vector(11 downto 0);
--VRAM水平地址
signal YAddress : std_logic_vector(5 downto 0);
--VRAM垂直地址
signal VRA : std_logic_vector(11 downto 0);
--VRAM输出地址
3)DPRAM模块作为VRAM,为每个像素提供映射地址
Modular : DPRAM
port map
(
data => Data,
--VRAM写入数据,由NIOS写入
rdaddress => VRA,
--VRAM输出地址,由VGA显示系统提供
rdclock => Fvga,
--VRAM输出时钟,31.5MHz
wraddress => Address,
--VRAM写入地址
wrclock => Fosc,
--VRAM写入时钟
wren => WREn and CE,
--VRAM写允许信号,
WREn和CE为1时有效
q => RGB
--RGB输出信号
);
4)水平同步信号的产生和VRAM水平地址寻址
process(Fvga)
variable HSValue : integer range 0 to HSMax
- 1 := 0;
begin
if rising_edge(Fvga) then
--在Fvga(31.5MHz)的上升沿循环计数(0-839)
if HSValue = HSMax - 1 then
HSValue := 0;
else
HSValue := HSValue + 1;
end if;
--在计数值小于63时(0至62),水平同步信号输出低电平,否则输出高电平
if HSValue < HSNegative then
HS <= '0';
else
HS <= '1';
end if;
- -在计数值等于839时,垂直同步计数允许信号为1,否则为0
if HSValue = HSMax - 1 then
H <='1';
else
H <='0';
end if;
--每行从首像素到末像素,每10个像素对应一个VRAM水平地址,共64个水平地址
if HSValue >= Left + 0 * PixelWide and HSValue < Left + 1 * PixelWide then XAddress <= "000000000000";
elsif HSValue >= Left + 1 * PixelWide and HSValue < Left + 2 * PixelWide then XAddress <= "000000000001";
elsif HSValue >= Left + 2 * PixelWide and HSValue < Left + 3 * PixelWide then XAddress <= "000000000010";
……………………..--中间省略
elsif HSValue >= Left + 63 * PixelWide and HSValue < Left + 64 * PixelWide then XAddress <= "000000111111";
--超出首像素和末像素时,输出默认地址0x110000000000的0x00
else XAddress <= "110000000000";
end if;
end if;
end process;
5)垂直同步信号的产生和VRAM垂直地址寻址
process(Fvga, H)
variable VSValue: integer range 0 to VSMax - 1 := 0;
begin
if rising_edge(Fvga) then
--在Fvga(31.5MHz)的上升沿,当垂直同步计数允许信号为1时,对行数循环计数(0~479)
if H = '1' then
if VSValue = VSMax - 1 then
VSValue := 0;
else
VSValue := VSValue + 1;
end if;
else
VSValue := VSValue;
end if;
--当行计数小于3时,垂直同步信号输出低电平,否则输出高电平
if VSValue < VSNegative then
VS <= '0';
else
VS <= '1';
end if;
--从首行到末行,每10个行对应一个VRAM垂直地址,共48个水平地址
if VSValue >= Top + 0 * PixelHigh and VSValue < Top + 1 * PixelHigh then YAddress <= "000000";
elsif VSValue >= Top + 1 * PixelHigh and VSValue < Top + 2 * PixelHigh then YAddress <= "000001";
elsif VSValue >= Top + 2 * PixelHigh and VSValue < Top + 3 * PixelHigh then YAddress <= "000010";
……………………..--中间省略
elsif VSValue >= Top + 47 * PixelHigh and VSValue < Top + 48 * PixelHigh then YAddress <= "101111";
--超出首行和末行时,输出默认地址0x110000000000的垂直地址部分0x110000
else YAddress <= "110000";
end if;
end if;
end process;
6)VRAM输出地址的合成
VRA <= YAddress & "000000" or XAddress;
NIOS程序设计
1)字模表。每个字符均由5*7个逻辑像素组成,占用5个字节。字符"1"的字模如下图所示。任意字符的ASCII码经过差值计算后就能得到该字符在字模表中的位置。
图(略)
unsigned char CHTAB[][5]=
{
{0x3E, 0x51, 0x49, 0x45, 0x3E}, //"0"=00h
{0x00, 0x42, 0x7F, 0x40, 0x00}, //"1"=01h
{0x42, 0x61, 0x51, 0x49, 0x46}, //"2"=02h
{0x21, 0x41, 0x45, 0x4B, 0x31}, //"3"=03h
{0x18, 0x14, 0x12, 0x7F, 0x10}, //"4"=04h
--中间省略
{0x00, 0x41, 0x36, 0x08, 0x00}, //"}"=5Dh
{0x02, 0x01, 0x02, 0x04, 0x02}, //"~"=5Fh
{0x7F, 0x7F, 0x7F, 0x7F, 0x7F} //" "=60h
};
2)显示像素函数,负责向VRAM中指定行和列上的字节写入颜色值。VGA_0_BASE为VRAM的首地址。
void Pixel(unsigned char row, unsigned char column, unsigned char color)
{
unsigned short offset;
offset = row * 64 + column;
IOWR(VGA_0_BASE, offset, color);
}
3)字符显示函数
void Character(unsigned char row, unsigned char column, unsigned char *CH, unsigned char frontcolor, unsigned char backcolor)
{
unsigned char i, h, color;
for(h = 0; h < 5; h ++)//遍历字模占用的5个字节
{
for(i = 0; i < 8; i ++)//遍历字模中每个字节的8个bit
{
if(1 == ((*(CH + h) >> i) & 1))
{
color = frontcolor;//如果某bit为1,则显示前景颜色
}
else
{
color = backcolor;//如果某bit为0,则显示背景颜色值
}
Pixel(row + i, column + h, color);//显示像素
}
}
}
实验结果
经过实验证明,此VGA显示系统完全符合设计要求。如果在Flash中加入汉字库,还可以显示汉字。但是,由于受实验板和器件的限制,虽然VGA显示系统输出分辨率为640*480,但实际显示分辨率仅为64*48,因此仍需要对电路部分进行改进和完善。 |