目录
VGA时序控制器的设计
VGA(Video Graphics Array)是IBM公司于1987年推出基于模拟信号的视频显示标准,目前仍是计算机、投影仪和液晶显示器等电子产品基本的视频接口标准。
VGA应用DB15物理接口。 VGA接口信号的定义如右图所示,主要有5条基本信号线:红、绿、蓝三基色模拟信号线,以及行同步(HSYNC)和场同步(VSYNC)两条数字信号线。
VGA三基色模拟量采用RS343电平标准,其中红色和蓝色信号的电压范围为0~0.714V,如图6-39所示,而绿色信号的电压范围为0~1.0V。
VGA行、场同步信号分别为行扫描和场扫描提供同步信号,采用TTL电平标准。
行同步信号和场同步信号分为前沿(front porch) 、同步头(sync)、后沿(back porch)和显示(display interval)四个阶段。行同步信号以像素(pixel)为单位,而场同步信号则以行(line)为单位。行、场同步头(a段)为低电平有效,而b、c和d段时则为高电平。c段为显示时间窗口,其余时段处于消隐(blank)状态。
VGA将显示图像按行划分为若干个像素,按列划分为若干行,采用动态扫描方式刷新图像 基于FPGA实现的VGA应用系统的典型结构如图所示,由锁相环、VGA时序控制器和像素生成电路三部分组成。
VGA时序控制器的设计与开发板VGA接口电路有密切的关系。 DE2-115开发板的VGA接口电路如图所示。
设计过程:
基于上述VGA接口电路,设计VGA时序控制器的结构框图如图所示,由两个过程语句和DAC控制信号生成逻辑语句三部分组成。
根据上述结构框图,描述分辨率为640480@60Hz模式行、场同步信号以及像素坐标X、Y的VGA时序控制器的Verilog HDL代码参考如下:
module VGA_controller (
input wire iVGA_clk, // VGA输入时钟
output wire oVGA_clk, // VGA输出时钟
output reg VGA_HS, // VGA行同步信号
output reg VGA_VS, // VGA场同步信号
output reg [9:0] X, // 像素行坐标X
output reg [9:0] Y, // 像素列坐标Y
output wire DAC_clk, // ADV7123时钟
output wire DAC_sync_n, // ADV7123同步信号
output wire DAC_blank_n // ADV7123消隐信号
);
// 640×480@60Hz行参数定义
localparam H_FRONT = 16; // 前沿
localparam H_SYNC = 96; // 同步头
localparam H_BACK = 48; // 后沿
localparam H_ACT = 640; // 显示段
localparam H_BLANK = H_FRONT+H_SYNC+H_BACK; // 消隐期
localparam H_TOTAL = H_FRONT+H_SYNC+H_BACK+H_ACT; // 总像素
// 640×480@60Hz场参数定义
localparam V_FRONT = 10; // 前沿
localparam V_SYNC = 2; // 同步头
localparam V_BACK = 33; // 后沿
localparam V_ACT = 480; // 显示段
localparam V_BLANK = V_FRONT+V_SYNC+V_BACK; // 消隐期
localparam V_TOTAL = V_FRONT+V_SYNC+V_BACK+V_ACT; // 总行数
// 内部变量定义
reg [9:0] H_cnt; // 行计数器
reg [9:0] V_cnt; // 场计数器
// VGA输出时钟逻辑
assign oVGA_clk = iVGA_clk;
// ADV7123时钟信号逻辑
assign DAC_clk = ~iVGA_clk;
// ADV7123同步信号逻辑
assign DAC_sync_n = 1'b0;
// ADV7123消隐信号逻辑
assign DAC_blank_n = ~((H_cnt<H_BLANK)||(V_cnt<V_BLANK));
// 行同步信号VGA_HS和行像素坐标X生成过程
always @( posedge iVGA_clk ) begin
// 行计数
if( H_cnt < H_TOTAL )
H_cnt <= H_cnt+1'b1;
else
H_cnt <= 0;
// 行同步头生成
if( H_cnt == H_FRONT-1 ) // 检测行前沿结束点
VGA_HS <= 1'b0;
if( H_cnt == H_FRONT+H_SYNC-1 ) // 检测行同步头结束点
VGA_HS <= 1'b1;
// 行像素坐标生成
if ( H_cnt >= H_BLANK)
X <= H_cnt - H_BLANK;
else
X <= 0;
end
// 场同步信号VGA_VS和列坐标Y生成过程
always @( posedge VGA_HS ) begin
// 场计数
if ( V_cnt < V_TOTAL )
V_cnt <= V_cnt+1'b1;
else
V_cnt <= 0;
// 场同步头生成
if( V_cnt == V_FRONT-1 ) // 检测场前沿结束点
VGA_VS <= 1'b0;
if ( V_cnt == V_FRONT+V_SYNC-1 ) // 检测场同步头结束点
VGA_VS <= 1'b1;
// 列像素坐标生成
if ( V_cnt >= V_BLANK )
Y <= V_cnt - V_BLANK;
else
Y <= 0;
end
endmodule
为了测试VGA时序控制器的功能,还需要编写像素生成模块,产生RGB数字量给VGA接口电路,在VGA时序控制器的作用下,驱动接口电路依次在像素坐标点(X、Y)上显示VGA色彩信息。
为方便理解像素的生成原理,可以将VGA显示方案分为按区域显示、按像素显示和按目标显示三种类型。
1.按区域显示
按区域显示是指将VGA显示屏划分为若干个规则的区域,每个区域分配一种色彩。例如,将显示屏按行划分为8个区、按列划分为8个区,则整个屏幕共划分为(8×8=)64个区域。每个区域分配不同的色彩时,则会形成彩色方格图像(color pattern),如图所示。
描述8×8彩色方格图像的Verilog HDL代码参考如下:
module VGA_pattern (VGA_clk,X,Y,VGA_red,VGA_gre,VGA_blue);
input VGA_clk; // 来自iVGA_clk/oVGA_clk
input [9:0] X,Y; // 像素点坐标
output reg [7:0] VGA_red,VGA_gre,VGA_blue;
always @( posedge VGA_clk ) begin // 方格色彩定义
VGA_red <= ( Y < 120 ) ? 64 :
( Y >= 120 && Y < 240 ) ? 128 :
( Y >= 240 && Y < 360 ) ? 192 : 255 ;
VGA_gre <= ( X < 80 ) ? 32 :
( X >= 80 && X < 160 ) ? 64 :
( X >= 160 && X < 240 ) ? 92 :
( X >= 240 && X < 320 ) ? 128 :
( X >= 320 && X < 400 ) ? 160 :
( X >= 400 && X < 480 ) ? 192 :
( X >= 480 && X < 560) ? 224 : 255 ;
VGA_blue <= ( Y < 60 ) ? 255 :
( Y >= 60 && Y < 120 ) ? 224 :
( Y >= 120 && Y < 180 ) ? 192 :
( Y >= 180 && Y < 240 ) ? 160 :
( Y >= 240 && Y < 300 ) ? 128 :
( Y >= 300 && Y < 360 ) ? 96 :
( Y >= 360 && Y < 420 ) ? 64 : 32 ;
end
endmodule
定制锁相环IP(ALTPLL)产生25.2MHz(从c0输出)和2.52MHz(从c1输出)方波信号,分别作为VGA时钟(iVGA_clk)和嵌入式逻辑分析仪的时钟,然后将锁相环、VGA时序控制器(VGA_controller)和像素生成模块(VGA_pattern)连接成图所示的顶层测试电路(VGA_pattern_tst.bdf)。
2.按像素显示
【REVIEW】基于FPGA实现的VGA应用系统的典型结构如图所示,由锁相环、VGA时序控制器和像素生成电路三部分组成。
【REVIEW】为方便理解像素的生成原理,可以将VGA显示方案分为: 1.按区域显示; 2. 按像素显示; 3.按目标显示 三种类型。
按像素显示是指将VGA像素信息存储在ROM/RAM中,每个像素点对应一组存储单元,然后在VGA时序控制器的作用下从ROM/RAM中读取数据显示图像。
对于简单的静态图像显示,可以应用FPGA片上ROM保存每个像素的色彩数据。计算机图像文件格式: BMP,TIFF,JPEG,GIF,PNG...
计算机系统中广泛使用RGB模式表示图像的色彩,有RGB888、RGB565和RGB332等多种模式。 RGB888将红、绿、蓝三基色色彩数据各用一个字节表示,有2563(= 16777216)种色彩组合,相应的图像称为真彩(true color)图像。 RGB565将红、绿、蓝三基色色彩数据用两个字节表示,有65536种色彩组合。 RGB332将红、绿、蓝三基色色彩数据用一个字节表示,有256种色彩组合。
在640×480显示模式下:
存储RGB888需要(640×480×24=)7200kbits;
存储RGB565需要(640×480×16=)4800kbits;
存储RGB332需要(640×480×8=)2400kbits。
设计过程:
从位图文件中提取RGB数据可以通过文件操作语句实现,既可以通过编写C程序实现,也可以应用Matlab中的m语言实现。
从位图文件中提取RGB分量,合成为RGB332色彩数据,并生成存储器初始化文件的C程序参考如下:
#include <stdio.h>
#include <malloc.h>
#define BM 19778 // 位图文件标志:0x424d,对应十进制19778
#define PATH "c:\\tiger.bmp" // 位图文件路径,设位图文件存储在C盘根目录下
/* 子程序1:判断是否为位图文件,位图标志在0~1字节。*/
int IsBitMap(FILE *fp)
{
unsigned short bfType;
fread(&bfType,1,2,fp);
if(bfType==BM) return 1;
else return 0;
}
/* 子程序2:获取位图宽度,参数在18~21字节。 */
long getBitMapWidth(FILE *fp)
{
long biWidth;
fseek(fp,18,SEEK_SET); // 设置文件指针,指向从文件头开始、偏移量为18的位置
fread(&biWidth,1,4,fp); // 读取4个字节
return biWidth; // 返回参数
}
/* 子程序3:获取位图高度,参数在22~25字节。 */
long getBitMapHeight(FILE *fp)
{
long biHeight;
fseek(fp,22,SEEK_SET); // 设置文件指针,指向从文件头开始、偏移量为22的位置
fread(&biHeight,1,4,fp); // 读取4个字节
return biHeight; // 返回参数
}
/* 子程序4:获取每个像素的位数,参数在28~29字节。 */
unsigned short getBitsPerPixel(FILE *fp)
{
unsigned short biBitCount;
fseek(fp,28,SEEK_SET); // 设置文件指针,指向从文件头开始、偏移量为28的位置
fread(&biBitCount,1,2,fp); // 读取2个字节
return biBitCount; // 返回参数
}
/* 子程序5:获取图像数据区的起始位置,参数在10~13字节。 */
long getMapAreaOffBits(FILE *fp)
{
long bfOffBits;
fseek(fp,10,SEEK_SET); // 设置文件指针,指向从文件头开始、偏移量为10的位置
fread(&bfOffBits,1,4,fp); // 读取4个字节
return bfOffBits; // 返回参数
}
/* 子程序6:获取RGB分量值,合成RGB332色彩数据,并建立存储器初始化文件。 */
void getRGBdata(FILE* fp,unsigned char *r,unsigned char *g,unsigned char *b)
{
FILE* fp_rgb; // 定义存储器初始化文件指针
// 变量定义
int i, j=0; // 循环变量
int BytesPerLine; // 行字节数变量
unsigned char* pixel=NULL; // 像素指针,初始化为空指针
long biHeight,biWidth; // 像素高度和宽度变量
unsigned short BitsPerPixel; // 每个像素的位数
long bfOffBits;
long ROM_init_address; // 初始化地址
unsigned char ROM_init_data; // 初始化数据
// 获取位图参数
biHeight=getBitMapHeight(fp); // 获取图像高度
biWidth=getBitMapWidth(fp); // 获取图像宽度
BitsPerPixel=getBitsPerPixel(fp); // 获取每个像素的位数
bfOffBits=getMapAreaOffBits(fp); // 获取图像数据区的起始地址
// 打开存储器初始化文件
fp_rgb=fopen("c:\\tiger_rgb332.mif", "w+");
// 添加参数信息标注
fprintf(fp_rgb,"WIDTH=8;\n"); // 位宽为8
fprintf(fp_rgb,"DEPTH=%ld;\n",biWidth*biHeight); // 单元数=宽度*高度
fprintf(fp_rgb,"ADDRESS_RADIX=UNS;\n"); // 地址以十进制表示
fprintf(fp_rgb,"DATA_RADIX=UNS;\n"); // 数据以十进制表示
fprintf(fp_rgb,"CONTENT BEGIN\n"); // 存储开始标志
// 计算每行占用的字节数,补0对齐
BytesPerLine=((BitsPerPixel*biWidth+31)>>5)<<2;
// 分配行字节缓冲区
pixel=(unsigned char *)malloc(BytesPerLine);
// 像素三基色分解
for(j=biHeight-1;j>=0;j--) // 按行倒序处理
{
// 设置文件指针,指向对应行像素的起始地址
fseek(fp,bfOffBits+j*BytesPerLine,SEEK_SET);
// 读取全行字节数据
fread(pixel,1,BytesPerLine,fp);
// 行像素BGR分解
for(i = 0; i < biWidth; i++)
{
*(b+i)=pixel[i*3]; // 提取B分量,存入蓝色缓冲区 b: b7b6b5b4b3b2b1b0
*(g+i)=pixel[i*3+1]; // 提取G分量,存入绿色缓冲区 g: g7gg6g5g4g3g2g1g0
*(r+i)=pixel[i*3+2]; // 提取R分量,存入红色缓冲区 r: r7r6r5r4r3r2r1r0
}
// 合成RGB332格式数据,写入存储器初始化数据文件 rgb: r7r6r5g4g3g2b1b0
for(i = 0; i < biWidth; i++)
{
ROM_init_address=(biHeight-1-j)*biWidth+i;
ROM_init_data=((*(r+i)>>5)<<5)+((*(g+i)>>5)<<2)+(*(b+i)>>6);
fprintf(fp_rgb,"%6d : %2d;\n",ROM_init_address,ROM_init_data);
}
}
fprintf(fp_rgb,"END;\n"); // 存储数据结束标志
fclose(fp_rgb); // 关闭存储文件
}
int main() // 主程序
{
FILE *fp=fopen(PATH,"r"); // 打开位图文件
unsigned char *r,*g,*b; // 定义行缓冲区指针
// 开辟行像素缓冲区,按每行2000个字节设置
r=(unsigned char *)malloc(2000); // 红色缓冲区
b=(unsigned char *)malloc(2000); // 绿色缓冲区
g=(unsigned char *)malloc(2000); // 蓝色缓冲区
// 判断是否为位图文件
if(IsBitMap(fp)) printf("该文件是位图!\n");
else { printf("该文件不是位图!\n");
// 不进行处理,关闭文件返回
fclose(fp); return 0;
}
// 读取并显示位图信息
printf("位图宽度=%ld,位图高度=%ld\n",
getBitMapWidth(fp),getBitMapHeight(fp));
printf("该图像是%d位图\n",getBitsPerPixel(fp));
printf("图像数据区偏移地址=%d\n",getMapAreaOffBits(fp));
// 读取色彩数据,生成RGB232存储器初始化文件
getRGBdata(fp,r,g,b);
return 1; // 返回操作系统
}
编译并运行C程序,将生成存储器初始化数据文件tiger_rgb332.mif,然后定制ROM,并将tiger_rgb332.mif加载到ROM中。
定制ROM_tiger_rgb332,输出HDL的语言类型为Verilog HDL。