文章目录
前言
我们在上一篇文章中已经对VGA视频传输标准作了详细的介绍,包括VGA接口协议、行/场同步时序、以及显示分辨率等,也实现了基于VGA接口标准的等宽彩条的显示实验,如果对VGA的基本概念不是很熟悉的话,可以去看看VGA彩条显示这部分的内容。
在上篇文章的结尾,提高还可以利用VGA接口标准来显示其他图片、字符,在本次文章中,我们就一起来完成VGA的字符显示的内容,达到一个复习巩固的效果。
一、实验任务
本篇的实验任务就是使用VGA接口标准,在显示器的中间显示“卡布奇洛”这四个汉字。
显示模式:640*480@60
点阵大小:64*64
字符大小:56*56
二、字符取模
字符(包括汉字、字母和符号等)的本质是点阵,在VGA屏幕上体现为字符显示区域内像素点的集合。字符的大小决定了字符显示区域内像素点的数目,而字符的样式(字体、颜色等)则决定了各像素点的颜色值。因此,我们在进行字符显示之前,先要对指定的字符进行字符取模,获得该字符的点阵数据。
为了获得指定字符的点阵数据,就需要对字符进行取模处理,字模的提取可以通过字符取模软件来实现,我们这里所使用的取模软件为PCtoLCD2002来提取“卡布奇洛”这四个汉字的字模,下面就是使用PCtoLCD2002软件进行字符取模的过程。
① 双击打开PCtoLCD2002字符取模软件,将模式修改为字符模式。
② 根据显示要求修改点阵大小与字符大小,我这里点阵大小为64*64,字符大小为56*56。
③ 在点阵大小与字符大小设置完成后,我们在输入框中输入我们要生成字符的汉字,我这里输入的是“卡布奇洛”。
④ 这时就显示出了字模,但是这时不要生成字模,因为这时生成字模是单个汉字的字模,不是我们需要的整体的字模。这时我们点击另存为,保存到我们项目工程的doc文件夹下,保存的格式为.bmp格式。
⑤ 保存完成过后,在软件的文件目录下选择打开,打开刚刚保存的字模,然后打开选项进行字模的配置。
⑥ 参数设置完成后点击确定,然后点击生成字模,保存字模。
⑦ 最后就可以看到我们的doc文件夹下有生成的字模数据了。
到此为止呢我们的字模生成算是完成了,我们保存的字模的点阵大小是256*64,因为我们一个点阵是64*64,而我们有四个点阵,也就是64*4=256,所以我们的整体字模的点阵大小就是256*64。
三、程序设计
由于本次实验工程是在上一个实验工程上进行了修改,所以上一个实验工程的一些模块是可以直接进行复用的,这里主要需要更改VGA的显示模块,其余两个模块可以进行复用。
① VGA字符显示顶层模块:
/*====================================*
filename : vga_char_top.v
description : VGA字符显示顶层模块
time : 2022-12-29
author : 卡夫卡与海
*=====================================*/
module vga_char_top(
input clk ,//系统时钟
input rst_n ,//复位
output vga_hsync ,//行同步信号
output vga_vsync ,//场同步信号
output [15:0] vga_rgb //数据输出(红绿蓝)
);
//信号定义
wire vga_clk ;//vga工作时钟 25MHZ
wire locked ;
wire vga_reset ;
wire [9:0] pix_x ;
wire [9:0] pix_y ;
wire [15:0] pix_data ;
assign vga_reset = rst_n && locked;
//模块例化
//PLL
CLK_PLL CLK_PLL_inst (
.areset (~rst_n ),
.inclk0 (clk ),
.c0 (vga_clk),
.locked (locked )
);
//vga控制模块
vga_control u_vga_control(
/*input */.clk_25 (vga_clk ),//时钟 25MHZ
/*input */.rst_n (vga_reset),//复位
/*input [15:0] */.pix_data (pix_data ),//输入图像数据
/*output [9:0] */.pix_x (pix_x ),//横坐标
/*output [9:0] */.pix_y (pix_y ),//纵坐标
/*output */.hsync (vga_hsync),//行同步信号
/*output */.vsync (vga_vsync),//场同步信号
/*output [15:0] */.vga_rgb (vga_rgb ) //输出图像数据(RGB565格式)
);
//vga显示模块
vga_display u_vga_display(
/*input */.clk_25 (vga_clk ),//VGA驱动时钟
/*input */.rst_n (vga_reset),//复位
/*input [9:0] */.pix_x (pix_x ),//横坐标
/*input [9:0] */.pix_y (pix_y ),//纵坐标
/*output reg [15:0] */.pix_data (pix_data ) //数据
);
endmodule
② VGA控制模块:
/*===============================*
filename : vga_control.v
description : VGA控制模块
time : 2022-12-28
author : 卡夫卡与海
*================================*/
module vga_control(
input clk_25 ,//时钟 25MHZ
input rst_n ,//复位
input [15:0] pix_data ,//输入图像数据
output [9:0] pix_x ,//横坐标
output [9:0] pix_y ,//纵坐标
output hsync ,//行同步信号
output vsync ,//场同步信号
output [15:0] vga_rgb //输出图像数据(RGB565格式)
);
//参数定义
//显示模式:640*480@60
parameter H_SYNC = 96 ,//行同步
H_BACK = 48 ,//行显示后沿
H_DISP = 640,//行有效数据
H_FRONT = 16 ,//行显示前沿
H_TOTAL = 800;//行扫描周期
parameter V_SYNC = 2 ,//场同步
V_BACK = 33 ,//场显示后沿
V_DISP = 480,//场有效数据
V_FRONT = 10 ,//场显示前沿
V_TOTAL = 525;//场扫描周期
//信号定义
reg [9:0] cnt_h ;//行计数器
reg [9:0] cnt_v ;//场计数器
wire rgb_vaild ;//数据有效信号
wire data_req ;//数据请求信号
//cnt_h
always @(posedge clk_25 or negedge rst_n)begin
if(!rst_n)begin
cnt_h <= 10'd0;
end
else begin
if(cnt_h < H_TOTAL - 1)begin
cnt_h <= cnt_h + 1'b1;
end
else begin
cnt_h <= 10'd0;
end
end
end
//cnt_v
always @(posedge clk_25 or negedge rst_n)begin
if(!rst_n)begin
cnt_v <= 10'd0;
end
else if(cnt_h == H_TOTAL - 1)begin
if(cnt_v < V_TOTAL - 1)begin
cnt_v <= cnt_v + 1'b1;
end
else begin
cnt_v <= 10'd0;
end
end
end
//rgb_vaild 数据输出有效信号
assign rgb_vaild = ((cnt_h >= H_BACK + H_SYNC)
&&(cnt_h < H_SYNC + H_BACK + H_DISP)
&&(cnt_v >= V_SYNC + V_BACK)
&&(cnt_v < V_SYNC + V_BACK + V_DISP))
? 1'b1 : 1'b0;
//data_req 像素点颜色数据输入请求信号
assign data_req = (((cnt_h >= H_SYNC + H_BACK - 1)
&& (cnt_h < H_SYNC + H_BACK + H_DISP - 1))
&& ((cnt_v >= V_SYNC + V_BACK)
&& (cnt_v < V_SYNC + V_BACK + V_DISP)))
? 1'b1 : 1'b0;
//输出
//pix_x 横坐标
assign pix_x = (data_req==1'b1)?(cnt_h - (H_SYNC+H_BACK-1)):10'h3ff;
//pix_y 纵坐标
assign pix_y = (data_req==1'b1)?(cnt_v - (V_SYNC+V_BACK-1)):10'h3ff;
//hsync
assign hsync = (cnt_h <= H_SYNC - 1) ? 1'b0 : 1'b1;
//vsync
assign vsync = (cnt_v <= V_SYNC - 1) ? 1'b0 : 1'b1;
//vga_rgb
assign vga_rgb = (rgb_vaild == 1'b1) ? pix_data : 16'd0;
endmodule
③ VGA显示模块:
/*===============================*
filename : vga_display.v
description : VGA显示模块
time : 2022-12-29
author : 卡夫卡与海
*================================*/
module vga_display(
input clk_25 ,//VGA驱动时钟
input rst_n ,//复位
input [9:0] pix_x ,//横坐标
input [9:0] pix_y ,//纵坐标
output reg [15:0] pix_data //数据
);
//参数定义
parameter CHAR_B_H = 10'd192 ,//字符开始横坐标
CHAR_B_V = 10'd208 ;//字符开始纵坐标
parameter CHAR_W = 10'd256 ,//字符宽度
CHAR_H = 10'd56 ;//字符深度
//颜色参数 RGB565格式
parameter BLACK = 16'h0000,//黑色(背景色)
GOLDEN = 16'hFEC0;//金色(字符颜色)
//信号定义
wire [9:0] char_x ;//字符横坐标
wire [9:0] char_y ;//字符纵坐标
reg [255:0] char [63:0] ;
//char_x
assign char_x = (((pix_x >= CHAR_B_H)&&(pix_x < (CHAR_B_H + CHAR_W)))
&&((pix_y >= CHAR_B_V)&&(pix_y < (CHAR_B_V + CHAR_H))))
? (pix_x - CHAR_B_H) : 10'h3ff;
//char_y
assign char_y = (((pix_x >= CHAR_B_H)&&(pix_x < (CHAR_B_H + CHAR_W)))
&&((pix_y >= CHAR_B_V)&&(pix_y < (CHAR_B_V + CHAR_H))))
? (pix_y - CHAR_B_H) : 10'h3ff;
//char
always @(posedge clk_25)begin
char[0] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[1] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[2] <= 256'h0000000000000000000000000000000000000020000000000000000000000000;
char[3] <= 256'h000000C00000000000000030000000000000003C000000000000000000000000;
char[4] <= 256'h000000F8000000000000003C000000000000003E000000000000000000000000;
char[5] <= 256'h0000007C000000000000003E000000000000001C000000000000000E00000000;
char[6] <= 256'h0000007C000000000000003C000000000000001C000000000000000F00000000;
char[7] <= 256'h0000003C000000000000003C000000000000001C000000000000000F80000000;
char[8] <= 256'h0000003C0000000000000078000000000000001C000000000000000F80000000;
char[9] <= 256'h0000003C1F00000000000078000000000000001C1C000000000C000F00000000;
char[10] <= 256'h0000003DFF80000000000070001F00000000001FFF000000000F000E00000000;
char[11] <= 256'h0000003FFE000000000000701FFFC000000001FFFF000000000F801E1F000000;
char[12] <= 256'h0000003FC0000000000000FFFFFFE00000007FFF800000000007C01FFF800000;
char[13] <= 256'h0000003C0000000000007FFFFFFFC00000003FFC000000000003C03FFF800000;
char[14] <= 256'h000000180000000000FFFFFC000000000000003800000000000180701F000000;
char[15] <= 256'h0000001800000000007FFDC0000000000000003800000000000000701E000000;
char[16] <= 256'h0000001800000000003E03C7800000000000007C00000000000000E01E000000;
char[17] <= 256'h000000180000000000000383C000000000000077C0000000000001C03C000000;
char[18] <= 256'h00000018007FC00000000783C0000000000000E3E00000000000038038000000;
char[19] <= 256'h000000181FFFE00000000703C0000000000001C1F0000000000007F078000000;
char[20] <= 256'h0000001FFFFFE00000000E03C000000000000380F00000000200083870000000;
char[21] <= 256'h000001FFFFC0000000001E0380000000000007003000000007C0001EE0000000;
char[22] <= 256'h0000FFFF0000000000001C038000000000000E00001FE00003F0000FE0000000;
char[23] <= 256'h07FFFFF8000000000000380380E00000000038000FFFF00001F00003C0000000;
char[24] <= 256'h07FFF07C0000000000007803BFF8000000004007FFFFF80000F00007E0000000;
char[25] <= 256'h01FE003C0000000000007FFFFFFC000000000FFFFFF020000070000FF0000000;
char[26] <= 256'h0000003C000000000000EFFFC07C0000007FFFFE780000000000000F7C000000;
char[27] <= 256'h00000038000000000001CE03807C000007FFFC003C0000000000001E3E000000;
char[28] <= 256'h00000018000000000003CE038078000003FC00003C0000000001003C1F800000;
char[29] <= 256'h00000018E000000000078E03803800000000006038000000000200780FC00000;
char[30] <= 256'h00000018F8000000000F0E0380380000000001F838000000000200F007F00000;
char[31] <= 256'h000000187E000000001E0E038038000000039FFC38000000000601C001FC0000;
char[32] <= 256'h000000183F000000003C0E03803800000001F83C38000000000C078000FF8000;
char[33] <= 256'h000000181F80000000700E03807800000001C03838000000000C0F00007FF800;
char[34] <= 256'h000000180FC0000000E00E03807800000001C0383800000000181C00003FF000;
char[35] <= 256'h0000001807E0000001800E03907000000000C030380000000018780006000000;
char[36] <= 256'h0000001803E0000007000E038FF000000000C070380000000038E0007F800000;
char[37] <= 256'h0000001800E0000008001E0387F000000000C3F03C00000000730F1FFFE00000;
char[38] <= 256'h000000180000000000001C0381F000000000FFF83C00000000F007FF03F00000;
char[39] <= 256'h000000180000000000001C0381E000000000F0003C00000000E0078003F00000;
char[40] <= 256'h000000180000000000000C0380C000000000C0003C00000003E0078003E00000;
char[41] <= 256'h00000018000000000000080380800000000040003C00000003E0038003C00000;
char[42] <= 256'h00000038000000000000000380000000000000003C00000003C0038003800000;
char[43] <= 256'h00000038000000000000000380000000000000003C00000003C0038003800000;
char[44] <= 256'h00000038000000000000000380000000000000003C00000003C0038007000000;
char[45] <= 256'h00000038000000000000000380000000000000003C00000001C0038007000000;
char[46] <= 256'h000000780000000000000003800000000000000C3C00000001C00383FF800000;
char[47] <= 256'h0000007800000000000000038000000000000007FC000000008003FFFF800000;
char[48] <= 256'h0000007800000000000000018000000000000003F8000000000001FF80000000;
char[49] <= 256'h0000003800000000000000018000000000000001F80000000000018000000000;
char[50] <= 256'h0000003800000000000000018000000000000000F80000000000010000000000;
char[51] <= 256'h0000001000000000000000010000000000000000F00000000000000000000000;
char[52] <= 256'h0000000000000000000000010000000000000000600000000000000000000000;
char[53] <= 256'h0000000000000000000000010000000000000000000000000000000000000000;
char[54] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[55] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[56] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[57] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[58] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[59] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[60] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[61] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[62] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[63] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
end
//pix_data
always @(posedge clk_25 or negedge rst_n)begin
if(!rst_n)begin
pix_data <= BLACK;
end
else if(((pix_x >= CHAR_B_H-1)&&(pix_x < (CHAR_B_H + CHAR_W-1)))
&&((pix_y >= CHAR_B_V)&&(pix_y < (CHAR_B_V + CHAR_H)))
&&(char[char_x][char_y] == 1'b1))begin
pix_data <= GOLDEN;
end
else begin
pix_data <= BLACK;
end
end
endmodule
④ VGA仿真模块:
/*==================================*
filename : vga_top_tb.v
description : VGA顶层模块仿真
time : 2022-12-28
author : 卡夫卡与海
*===================================*/
`timescale 1ns/1ns
module vga_top_tb();
reg clk ;//50MHZ
reg rst_n ;
wire hsync ;
wire vsync ;
wire [15:0] vga_rgb ;
//产生时钟、复位
initial begin
clk = 1'b1;
rst_n = 1'b0;
#20
rst_n = 1'b1;
end
always #10 clk = ~clk;
//模块例化
vga_char_top u_vga_char_top(
/*input */.clk (clk ),//系统时钟
/*input */.rst_n (rst_n ),//复位
/*output */.vga_hsync (hsync ),//行同步信号
/*output */.vga_vsync (vsync ),//场同步信号
/*output [15:0] */.vga_rgb (vga_rgb) //数据输出(红绿蓝)
);
endmodule
由于我这里设备问题,这次就没有进行上板验证了,接下来也就是绑定引脚进行上板验证就可以了,感兴趣的小伙伴可自行上板,看看效果如何,哈哈!
总结
本篇文章是在上一篇的基础上进行改进的,有许多原理性的内容可以参考上一篇文章,这里只是完成了上次遗留下来的问题罢了。字符显示的原理也是很清晰的,最重要的就是要抓住字符显示的范围,以及对字符显示进行赋值操作,和彩条显示原理差不多。