Verilog实现VGA字符显示
实现目标
在显示器中以640*480的分辨率显示0-9、A-Z、‘:’、‘*’的任意字符,字符大小为7*8的像素规模。
实现原理
1、基本的VGA显示
所有VGA显示差不多都是基于下面这一段Verilog代码的,时钟分频、行同步信号hs、场同步信号vs、rgb颜色值,这些都是必不可少的,还要注意的是对于640*480的分辨率,显示器真正可显示的区域为行计数介于31~510、列计数介于144~783之间(实际上对不同的显示器可能会有几个像素的差异),在这个范围之外都必须把rgb值设置为全0,否则会对显示造成干扰。
下面代码效果为在显示器屏幕中央显示一个绿色矩形方块。
// vga_display.v
`timescale 1ns / 1ps
module vga_display(
input clk,
input rst,
output reg [2:0] r,
output reg [2:0] g,
output reg [1:0] b,
output hs,
output vs
);
// 显示器可显示区域
parameter UP_BOUND = 31;
parameter DOWN_BOUND = 510;
parameter LEFT_BOUND = 144;
parameter RIGHT_BOUND = 783;
// 屏幕中央的矩形方块
parameter up_pos = 211;
parameter down_pos = 330;
parameter left_pos = 384;
parameter right_pos = 543;
wire pclk;
reg [1:0] count;
reg [9:0] hcount, vcount;
// 获得像素时钟25MHz
assign pclk = count[1];
always @ (posedge clk or posedge rst)
begin
if (rst)
count <= 0;
else
count <= count+1;
end
// 列计数与行同步
assign hs = (hcount < 96) ? 0 : 1;
always @ (posedge pclk or posedge rst)
begin
if (rst)
hcount <= 0;
else if (hcount == 799)
hcount <= 0;
else
hcount <= hcount+1;
end
// 行计数与场同步
assign vs = (vcount < 2) ? 0 : 1;
always @ (posedge pclk or posedge rst)
begin
if (rst)
vcount <= 0;
else if (hcount == 799) begin
if (vcount == 520)
vcount <= 0;
else
vcount <= vcount+1;
end
else
vcount <= vcount;
end
// 设置显示信号值
always @ (posedge pclk or posedge rst)
begin
if (rst) begin
r <= 0;
g <= 0;
b <= 0;
end
else if (vcount>=UP_BOUND && vcount<=DOWN_BOUND
&& hcount>=LEFT_BOUND && hcount<=RIGHT_BOUND) begin
if (vcount>=up_pos && vcount<=down_pos
&& hcount>=left_pos && hcount<=right_pos) begin
r <= 3'b000;
g <= 3'b111;
b <= 2'b00;
end
else begin
r <= 3'b000;
g <= 3'b000;
b <= 2'b00;
end
end
else begin
r <= 3'b000;
g <= 3'b000;
b <= 2'b00;
end
end
endmodule
2、基于RAM和字模的VGA显示
我们都知道电脑有显存,它存储了显示器每个像素应该显示的rgb颜色值,显存不断被刷新,于是可以显示动态的图像。基于此,最简单的想法就是可以在Verilog里面定义一个640*480的8位寄存器二维数组作为VGA显示的显存,然后在reset信号使能的时候对每个数组单元赋值,这样我们就用一个数组存储了一幅静态图像,然后在VGA扫描显示的时候,分别根据寄存器数组的值来设置各个像素的rgb值。
显然,上面的方法是可行的,只不过工程浩大,难以实现!试想,要在屏幕显示一个7*8的字符就要分别设置56个寄存器的值,而且每个寄存器都要根据不同的字符设置不同的值,这是多么可怕的事情!
除此之外,可以考虑通过一个通用的RAM_set模块来一次性设置一个字符对应的寄存器的值。以显示两个字符为例(字符颜色白色,背景黑色),通过Verilog声明一个长度为14的8位寄存器数组reg [7:0] p[13:0],因为两个字符占用2*7*8=14*8个像素,所以p[i][j]=0表示第i+1列第j+1行像素为黑色,反之1为白色。这样的话,就可以分别把p[13:7]、p[6:0]通过RAM_set来设置。RAM_set模块可以根据要显示的不同字符的字模给寄存器赋不同的值。

图1 常见字符字模图
实现代码
下面的代码实现在显示器屏幕中央显示两个字符"3D"。
// vga_char_display.v
`timescale 1ns / 1ps
module vga_char_display(
input clk,
input rst,
output reg [2:0] r,
output reg [2:0] g,
output reg [1:0] b,
output hs,
output vs
);
// 显示器可显示区域
parameter UP_BOUND = 31;
parameter DOWN_BOUND = 510;
parameter LEFT_BOUND = 144;
parameter RIGHT_BOUND = 783;
// 屏幕中央两个字符的显示区域
parameter up_pos = 267;
parameter down_pos = 274;
parameter left_pos = 457;
parameter right_pos = 470;
wire pclk;
reg [1:0] count;
reg [9:0] hcount, vcount;
wire [7:0] p[13:0];
RAM_set u_ram_1 (
.clk(clk),
.rst(rst),
.data(6'b00_0011),
.col0(p[0]),
.col1(p[1]),
.col2(p[2]),
.col3(p[3]),
.col4(p[4]),
.col5(p[5]),
.col6(p[6])
);
RAM_set u_ram_2 (
.clk(clk),
.rst(rst),
.data(6'b00_1101),
.col0(p[7]),
.col1(p[8]),
.col2(p[9]),
.col3(p[10]),
.col4(p[11]),
.col5(p[12]),
.col6(p[13])
);
// 获得像素时钟25MHz
assign pclk = count[1];
always @ (posedge clk or posedge rst)
begin
if (rst)
count <= 0;
else
count <= count+1;
end
// 列计数与行同步
assign hs = (hcount < 96) ? 0 : 1;
always @ (posedge pclk or posedge rst)
begin
if (rst)
hcount <= 0;
else if (hcount == 799)
hcount <= 0;
else
hcount <= hcount+1;
end
// 行计数与场同步
assign vs = (vcount < 2) ? 0 : 1;
always @ (posedge pclk or posedge rst)
begin
if (rst)
vcount <= 0;
else if (hcount == 799) begin
if (vcount == 520)
vcount <= 0;
else
vcount <= vcount+1;
end
else
vcount <= vcount;
end
// 设置显示信号值
always @ (posedge pclk or posedge rst)
begin
if (rst) begin
r <= 0;
g <= 0;
b <= 0;
end
else if (vcount>=UP_BOUND && vcount<=DOWN_BOUND
&& hcount>=LEFT_BOUND && hcount<=RIGHT_BOUND) begin
if (vcount>=up_pos && vcount<=down_pos
&& hcount>=left_pos && hcount<=right_pos) begin
if (p[hcount-left_pos][vcount-up_pos]) begin
r <= 3'b111;
g <= 3'b111;
b <= 2'b11;
end
else begin
r <= 3'b000;
g <= 3'b000;
b <= 2'b00;
end
end
else begin
r <= 3'b000;
g <= 3'b000;
b <= 2'b00;
end
end
else begin
r <= 3'b000;
g <= 3'b000;
b <= 2'b00;
end
end
endmodule
// RAM_set.v `timescale 1ns / 1ps module RAM_set( input clk, input rst, input [5:0] data, output reg [7:0] col0, output reg [7:0] col1, output reg [7:0] col2, output reg [7:0] col3, output reg [7:0] col4, output reg [7:0] col5, output reg [7:0] col6 ); always @(posedge clk or negedge rst) begin if (!rst) begin col0 <= 8'b0000_0000; col1 <= 8'b0000_0000; col2 <= 8'b0000_0000; col3 <= 8'b0000_0000; col4 <= 8'b0000_0000; col5 <= 8'b0000_0000; col6 <= 8'b0000_0000; end else begin case (data) 6'b00_0000: // "0" begin col0 <= 8'b0000_0000; col1 <= 8'b0011_1110; col2 <= 8'b0101_0001; col3 <= 8'b0100_1001; col4 <= 8'b0100_0101; col5 <= 8'b0011_1110; col6 <= 8'b0000_0000; end 6'b00_0001: // "1" begin col0 <= 8'b0000_0000; col1 <= 8'b0000_0000;; col2 <= 8'b0100_0010; col3 <= 8'b0111_1111; col4 <= 8'b0100_0000; col5 <= 8'b0000_0000;; col6 <= 8'b0000_0000; end 6'b00_0010: // "2" begin col0 <= 8'b0000_0000; col1 <= 8'b0100_0010; col2 <= 8'b0110_0001; col3 <= 8'b0101_0001; col4 <= 8'b0100_1001; col5 <= 8'b0100_0110; col6 <= 8'b0000_0000; end 6'b00_0011: // "3" begin col0 <= 8'b0000_0000; col1 <= 8'b0010_0010; col2 <= 8'b0100_0001; col3 <= 8'b0100_1001; col4 <= 8'b0100_1001; col5 <= 8'b0011_0110; col6 <= 8'b0000_0000; end 6'b00_0100: // "4" begin col0 <= 8'b0000_0000; col1 <= 8'b0001_1000; col2 <= 8'b0001_0100; col3 <= 8'b0001_0010; col4 <= 8'b0111_1111; col5 <= 8'b0001_0000; col6 <= 8'b0000_0000; end 6'b00_0101: // "5" begin col0 <= 8'b0000_0000; col1 <= 8'b0010_0111; col2 <= 8'b0100_0101; col3 <= 8'b0100_0101; col4 <= 8'b0100_0101; col5 <= 8'b0011_1001; col6 <= 8'b0000_0000; end 6'b00_0110: // "6" begin col0 <= 8'b0000_0000; col1 <= 8'b0011_1110; col2 <= 8'b0100_1001; col3 <= 8'b0100_1001; col4 <= 8'b0100_1001; col5 <= 8'b0011_0010; col6 <= 8'b0000_0000; end 6'b00_0111: // "7" begin col0 <= 8'b0000_0000; col1 <= 8'b0110_0001; col2 <= 8'b0001_0001; col3 <= 8'b0000_1001; col4 <= 8'b0000_0101; col5 <= 8'b0000_0011; col6 <= 8'b0000_0000; end 6'b00_1000: // "8" begin col0 <= 8'b0000_0000; col1 <= 8'b0011_0110; col2 <= 8'b0100_1001; col3 <= 8'b0100_1001; col4 <= 8'b0100_1001; col5 <= 8'b0011_0110; col6 <= 8'b0000_0000; end 6'b00_1001: // "9" begin col0 <= 8'b0000_0000; col1 <= 8'b0010_0110; col2 <= 8'b0100_1001; col3 <= 8'b0100_1001; col4 <= 8'b0100_1001; col5 <= 8'b0011_1110; col6 <= 8'b0000_0000; end 6'b00_1010: // "A" begin col0 <= 8'b0000_0000; col1 <= 8'b0111_1100; col2 <= 8'b0001_0010; col3 <= 8'b0001_0001; col4 <= 8'b0001_0010; col5 <= 8'b0111_1100; col6 <= 8'b0000_0000; end 6'b00_1011: // "B" begin col0 <= 8'b0000_0000; col1 <= 8'b0111_1111; col2 <= 8'b0100_1001; col3 <= 8'b0100_1001; col4 <= 8'b0100_1001; col5 <= 8'b0011_0110; col6 <= 8'b0000_0000; end 6'b00_1100: // "C" begin col0 <= 8'b0000_0000; col1 <= 8'b0011_1110; col2 <= 8'b0100_0001; col3 <= 8'b0100_0001; col4 <= 8'b0100_0001; col5 <= 8'b0010_0010; col6 <= 8'b0000_0000; end 6'b00_1101: // "D" begin col0 <= 8'b0000_0000; col1 <= 8'b0111_1111; col2 <= 8'b0100_0001; col3 <= 8'b0100_0001; col4 <= 8'b0100_0001; col5 <= 8'b0011_1110; col6 <= 8'b0000_0000; end 6'b00_1110: // "E" begin col0 <= 8'b0000_0000; col1 <= 8'b0111_1111; col2 <= 8'b0100_1001; col3 <= 8'b0100_1001; col4 <= 8'b0100_1001; col5 <= 8'b0100_0001; col6 <= 8'b0000_0000; end 6'b00_1111: // "F" begin col0 <= 8'b0000_0000; col1 <= 8'b0111_1111; col2 <= 8'b0000_1001; col3 <= 8'b0000_1001; col4 <= 8'b0000_1001; col5 <= 8'b0000_0001; col6 <= 8'b0000_0000; end 6'b01_0000: // "G" begin col0 <= 8'b0000_0000; col1 <= 8'b0011_1110; col2 <= 8'b0100_0001; col3 <= 8'b0100_1001; col4 <= 8'b0100_1001; col5 <= 8'b0011_1010; col6 <= 8'b0000_0000; end 6'b01_0001: // "H" begin col0 <= 8'b0000_0000; col1 <= 8'b0111_1111; col2 <= 8'b0000_1000; col3 <= 8'b0000_1000; col4 <= 8'b0000_1000; col5 <= 8'b0111_1111; col6 <= 8'b0000_0000; end 6'b01_0010: // "I" begin col0 <= 8'b0000_0000; col1 <= 8'b0000_0000; col2 <= 8'b0100_0001; col3 <= 8'b0111_1111; col4 <= 8'b0100_0001; col5 <= 8'b0000_0000; col6 <= 8'b0000_0000; end 6'b01_0011: // "J" begin col0 <= 8'b0000_0000; col1 <= 8'b0010_0000; col2 <= 8'b0100_0001; col3 <= 8'b0100_0001; col4 <= 8'b0011_1111; col5 <= 8'b0000_0001; col6 <= 8'b0000_0000; end 6'b01_0100: // "K" begin col0 <= 8'b0000_0000; col1 <= 8'b0111_1111; col2 <= 8'b0000_1000; col3 <= 8'b0001_0100; col4 <= 8'b0010_0010; col5 <= 8'b0100_0001; col6 <= 8'b0000_0000; end 6'b01_0101: // "L" begin col0 <= 8'b0000_0000; col1 <= 8'b0111_1111; col2 <= 8'b0100_0000; col3 <= 8'b0100_0000; col4 <= 8'b0100_0000; col5 <= 8'b0100_0000; col6 <= 8'b0000_0000; end 6'b01_0110: // "M" begin col0 <= 8'b0000_0000; col1 <= 8'b0111_1111; col2 <= 8'b0000_0010; col3 <= 8'b0000_1100; col4 <= 8'b0000_0010; col5 <= 8'b0111_1111; col6 <= 8'b0000_0000; end 6'b01_0111: // "N" begin col0 <= 8'b0000_0000; col1 <= 8'b0111_1111; col2 <= 8'b0000_0010; col3 <= 8'b0000_0100; col4 <= 8'b0000_1000; col5 <= 8'b0111_1111; col6 <= 8'b0000_0000; end 6'b01_1000: // "O" begin col0 <= 8'b0000_0000; col1 <= 8'b0011_1110; col2 <= 8'b0100_0001; col3 <= 8'b0100_0001; col4 <= 8'b0100_0001; col5 <= 8'b0011_1110; col6 <= 8'b0000_0000; end 6'b01_1001: // "P" begin col0 <= 8'b0000_0000; col1 <= 8'b0111_1111; col2 <= 8'b0000_1001; col3 <= 8'b0000_1001; col4 <= 8'b0000_1001; col5 <= 8'b0000_0110; col6 <= 8'b0000_0000; end 6'b01_1010: // "Q" begin col0 <= 8'b0000_0000; col1 <= 8'b0011_1110; col2 <= 8'b0100_0001; col3 <= 8'b0101_0001; col4 <= 8'b0110_0001; col5 <= 8'b0111_1110; col6 <= 8'b0000_0000; end 6'b01_1011: // "R" begin col0 <= 8'b0000_0000; col1 <= 8'b0111_1111; col2 <= 8'b0000_1001; col3 <= 8'b0001_1001; col4 <= 8'b0010_1001; col5 <= 8'b0100_0110; col6 <= 8'b0000_0000; end 6'b01_1100: // "S" begin col0 <= 8'b0000_0000; col1 <= 8'b0010_0110; col2 <= 8'b0100_1001; col3 <= 8'b0100_1001; col4 <= 8'b0100_1001; col5 <= 8'b0011_0010; col6 <= 8'b0000_0000; end 6'b01_1101: // "T" begin col0 <= 8'b0000_0000; col1 <= 8'b0000_0001; col2 <= 8'b0000_0001; col3 <= 8'b0111_1111; col4 <= 8'b0000_0001; col5 <= 8'b0000_0001; col6 <= 8'b0000_0000; end 6'b01_1110: // "U" begin col0 <= 8'b0000_0000; col1 <= 8'b0011_1111; col2 <= 8'b0100_0000; col3 <= 8'b0100_0000; col4 <= 8'b0100_0000; col5 <= 8'b0011_1111; col6 <= 8'b0000_0000; end 6'b01_1111: // "V" begin col0 <= 8'b0000_0000; col1 <= 8'b0001_1111; col2 <= 8'b0010_0000; col3 <= 8'b0100_0000; col4 <= 8'b0010_0000; col5 <= 8'b0001_1111; col6 <= 8'b0000_0000; end 6'b10_0000: // "W" begin col0 <= 8'b0000_0000; col1 <= 8'b0011_1111; col2 <= 8'b0100_0000; col3 <= 8'b0011_0000; col4 <= 8'b0100_0000; col5 <= 8'b0011_1111; col6 <= 8'b0000_0000; end 6'b10_0001: // "X" begin col0 <= 8'b0000_0000; col1 <= 8'b0110_0011; col2 <= 8'b0001_0100; col3 <= 8'b0000_1000; col4 <= 8'b0001_0100; col5 <= 8'b0110_0011; col6 <= 8'b0000_0000; end 6'b10_0010: // "Y" begin col0 <= 8'b0000_0000; col1 <= 8'b0000_0011; col2 <= 8'b0000_0100; col3 <= 8'b0111_1000; col4 <= 8'b0000_0100; col5 <= 8'b0000_0011; col6 <= 8'b0000_0000; end 6'b10_0011: // "Z" begin col0 <= 8'b0000_0000; col1 <= 8'b0110_0001; col2 <= 8'b0101_0001; col3 <= 8'b0100_1001; col4 <= 8'b0100_0101; col5 <= 8'b0100_0011; col6 <= 8'b0000_0000; end 6'b11_1110: // " " begin col0 <= 8'b0000_0000; col1 <= 8'b0000_0000; col2 <= 8'b0000_0000; col3 <= 8'b0000_0000; col4 <= 8'b0000_0000; col5 <= 8'b0000_0000; col6 <= 8'b0000_0000; end 6'b11_1111: // ":" begin col0 <= 8'b0000_0000; col1 <= 8'b0000_0000; col2 <= 8'b0011_0110; col3 <= 8'b0011_0110; col4 <= 8'b0000_0000; col5 <= 8'b0000_0000; col6 <= 8'b0000_0000; end default: // "*" begin col0 <= 8'b0000_0000; col1 <= 8'b0010_0010; col2 <= 8'b0001_0100; col3 <= 8'b0000_1000; col4 <= 8'b0001_0100; col5 <= 8'b0010_0010; col6 <= 8'b0000_0000; end endcase end end endmodule
2837

被折叠的 条评论
为什么被折叠?



