前言
VGA(Video Graphics Array)是IBM在1987年随PS/2机一起推出的一种视频传输标准,具有分辨率高、显示速率快、颜色丰富等优点,在彩色显示器领域得到了广泛的应用。VGA技术的应用还主要基于VGA显示卡的计算机、笔记本等设备,而在一些既要求显示彩色高分辨率图像又没有必要使用计算机的设备上,VGA技术的应用却很少见到。本文对嵌入式VGA显示的实现方法进行了研究。基于这种设计方法的嵌入式VGA显示系统,实现VGA图像的显示和控制。系统具有成本低、结构简单、应用灵活的优点,可广泛应用于超市、车站、飞机场等公共场所的广告宣传和提示信息显示,也可应用于工厂车间生产过程中的操作信息显示,还能以多媒体形式应用。
正文
一、VGA显示字符
1.项目需求
通过vga接口,使得显示屏上显示FPGA四个字符(理论讲解)
2.技术介绍
VGA驱动原理请参考:VGA接口驱动设计验证
本章讲解如何显示字符: 首先需要一取字模的工具,这里使用PCtoLCD2002(资料私信1发送)
输入需要取模的字符,设置字符大小:
这里设置单个字母的空间,为字符画区域
这里设置单个字母大小,设置字符的高矮胖瘦
以上需要自行设置,在下方对话框中输入需要显示的字符,先另存为.BMP文件,再次使用该软件打开保存的.BMP文件。
点击选项,进行如下图配置
点击确定,点击生成字模,保存字模,以下为字模提取结构
显示字符,确定显示起始坐标左上角坐标(字符定位点),并以该点向XY方向进行拓展,定义字符边框,定义一二维数数组存放字符数据,
parameter show_h = 10'd110,//字模定位点Y坐标
show_v = 10'd100;//字模定位点X坐标
parameter show_chan = 10'd256,//图片长度
show_kuan = 10'd32;//图片宽度
以确定的字符定位点为(0.0)定义两计数器,对字符区域重新编码,得到新的字符坐标,
wire[9:0] char_x;//字模Y坐标
wire[9:0] char_y;//字模X坐标
assign char_x =((pix_x >= show_h)&&(pix_x < (show_h + show_chan)))&&((pix_y >= show_v)&&(pix_y <(show_v + show_kuan)))?(pix_x - show_h):10'h3ff;//字模Y坐标数据生成
assign char_y =((pix_x >= show_h)&&(pix_x < (show_h + show_chan)))&&((pix_y >= show_v)&&(pix_y <(show_v + show_kuan)))?(pix_y - show_v):10'h3ff;字模X坐标数据生成
新的字符坐标与二维数组对应,以该坐标为二维数组的编号,对数组内部数据进行判断
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
vga_rgb <= show_1_rgb;
else
if( ((pix_x >= show_h - 1'b1)&&(pix_x < (show_h + show_chan - 1'b1)))
&& ((pix_y >= show_v )&&(pix_y <(show_v + show_kuan)))
&& (char[char_y][10'd255 - char_x] == 1'b1))//对字模区域进行颜色赋值
vga_rgb <= show_0_rgb;
else
vga_rgb <= show_1_rgb;//非字模区域颜色为背景颜色
end
有数据时给目标颜色,没有数据时显示背景颜色。
3.顶层架构
4.端口描述
clk | 板载时钟(50Mhz) |
rst_n | 复位按键(低电平有效) |
vga_hs | 行脉冲信号 |
vga_vs | 场脉冲信号 |
[7:0] vga_rgb | 颜色数据信号,高三位表示红色, 中间三位表示绿色,低两位表示蓝色 |
[9:0]vga_v | x坐标 |
[9:0]vga_h | y坐标 |
二、代码验证
vga_ctrl模块:原理参考VGA接口驱动设计验证
module vga_ctrl(
input clk ,
input rst_n ,
input[7:0] in_rgb ,
output vga_hs ,
output vga_vs ,
output[9:0] vga_h ,//坐标
output[9:0] vga_v ,//坐标
output[7:0] vga_rgb
);
reg [9:0] cnt_hs;//列计数器
reg [9:0] cnt_vs;//行计数器
wire hs_en;//列有效显示区域
wire vs_en;//行有效显示区域
wire volid_en;//有效显示区域
parameter hs_sync = 10'd96 ,//同步
hs_bask = 10'd40 ,//后沿
hs_left = 10'd8 ,//左边
hs_vali = 10'd640 ,//有效
hs_righ = 10'd8 ,//右边
hs_fpon = 10'd8 ,//前沿
hs_tota = 10'd800 ;//总共
parameter vs_sync = 10'd2 ,//同步
vs_bask = 10'd25 ,//后沿
vs_left = 10'd8 ,//上边
vs_vali = 10'd480 ,//有效
vs_righ = 10'd8 ,//底边
vs_fpon = 10'd2 ,//前沿
vs_tota = 10'd525 ;//总共
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt_hs <= 10'd0;
else
if(cnt_hs < hs_tota - 10'd1)
cnt_hs <= cnt_hs + 10'd1;
else
cnt_hs <= 10'd0;
end
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt_vs <= 10'd0;
else
if(cnt_hs == hs_tota - 10'd1)
if(cnt_vs < vs_tota - 10'd1)
cnt_vs <= cnt_vs + 10'd1;
else
cnt_vs <= 10'd0;
else
cnt_vs <= cnt_vs;
end
assign vga_hs = (cnt_hs < hs_sync)?1'b0:1'b1;//行同步信号赋值
assign vga_h = (volid_en == 1'b1)?(cnt_hs - (hs_sync + hs_bask + hs_left)):10'd0;//行坐标(Y)
assign vga_vs = (cnt_vs < vs_sync)?1'b0:1'b1;//场同步信号赋值
assign vga_v = (volid_en == 1'b1)?(cnt_vs - (vs_sync + vs_bask + vs_left)):10'd0;//列坐标(X)
assign hs_en = ((cnt_hs >= hs_sync + hs_bask + hs_left )&&(cnt_hs <= hs_sync + hs_bask + hs_left + hs_vali))?1'b1:1'b0;
assign vs_en = ((cnt_vs >= vs_sync + vs_bask + vs_left )&&(cnt_vs <= vs_sync + vs_bask + vs_left + vs_vali))?1'b1:1'b0;
assign volid_en = hs_en & vs_en;//显示区域
assign vga_rgb = (volid_en == 1'b1)?in_rgb:8'd0;
endmodule
vga_data模块:保存字符取模结果,进行RGB数据生成
module vga_data(
input clk ,
input rst_n ,
input[9:0] pix_x ,
input[9:0] pix_y ,
output reg[7:0] vga_rgb
);
parameter show_h = 10'd110,//字模定位点Y坐标
show_v = 10'd100;//字模定位点X坐标
parameter show_chan = 10'd256,//图片长度
show_kuan = 10'd32;//图片宽度
parameter show_1_rgb = 8'b000_000_00,//背景
show_0_rgb = 8'b111_100_00;//图片颜色
reg [255:0] char [31:0];
wire[9:0] char_x;//字模Y坐标
wire[9:0] char_y;//字模X坐标
assign char_x =((pix_x >= show_h)&&(pix_x < (show_h + show_chan)))&&((pix_y >= show_v)&&(pix_y <(show_v + show_kuan)))?(pix_x - show_h):10'h3ff;//字模Y坐标数据生成
assign char_y =((pix_x >= show_h)&&(pix_x < (show_h + show_chan)))&&((pix_y >= show_v)&&(pix_y <(show_v + show_kuan)))?(pix_y - show_v):10'h3ff;字模X坐标数据生成
always@(posedge clk)//生成的字模数据
begin
char[0 ] <= 256'h0000000000000000000000000001000000200000000100000020000000010000;
char[1 ] <= 256'h0020000000010000003FFFFFFFFF0000003FFFFFFFFF0000003FFFFFFFFF0000;
char[2 ] <= 256'h003FFFFFFFFF0000003000180001000000300018000100000030001800010000;
char[3 ] <= 256'h0030001800010000003000180000000000300018000000000030001800000000;
char[4 ] <= 256'h003000180000000000300018000000000030003C000000000030007E00000000;
char[5 ] <= 256'h003803FFE00000000038000000000000003C000000000000003E000000000000;
char[6 ] <= 256'h003F8000000000000003C0000000000000006000000000000000000000000000;
char[7 ] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[8 ] <= 256'h0000000000000000000000000001000000200000000100000020000000010000;
char[9 ] <= 256'h0020000000010000003FFFFFFFFF0000003FFFFFFFFF0000003FFFFFFFFF0000;
char[10 ] <= 256'h003FFFFFFFFF0000003000060001000000300006000100000030000600010000;
char[11 ] <= 256'h0030000600010000003000060000000000300006000000000030000600000000;
char[12 ] <= 256'h00300006000000000038000E000000000038000E00000000001C001C00000000;
char[13 ] <= 256'h001E003C00000000000F80F800000000000FFFF8000000000007FFF000000000;
char[14 ] <= 256'h0001FFC00000000000007F000000000000000000000000000000000000000000;
char[15 ] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[16 ] <= 256'h00000000000000000000000000000000000000FFE000000000000FFFFE000000;
char[17 ] <= 256'h00007FFFFF8000000000FFFFFFE000000003FE001FF800000007E00000FC0000;
char[18 ] <= 256'h000F0000003C0000001E0000000E0000001C0000000600000038000000070000;
char[19 ] <= 256'h0030000000030000003000000003000000300000000300000030000100030000;
char[20 ] <= 256'h00300001000300000018000100060000001C0001800E0000001E0001FFFC0000;
char[21 ] <= 256'h001F8001FFF80000001FF001FFF8000000003801FFF800000000000180000000;
char[22 ] <= 256'h0000000100000000000000010000000000000001000000000000000000000000;
char[23 ] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[24 ] <= 256'h00000000000100000000000000010000000000000003000000000000001F0000;
char[25 ] <= 256'h0000000001FF0000000000001FE3000000000001FC0100000000001FC0010000;
char[26 ] <= 256'h000001FCC000000000001FC0C00000000001FC00C0000000001FC000C0000000;
char[27 ] <= 256'h003E0000C0000000007F0000C0000000007FF000C0000000003FFF00C0000000;
char[28 ] <= 256'h0001FFF0C000000000001FFFC0000000000001FFFC0100000000001FFFC10000;
char[29 ] <= 256'h00000001FFFF0000000000000FFF00000000000000FF000000000000000F0000;
char[30 ] <= 256'h0000000000030000000000000001000000000000000100000000000000000000;
char[31 ] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
end
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
vga_rgb <= show_1_rgb;
else
if( ((pix_x >= show_h - 1'b1)&&(pix_x < (show_h + show_chan - 1'b1)))
&& ((pix_y >= show_v )&&(pix_y <(show_v + show_kuan)))
&& (char[char_y][10'd255 - char_x] == 1'b1))//对字模区域进行颜色赋值
vga_rgb <= show_0_rgb;
else
vga_rgb <= show_1_rgb;//非字模区域颜色为背景颜色
end
endmodule
顶层模块:顶层连线
module vga_driver(
input clk ,
input rst_n ,
output vga_hs ,//行脉冲信号,由列计数器控制产生
output vga_vs ,//场脉冲信号,由行计数器控制产生
output[9:0] vga_h ,//坐标
output[9:0] vga_v ,//坐标
output[7:0] vga_rgb/*颜色数据信号,高三位表示红色,
中间三位表示绿色,低两位表示蓝色*/
);
wire vga_clk;//25.2mhz
wire locked;
wire [7:0]in_rgb;
vga_pll vga_pll_inst (
.areset ( ~rst_n ),
.inclk0 ( clk ),
.c0 ( vga_clk ),
.locked ( locked )
);
vga_ctrl vga_ctrl_inst(
.clk (clk) ,//vga_clk
.rst_n (locked) ,
.in_rgb (in_rgb) ,
.vga_hs (vga_hs) ,
.vga_vs (vga_vs) ,
.vga_h (vga_h) ,
.vga_v (vga_v) ,
.vga_rgb (vga_rgb)
);
vga_data vga_data_inst(
.clk (clk ) ,
.rst_n (locked ) ,
.pix_x (vga_h ) ,
.pix_y (vga_v ) ,
.vga_rgb(in_rgb)
);
endmodule
仿真代码
`timescale 1ns/1ps
module vga_driver_tb;
reg clk ;
reg rst_n ;
wire vga_hs;
wire vga_vs;
wire[9:0] vga_h ;//坐标
wire[9:0] vga_v ;//坐标
wire[7:0] vga_rgb;
vga_driver vga_driver_instx(
.clk (clk) ,
.rst_n (rst_n) ,
.vga_hs (vga_hs) ,//行脉冲信号,由列计数器控制产生
.vga_vs (vga_vs) ,//场脉冲信号,由行计数器控制产生
.vga_h (vga_h) ,
.vga_v (vga_v) ,
.vga_rgb (vga_rgb)/*颜色数据信号,高三位表示红色,
中间三位表示绿色,低两位表示蓝色*/
);
initial clk = 0;
always #10 clk = ~clk;
initial begin
rst_n = 0;
#200
rst_n = 1;
#1000
$stop;
end
endmodule
三、仿真验证
该实验仿真需等较长时间,这里只讲解到有vga数据输出
这里可以看到有数据产出
放大仿真图进行分析,输出第一次数据变化与(116 . 111),而字符起始坐标为(100 . 110)这是由于字符在(100 . 110)显示的是字模提取后的字符背景区域,而(116 . 111)是字符区域。