Menu Close

FPGA图像处理之三:VGA文字显示Verilog实现

上一篇我们介绍了使用fpga驱动vga显示一个简单的彩色条纹。这一篇我们看看文字如何显示输出。

文字显示原理。文字的显示,其实也是把文字当做图片,一个像素点一个像素点的来输出。如下图:

%title插图%num

如果对正方形内字符的每一个像素点进行标记,哪些点应该显示白色,哪些点应该显示黑色,那么就可以在vga输出的时候,输出对应像素点的颜色值,也就能输出这个文字。

如何得到这个字的像素点颜色值呢?这儿我们可以使用一些工具,比如“PCtoLCD2002”这个软件。比如上面这个“字”,软件的字模格式设置如下:

%title插图%num

可以得到以下字模:

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x07,0x00,0x00
0x00,0x03,0x80,0x00,0x00,0x01,0xC0,0x00,0x1F,0xFF,0xFF,0xF8,0x1F,0xFF,0xFF,0xF8
0x1C,0x00,0x00,0x18,0x1C,0x00,0x00,0x18,0x1C,0x00,0x00,0x18,0x01,0xFF,0xFF,0x80
0x01,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0x80,0x00,0x00,0x1E,0x00,0x00,0x00,0x3C,0x00
0x00,0x00,0xF0,0x00,0x00,0x01,0xE0,0x00,0x00,0x01,0xC0,0x00,0x3F,0xFF,0xFF,0xFC
0x3F,0xFF,0xFF,0xFC,0x3F,0xFF,0xFF,0xFC,0x00,0x01,0xC0,0x00,0x00,0x01,0xC0,0x00
0x00,0x01,0xC0,0x00,0x00,0x01,0xC0,0x00,0x00,0x01,0xC0,0x00,0x00,0x01,0xC0,0x00
0x00,0x1B,0xC0,0x00,0x00,0x1F,0x80,0x00,0x00,0x0F,0x80,0x00,0x00,0x00,0x00,0x00/*”字”,0*/

这是32X32的大小,点阵显得有点多。只要把这些值存到rom之中,当vga扫描到需要输出的位置时,从rom中取出对应的值给RGB输出即可。(如果要输出的字不多且固定,也可以把字模直接写在代码里)

参考代码入下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
module VGA_character(
input       wire                sysclk,         //系统时钟,也就是板子的时钟
input       wire                rst,

output      reg     [3:0]       R,              //输出的颜色值
output      reg     [3:0]       G,              //输出的颜色值
output      reg     [3:0]       B,              //输出的颜色值
output      wire                HS,             //水平同步信号
output      wire                VS              //垂直同步信号

);

//显示分辨率 640*480@60Hz
//时钟25.175M
localparam      HPOLA           =       0;  //水平同步信号极性
localparam      VPOLA           =       0;  //垂直同步信号极性

localparam      HTOTAL          =       800;//水平总像素数
localparam      HSYNCP          =       96; //水平同步信号像素数
localparam      HBACKP          =       40; //扫描后沿像素数
localparam      LBorder         =       8;  //左边框
localparam      HDISP           =       640;//显示区域
localparam      RBorder         =       8;  //右边框
localparam      HFRONTP         =       8;  //扫描前沿像素数


localparam      VTOTAL          =       525;//垂直总像素数
localparam      VSYNCP          =       2;  //垂直同步信号
localparam      VBACKP          =       25; //扫描后沿像素数
localparam      TBorder         =       8;  //顶边框
localparam      VDISP           =       480;//显示区域
localparam      BBorder         =       8;  //底边框
localparam      VFRONTP         =       2;  //扫描前沿像素数


wire                vgaclk;
wire                locked;
wire                vgarst;

assign  vgarst = ~locked;                   //locked为高表示时钟正常,此时不复位

//使用锁相环来输出指定时钟
pll pll_inst (
    .areset ( rst ),
    .inclk0 ( sysclk ),
    .c0 ( vgaclk ),
    .locked ( locked )
    );

//*****************************************************
//“字”字模
reg         [31:0]  character[0:31];        //存储字模
reg         [9:0]   location_x = 432;       //显示位置
reg         [9:0]   location_y = 243;       //显示位置

initial begin
character[00] = 32'h00000000;
character[01] = 32'h00000000;
character[02] = 32'h00020000;
character[03] = 32'h00070000;
character[04] = 32'h00038000;
character[05] = 32'h0001C000;
character[06] = 32'h1FFFFFF8;
character[07] = 32'h1FFFFFF8;
character[08] = 32'h1C000018;
character[09] = 32'h1C000018;
character[10] = 32'h1C000018;
character[11] = 32'h01FFFF80;
character[12] = 32'h01FFFF80;
character[13] = 32'h01FFFF80;
character[14] = 32'h00001E00;
character[15] = 32'h00003C00;
character[16] = 32'h0000F000;
character[17] = 32'h0001E000;
character[18] = 32'h0001C000;
character[19] = 32'h3FFFFFFC;
character[20] = 32'h3FFFFFFC;
character[21] = 32'h3FFFFFFC;
character[22] = 32'h0001C000;
character[23] = 32'h0001C000;
character[24] = 32'h0001C000;
character[25] = 32'h0001C000;
character[26] = 32'h0001C000;
character[27] = 32'h0001C000;
character[28] = 32'h001BC000;
character[29] = 32'h001F8000;
character[30] = 32'h000F8000;
character[31] = 32'h00000000;
end
//*****************************************************
   
reg         [9:0]   hcnt;                           //行计数
reg         [9:0]   vcnt;                           //场计数
   
   
//行计数
always@(posedge vgaclk or posedge vgarst) begin
if(vgarst)
    hcnt <= 0;
else
    if(hcnt == HTOTAL - 1)
        hcnt <= 0;
    else
        hcnt <= hcnt + 1;
end
   

//场计数
always@(posedge vgaclk or posedge vgarst) begin
if(vgarst)
    vcnt <= 0;
else
    if((hcnt == HTOTAL - 1) && (vcnt == VTOTAL - 1))
        vcnt <= 0;
    else if(hcnt == HTOTAL - 1)
        vcnt <= vcnt + 1;
    else
        vcnt <= vcnt;
end


//行同步信号输出
assign HS = (hcnt <= HSYNCP - 1)  ?  HPOLA : ~HPOLA;

//场同步信号输出
assign VS = (vcnt <= VSYNCP - 1)  ?  VPOLA : ~VPOLA;


//显示区域的判断
wire    h_disp;
assign h_disp = (hcnt > (HSYNCP + HBACKP + LBorder - 1) &&
                hcnt < (HSYNCP + HBACKP + LBorder + HDISP - 1)) ? 1'b1 : 1'b0;
               
wire    v_disp;
assign v_disp = (vcnt > (VSYNCP + VBACKP + TBorder - 1) &&
                vcnt < (VSYNCP + VBACKP + TBorder + VDISP- 1))  ? 1'b1 : 1'b0;


wire disp;
assign disp = h_disp && v_disp;



always@(posedge vgaclk or posedge vgarst) begin
if(vgarst)
    begin
        R <= 4'b0000;
        G <= 4'b0000;
        B <= 4'b0000;
    end
else
    if(disp)
        begin
            if((hcnt>=location_x && hcnt<location_x+32) && (vcnt >= location_y && vcnt<location_y+32))
                if(character[vcnt-location_y][hcnt-location_x])
                    begin
                        R <= 4'b1111;
                        G <= 4'b1111;
                        B <= 4'b1111;
                    end
                else
                    begin
                        R <= 4'b0000;
                        G <= 4'b0000;
                        B <= 4'b0000;
                    end
            else
                begin
                    R <= 4'b0000;
                    G <= 4'b0000;
                    B <= 4'b0000;
                end
        end
    else
        begin
            R <= 4'b0000;
            G <= 4'b0000;
            B <= 4'b0000;
        end
end

endmodule

效果如下:

%title插图%num

附件下载

Posted in FPGA, 教材与教案

3 Comments

    • Yvonne

      initial既可以用于仿真文件,也可以用于实体文件。文章()中有说明:“在实体程序中使用的任何语法结构testbench都可以使用,如initial ,always,assign,模块例化等” ,也就是说,“initial ,always,assign,模块例化”在实体和仿真文件中都可以使用。

  1. Nikola

    字模的理解:每个暗点为0,每个亮点为1,逐个点描述出来。例如,文中的字模
    0x00,0x00,0x00,0x00 //第一行全是暗点,即都是0,VGA输出的时候应该输出背景色
    0x00,0x00,0x00,0x00 //同上
    0x00,0x02,0x00,0x00 //第三行,前8个点是0x00也就是0000_0000,后续8个点是0x02也就是0000_0010,也就是有一个亮点(最上面那个),其后点也都是暗点

    这样把所有点都记录下来就是所谓的“字模”

发表评论

相关链接