上一篇我们介绍了使用fpga驱动vga显示一个简单的彩色条纹。这一篇我们看看文字如何显示输出。
文字显示原理。文字的显示,其实也是把文字当做图片,一个像素点一个像素点的来输出。如下图:
如果对正方形内字符的每一个像素点进行标记,哪些点应该显示白色,哪些点应该显示黑色,那么就可以在vga输出的时候,输出对应像素点的颜色值,也就能输出这个文字。
如何得到这个字的像素点颜色值呢?这儿我们可以使用一些工具,比如“PCtoLCD2002”这个软件。比如上面这个“字”,软件的字模格式设置如下:
可以得到以下字模:
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 |
效果如下:
老师好,这个initial 关键字不是只在仿真文件上用的吗,在这里看到在设计文件上也可以,且不可综合吗?
initial既可以用于仿真文件,也可以用于实体文件。文章()中有说明:“在实体程序中使用的任何语法结构testbench都可以使用,如initial ,always,assign,模块例化等” ,也就是说,“initial ,always,assign,模块例化”在实体和仿真文件中都可以使用。
字模的理解:每个暗点为0,每个亮点为1,逐个点描述出来。例如,文中的字模
0x00,0x00,0x00,0x00 //第一行全是暗点,即都是0,VGA输出的时候应该输出背景色
0x00,0x00,0x00,0x00 //同上
0x00,0x02,0x00,0x00 //第三行,前8个点是0x00也就是0000_0000,后续8个点是0x02也就是0000_0010,也就是有一个亮点(最上面那个),其后点也都是暗点
这样把所有点都记录下来就是所谓的“字模”