【FPGA】十、VGA字符显示

文章目录

前言

一、实验任务

二、字符取模

三、程序设计

总结


前言

        我们在上一篇文章中已经对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

        由于我这里设备问题,这次就没有进行上板验证了,接下来也就是绑定引脚进行上板验证就可以了,感兴趣的小伙伴可自行上板,看看效果如何,哈哈!


总结

        本篇文章是在上一篇的基础上进行改进的,有许多原理性的内容可以参考上一篇文章,这里只是完成了上次遗留下来的问题罢了。字符显示的原理也是很清晰的,最重要的就是要抓住字符显示的范围,以及对字符显示进行赋值操作,和彩条显示原理差不多。

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值