FPGA学习系列:27. VGA驱动设计

描述

设计背景: 

    VGA (Video Graphics Array) 即视频图形阵列,IBM1987年随PS/2(PersonalSystem 2)一起推出的使用模拟信号的一种视频传输标准。这个标准对于现今的个人电脑市场已经十分过时。但在当时具有分辨率高显示速率快颜色丰富等优点在彩色显示器领域取得了广泛的应用,是众多制造商所共同支持的一个低标准。

 

设计原理: 

    VGA的实体图与接口示意图,如下图所示,它有15个针孔:


    在开发板(ZX_1)中,VGA的电路原理图如下图所示:

FPGA


    通过原理图,我们不难发现,VGA需要我们控制的接口只有5个:

FPGA


    显示器的扫描规律是什么?本设计采用逐行扫描,逐行扫描是扫描从屏幕左上角一点开始,从左向右逐点扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置,在这期间,CRT对电子束进行消隐,每行结束时,用行同步信号进行行同步;当扫描完所有的行,形成一帧,用场同步信号进行场同步,并使扫描回到屏幕左上方,同时进行场消隐,开始下一帧。通过这种扫描规律,很容易看出,在设计两个有效范围计数器时,场同步信号计数器是以行同步信号计数器为周期的。

    VGA的显示标准如下表所示:

FPGA


    对于普通的VGA显示器都要严格遵循“VGA工业标准”,否则可能会损害VGA显示器,因此我们在设计时VGA控制器时,都需要参考显示器的显示标准,下面是VGA的行扫描时序与场扫描时序:

    行扫描时序:

FPGA


    场扫描时序:

FPGA


    根据上述显示器的扫描参数以及扫描时序,例如800*600@60的显示模式,60指得是显示器图像的刷新频率,时钟40MHz指得是一个像素输出的频率。800*600VGA的分辨率,指有效显示区域为时序中的c段只有800*600,也就是行计数在[216,1016],列计数在[27,627],在这个范围内,给RGB色值才会有效。

    在VGA 工业标准显示模式要求:行同步、场同步都为负极性,即同步脉冲要求是负脉冲。行同步信号上电拉高,在行同步计数为0时拉低a个时钟周期,即128,之后拉高,在行同步计数到1055时,行同步计数器清零,场同步计数器加1。在行扫描时序中,扫描计数时,周期就是一个像素点的时间。

    场同步信号上电拉高,在场同步计数为0时拉低场同步a个时钟周期,即4之后拉高,在场同步计数到627场同步计数器清零。

    在VGA控制器中,还需要控制三个接口,即三种基色(RGB),它们共专用8位,分别是Red3位,Green3位,Blue3位,所以可以显示256种颜色,RGB数据的格式如下表所示:

FPGA


设计架构图: 

    本设计选择的VGA显示标准为800*600@60,实现点亮整个屏幕,并显示为全红。通过分析设计的功能,可以得到如下的顶层架构: 

FPGA


    顶层模块端口列表如下:

FPGA


    vga_pll模块是为了满足分辨率800*600@60的时钟为40MHz,而ZX_1开发板的系统时钟为50MHz,通过锁相环,将50MHz转化为40MHzvga_control模块是为了设定行场同步信号,并标定出有效显示区域,并输出控制颜色的po_rgb信号。为了便于移植,根据800*600@60分辨率下的参数,对其进行参数化定义。

 

设计代码: 

    VGA控制器代码:

0   module vga_control (pi_clk, pi_rst_n, po_hs, po_vs, po_rgb);

1       

2       input pi_clk, pi_rst_n; //系统时钟复位

3       output reg po_vs;  //VGA场同步信号

4       output reg po_hs;   //VGA行同步信号

5       output [7:0] po_rgb;    //VGA场红绿蓝三基色

6       

7       //----------------VGA时序-----------------------------------

8       //      显示模式        时钟     

9       //      800*600@60  40MHz   

10      ///场   同步(a)   消隐后沿(b) 有效显示(c) 消隐前沿(d) 扫描时间(e)

11      //hs        128     88              800         40              1056

12      //vs        4           23              600         1               628

13      

14      //  (Horizontal)扫描   Parameter (像素)

15      parameter   H_A =   128;

16      parameter   H_B =   80;

17      parameter   H_C =   800;

18      parameter   H_D =   40;

19      parameter   H_E   = 1056;

20      

21      

22      //  (Vertical)扫描 Parameter (行数)

23      parameter   V_A =   4;

24      parameter   V_B =   23;

25      parameter   V_C =   600;

26      parameter   V_D =   1;

27      parameter   V_E =   628;

28      

29      //行扫描计数器,

30      reg [10:0] hcnt;

31      

32      always @ (posedge pi_clk or negedge pi_rst_n)

33      begin

34          if (!pi_rst_n)

35              hcnt <= 11'd0;

36          else

37              begin

38                  if (hcnt == (H_E - 1'b1)) //扫描完一行像素

39                      hcnt <= 11'd0;

40                  else

41                      hcnt <= hcnt + 1'b1;

42              end 

43      end 

44      

45      //场扫描计数器

46      reg [10:0] vcnt;  

47      

48      always @ (posedge pi_clk or negedge pi_rst_n)

49      begin

50          if (!pi_rst_n)

51              vcnt <= 11'd0;

52          else if (vcnt == (V_E - 1'b1)) 

53              vcnt <= 11'd0;

54          else if (hcnt == (H_E - 1'b1))

55              vcnt <= vcnt + 1;

56      end     

57

58      //行同步输出

59      always @ (posedge pi_clk or negedge pi_rst_n)

60      begin

61          if (!pi_rst_n)

62              po_hs <= 1'b1;

63          else if (hcnt < H_A)

64              po_hs <= 1'b0;

65          else

66              po_hs <= 1'b1;

67      end 

68      

69      //assign po_hs = (hcnt <= H_A - 1'b1) ? 1'b0 : 1'b1;

70      

71      //场同步输出

72      always @ (posedge pi_clk or negedge pi_rst_n)

73      begin

74          if (!pi_rst_n)

75              po_vs <= 1'b1;

76          else if (vcnt < V_A)

77              po_vs <= 1'b0;

78          else

79              po_vs <= 1'b1;

80      end 

81      

82      //assign po_vs = (vcnt <= V_A - 1'b1) ? 1'b0 : 1'b1;

83      

84      wire rgb_en;

85      

86      assign rgb_en = (hcnt >= H_A + H_B  && hcnt < H_A + H_B + H_C) && 

87                          (vcnt >= V_A + V_B  && vcnt < V_A + V_B + V_C) ? 1'b1 : 1'b0;

88      

89      assign po_rgb = rgb_en ? 8'b111_000_00 : 8'b0000_0000;

90      

91  endmodule 


    顶层文件如下所示:

0   module vga_display_pure (pi_clk, pi_rst_n, po_hs, po_vs, po_rgb);

1       

2       input pi_clk, pi_rst_n; //系统时钟复位

3       output po_vs;  //VGA场同步信号

4       output po_hs;   //VGA行同步信号

5       output [7:0] po_rgb;    //VGA场红绿蓝三基色

6       

7       //----------------VGA时序-----------------------------------

8       //      显示模式        时钟     

9       //      800*600@60  40MHz   

10      ///场   同步(a)   消隐后沿(b) 有效显示(c) 消隐前沿(d) 扫描时间(e)

11      //hs        128     88              800         40              1056

12      //vs        4           23              600         1               628

13      

14      wire vga_clk;

15      

16      vga_pll vga_pll_dut(

17          .areset(~pi_rst_n),

18          .inclk0(pi_clk),

19          .c0(vga_clk)

20      );

21      

22      vga_control vga_control_dut(

23          .pi_clk(vga_clk), 

24          .pi_rst_n(pi_rst_n), 

25          .po_hs(po_hs),

26          .po_vs(po_vs),

27          .po_rgb(po_rgb)

28      );

29      

30  endmodule 

    

    通过编译后生成的RTL视图如下:

FPGA 

    

    为了验证本设计的逻辑正确性,我们先对其进行了仿真,在仿真时,为了减少仿真的时间,先将行、场扫描的对应参数,进行了缩放,这样不仅节约了仿真时间,同时由于扫描数据量变少,更加便于分析观察。其仿真代码所示;

0   `timescale 1ns/1ps  //仿真时间精度时间单位

1 

2   module vga_display_pure_tb;

3       

4       reg pi_clk, pi_rst_n; //系统时钟复位

5       wire  po_vs;  //VGA场同步信号

6       wire  po_hs;    //VGA行同步信号

7       wire  [7:0] po_rgb; //VGA场红绿蓝三基色

8       

9       //初始化数据,并附相应初值

10      initial begin

11          pi_clk = 0;

12          pi_rst_n = 0;  

13          #200.1 pi_rst_n = 1;  

14          

15      end 

16

17      vga_display_pure vga_display_pure_inst (

18          .pi_clk(pi_clk), 

19          .pi_rst_n(pi_rst_n), 

20          .po_hs(po_hs), 

21          .po_vs(po_vs),

22          .po_rgb(po_rgb)

23      );

24      

25      always #10 pi_clk = ~pi_clk;  //50MHz时钟描述

26

27  endmodule


仿真图: 

FPGA


    rgb_en信号,只有当po_vspo_hs同时为高电平时,才有效,并且有po_rgb Red基色信号输出,时序仿真细节图如下所示:

FPGA


    通过观察和分析时序图,可以发现与设计吻合,接下来则可进行管脚分配,并下板验证,验证结果如下:

   


打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分