VGA实验:显示字符

这个实验是在看了特权写的《深入浅出玩转FPGA》之后自己写的也算自己做一个小小的练习吧,目的是在屏幕上显示一行字:DIY 逻辑分析仪。

由于试验中用到取字模的软件PCtoLCD2002,所以首先介绍下这个软件。

 

 

 

软件的设置如上图。

采用阴码格式,逐列扫面,高位在前的方式,所谓“高位在前”就是扫描图像的最先扫到的点放在前面,例如:**...... 八个像素点,*代表亮,.代表不亮,此时扫到的代码为:11000000,存到存储器中时候为:0xc0,所以当你要去从存储器中取数的时候应该先去取最高位。在程序中:rom_data[31-x].因为你去取第0个点的值的时候,它是存在存储器的最高位。

我是把取出来的自摸一个一个敲到mif文件中的,但是看到网友Crazy Bingo 是用自己写的软件C2Mif v1.0 转换软件生成的,有时间学学。因为我的方法太笨了,当代码很多时,敲的你很郁闷,而且也很容易出错,但不好找出错误。

 

下面就是模块了

首先是同步模块

 1 module sync_module (clk_25m,rst_n,valid,x,y,hsync,vsync);
2 input clk_25m;
3 input rst_n;
4 output valid;
5 output hsync,vsync;
6 output [9:0]x;
7 output [9:0]y;
8
9 /************************************/
10
11
12 /************************************/
13
14 reg [9:0]count_h;
15
16 always @ (posedge clk_25m or negedge rst_n)
17 if (!rst_n)
18 count_h<=10'd0;
19 else if (count_h==10'd799)
20 count_h<=10'd0;
21 else
22 count_h<=count_h+1'b1;
23
24 /*************************************/
25
26
27 /*************************************/
28
29 reg [9:0]count_v;
30
31 always @ (posedge clk_25m or negedge rst_n)
32 if(!rst_n)
33 count_v<=10'd0;
34 else if(count_v==10'd524)
35 count_v<=10'd0;
36 else if (count_h==10'd799)
37 count_v<=count_v+1'b1;
38
39 /**************************************/
40
41
42 /**************************************/
43
44 reg isready;
45
46 always @ (posedge clk_25m or negedge rst_n)
47 if (!rst_n)
48 isready<=1'b0;
49 else if ((count_h >=10'd144 && count_h <=10'd784) &&
50 (count_v >=10'd35 && count_v <=10'd515))
51 isready <=1'b1;
52 else
53 isready <=1'b0;
54
55 /****************************************/
56
57
58 /****************************************/
59 assign valid=isready;
60 assign hsync= (count_h<10'd96)?1'b0:1'b1;
61 assign vsync= (count_v<10'd2)?1'b0:1'b1;
62
63
64
65 assign x=count_h;
66 assign y=count_v;
67
68 /***************************************/
69
70 endmodule

同步模块和上次没有什么改变,就不做解释了。

 

下面重要的是VGA控制模块

 1 module vga_control (clk_25m,rst_n,vga_r,vga_g,vga_b,x,y,valid,rom_addr,rom_data);
2 input clk_25m;
3 input rst_n;
4 input [9:0]x;
5 input[9:0]y;
6 input valid;
7 input [31:0]rom_data;
8 output [7:0]rom_addr;
9 output [9:0]vga_r;
10 output [9:0]vga_g;
11 output [9:0]vga_b;
12
13 /*****************************/
14
15
16 /*****************************/
17
18 reg [7:0]addr;
19 always @(posedge clk_25m or negedge rst_n)
20 if(!rst_n) addr <= 8'd0;
21 else if(x == 10'd242) addr <= 8'd0;
22 else addr <= addr+1'b1;
23
24
25 wire dis_topic = (y> 10'd39) & (y< 10'd72)
26 & (x> 10'd245) & (x < 10'd470);
27
28
29 /********************************/
30
31
32 /********************************/
33
34 reg [9:0]r;
35 reg [9:0]g;
36 reg [9:0]b;
37
38 always @ (posedge clk_25m)
39 if(!valid) begin
40 r<=10'd0;
41 g<=10'd0;
42 b<=10'd0;
43 end
44 else if(dis_topic) begin
45 if(rom_data[10'd71-y])
46 begin
47 r<=10'h000;
48 g<=10'h3ff;
49 b<=10'h000;
50 end
51 else begin
52 r<=10'h3ff;
53 g<=10'h3ff;
54 b<=10'h3ff;
55 end
56 end
57 else begin
58 r<=10'h3ff;
59 g<=10'h3ff;
60 b<=10'h3ff;
61 end
62
63 /********************************/
64
65
66 /********************************/
67 assign rom_addr=addr;
68 assign vga_r=r;
69 assign vga_b=b;
70 assign vga_g=g;
71
72 endmodule
73
74
75
76
77
78
79
80


第7和8行定义了一个rom的端口,这个rom中存的是“DIY 逻辑分析仪”这几个字的代码,采用32*32的大小,就是一个汉字占用的点数是高32位,宽32位,一个英文字符是高32位宽16位,所以总共占用的空间是高32位,宽224位。这里存储是采用32位宽和224深度的存储器。




     如上图所示,字符的第一列共32位,存在存储器的地址位addr0的地方,第二列存在addr1的地方。所以深度为224.当从存储器中取数的时候,扫描屏幕的一行,对应存储器中一列,如上图中剪头所示,例如:显示字符的第一行,访问的存储器依次为:addr0,rom_data[31];addr1,rom_data[31];addr2,rom_data[31];....;addr223,rom_data[31];

      第18行是定义了一个地址寄存器addr,当检测到x=242时候,地址为0,以后每来一个clk,地址递增。

      第25行定义显示区域是x从246到479,y从40到71;但第18行当x=242时候就地址就开始有效,为什么后面是从246开始显示?呵呵,这地方是个细节,因为每个模块之间传输数据都要延迟一个clk,同步模块计算出x,y坐标,传到VGA控制模块,VGA控制模块检测到正确的x,y坐标时给rom模块地址,然后从rom模块去数据要耗掉3个时钟周期,但是在例化rom模块时候对输出数据又寄存了一下,所以又耗掉一个周期,所以总共耗掉4个周期,也就是说,如果想在x=246那个点显示就需要提前4个周期送数据,所以要从x=242的时候就开始送数据。

     第44-50行使显示“DIY 逻辑分析仪”这几个字,45行的rom_data[71-y],是因为取模时候是高位在前,y坐标对应显示的开始点位y=40,也就是说y=40 的点对应显示的零点,也即:y-40。又存储器是32位:0-31,所以从存储器中取数与y的关系为:31-(y-40)=71-y。48行定义显示颜色为绿色 ,颜色可以根据自己喜欢的自己调节。

 1 module vga_2 (vga_clk,clk,rst_n,hsync,vsync,sync_n,blank_n,vga_r,vga_b,vga_g);
2 input clk;
3 input rst_n;
4 output hsync;
5 output vsync;
6 output sync_n;
7 output blank_n;
8 output [9:0]vga_r;
9 output [9:0]vga_g;
10 output [9:0]vga_b;
11 output vga_clk;
12
13
14 /****************************/
15
16
17 assign sync_n=1'b0;
18 assign blank_n=hsync && vsync;
19
20 /****************************/
21
22 wire vga_clk;
23
24 vga_pll U1(
25 .inclk0(clk),
26 .c0(vga_clk));
27
28 /***************************/
29
30
31 /***************************/
32 wire [7:0] rom_addr;
33 wire [31:0]rom_data;
34
35 vga_rom_topic vga_rom_topic_inst (
36 .address ( rom_addr ),
37 .clock ( vga_clk ),
38 .q ( rom_data )
39 );
40
41
42 /**************************/
43
44
45 /**************************/
46 wire [9:0]x;
47 wire [9:0]y;
48
49 sync_module U2(
50 .clk_25m(vga_clk),
51 .rst_n(rst_n),
52 .valid(valid),
53 .x(x),
54 .y(y),
55 .hsync(hsync),
56 .vsync(vsync));
57
58 /**************************/
59
60
61 /**************************/
62
63 vga_control U3(
64 .clk_25m(vga_clk),
65 .rst_n(rst_n),
66 .vga_r(vga_r),
67 .vga_g(vga_g),
68 .vga_b(vga_b),
69 .x(x),
70 .y(y),
71 .valid(valid),
72 .rom_addr(rom_addr),
73 .rom_data(rom_data));
74
75
76
77
78 /*****************************/
79
80
81 endmodule

 

以上代码是顶层模块的代码。

显示结果如下:

 

 

posted on 2011-11-09 20:50  @火枪手@  阅读(6238)  评论(5编辑  收藏  举报

导航