0%

基于FPGA的VGA控制器

目标

​ 设计一个基于FPGA的VGA控制器,可以显示640*480的图片,为下一步学习摄像头做准备。

硬件平台:Basys 3 开发板

原理

​ 标准15针VGA接口共有五个信号接口,如下表所示,其中hsync、vsync为行同步和场同步信号,RGB为模拟信号。

信号名称 作用
hsync 行同步信号
vsync 场同步信号
R 红色信号
G 绿色信号
B 蓝色信号
行扫描

行扫描时序

场扫描

场扫描时序

VGA时序图

VGA时序图

设计

整个电路分为这几个部分:

  • VGA控制器
  • 图像控制器
  • ROM存储
  1. 首先是VGA控制器部分,这部分产生行同步信号和场同步信号;

    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
    module vga_sync(
    trueinput wire clk,reset,
    trueoutput wire hsync,vsync,video_on,p_tick,
    trueoutput wire [9:0] pixel_x,pixel_y,
    trueoutput wire [18:0] addr
    );

    true//定义常数
    true//VGA 640 * 480 同步参数
    truelocalparam HD = 640; //水平显示区域
    truelocalparam HF = 48; //水平扫描左边界
    truelocalparam HB = 16; //水平扫描右边界
    truelocalparam HR = 96; //水平折回区
    truelocalparam VD = 480; //垂直显示区域
    truelocalparam VF = 10; //垂直扫描顶部边界
    truelocalparam VB = 33; //垂直扫描底部边界
    truelocalparam VR = 2; //垂直折回区

    true//模4计数器
    truereg [1:0] mod4_reg;
    truereg [1:0] mod4_next;
    true//同步计数器
    truereg [9:0] h_count_reg,h_count_next;
    truereg [9:0] v_count_reg,v_count_next;
    true//输出缓冲器
    truereg v_sync_reg,h_sync_reg;
    truewire v_sync_next,h_sync_next;
    true//状态信号
    truewire h_end,v_end,pixel_tick,addr_end;
    true//地址输出缓冲器
    truereg [18:0] addr_reg;
    truereg [18:0] addr_next;

    truealways @(posedge clk or posedge reset) begin
    truetrueif (reset) begin
    truetruetrue// reset
    truetruetruemod4_reg <= 1'b0;
    truetruetruev_count_reg <= 0;
    truetruetrueh_count_reg <= 0;
    truetruetruev_sync_reg <= 1'b0;
    truetruetrueh_sync_reg <= 1'b0;
    truetruetrueaddr_reg <= 0;
    truetrueend
    truetrueelse begin
    truetruetruemod4_reg <= mod4_next;
    truetruetruev_count_reg <= v_count_next;
    truetruetrueh_count_reg <= h_count_next;
    truetruetruev_sync_reg <= v_sync_next;
    truetruetrueh_sync_reg <= h_sync_next;
    truetruetrueaddr_reg <= addr_next;
    truetrueend
    trueend

    true//模4计数器产生25MHz时钟使能信号
    truealways @(*) begin
    truetrueif (reset) begin
    truetruetrue// reset
    truetruetruemod4_next <= 2'b0;
    truetrueend
    truetrueelse begin
    truetruetruemod4_next <= mod4_reg + 1;
    truetrueend
    trueend
    true// assign mod4_next = mod4_reg + 1;
    trueassign pixel_tick = (mod4_reg == 2'b11)&&(!clk);

    true//状态信号
    true//水平扫描结束信号(799)
    trueassign h_end = (h_count_reg==(HD+HF+HB+HR-1));
    true//垂直扫描计数器结束信号
    trueassign v_end = (v_count_reg==(VD+VF+VB+VR-1));
    true//地址结束信号
    trueassign addr_end = (addr_reg==(HD*VD-1));

    true//水平同步扫描模800计数器下一状态
    truealways @(*) begin
    truetrueif (pixel_tick) begin
    truetruetrueif (h_end) begin
    truetruetruetrueh_count_next = 0;
    truetruetrueend else begin
    truetruetruetrueh_count_next = h_count_reg + 1;
    truetruetrueend
    truetrueend else begin
    truetruetrueh_count_next = h_count_reg;
    truetrueend
    trueend

    true//垂直同步扫描模525计数器新下一状态
    truealways @(*) begin
    truetrueif (pixel_tick && h_end) begin
    truetruetrueif (v_end) begin
    truetruetruetruev_count_next = 0;
    truetruetrueend else begin
    truetruetruetruev_count_next = v_count_reg + 1;
    truetruetrueend
    truetrueend else begin
    truetruetruev_count_next = v_count_reg;
    truetrueend
    trueend

    true//地址下一个状态
    truealways @(*) begin
    truetrueif (pixel_tick) begin
    truetruetrueif (addr_end) begin
    truetruetruetrueaddr_next = 0;
    truetruetrueend else if(video_on) begin
    truetruetruetrueaddr_next = addr_reg + 1;
    truetruetrueend else begin
    truetruetruetrueaddr_next = addr_reg;
    truetruetrueend
    truetrueend else begin
    truetruetrueaddr_next = addr_reg;
    truetrueend
    trueend

    true//同步缓冲器
    true//h_sync_next 信号在计数器数值为656和751时赋值
    trueassign h_sync_next = (h_count_reg >= (HD+HB)&&h_count_reg <= (HD+HB+HR-1));
    true//v_sync_next 信号在计数器数值为490和491时赋值
    trueassign v_sync_next = (v_count_reg >= (VD+VB)&&v_count_reg <= (VD+VB+VR-1));

    true//产生video_on 信号
    trueassign video_on = (h_count_reg < HD) && (v_count_reg < VD);

    true//输出
    trueassign hsync = h_sync_reg;
    trueassign vsync = v_sync_reg;
    trueassign pixel_x = h_count_reg;
    trueassign pixel_y = v_count_reg;
    trueassign p_tick = pixel_tick;
    trueassign addr = addr_reg;
    endmodule

  2. 然后是图像控制部分;

    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
    module vga_display
    true(
    truetrueinput clk,reset,
    input p_tick,
    truetrueinput [9:0] pixel_x,pixel_y,
    input video_on,
    truetrueinput [11:0] rgb_in,
    truetrueoutput [14:0] addr_out,
    truetrueoutput [11:0] rgb_out
    );

    trueparameter DH = 150, DV = 150;
    truereg [14:0] addr_reg,addr_next;
    truereg [11:0] rgb_reg,rgb_next;

    trueassign pic_en = ((pixel_x >= 0 && pixel_x < DH) && (pixel_y >= 0 && pixel_y < DV));

    always @(posedge clk or posedge reset) begin
    if (reset) begin
    // reset
    addr_reg <= 15'b0;
    rgb_reg <= 12'b0;
    end else begin
    addr_reg <= addr_next;
    rgb_reg <= rgb_next;
    end
    end

    always @(*) begin
    if (p_tick) begin
    if (addr_reg == DH * DV - 1) begin
    addr_next = 0;
    end else if (pic_en) begin
    addr_next = addr_reg + 1;
    end else begin
    addr_next = addr_reg;
    end
    end else begin
    addr_next = addr_reg;
    end
    end

    always @(*) begin
    if (pic_en) begin
    rgb_next = rgb_in;
    end else begin
    rgb_next = 12'b0000_1111_1111;
    end
    end

    assign addr_out = addr_reg;
    assign rgb_out = (video_on)?rgb_reg:12'b0;

    endmodule

  3. 最后ROM储存。

    这部分调用了Xilinx官方的IP,在IP Catalog 中选择Block Memory Generator,

    1. 首先在Basis里选择Memory Type为Single Port ROM;

      ROM1
    2. 然后在Port A Options 里设置数据宽度和深度,因为Basys3上的VGA是RGB444格式的,所以数据宽度设为12位,深度可以理解为像素数,本来是想显示640*480的图片的,但Basys3上的RAM容量不够,所以就显示的150*150的图片,则深度为22500(150*150),不需要使能,所以Enable Port Type设为Always Enable;

      ROM2
    3. Other Options里,装载 .coe文件。

      ROM3
      ROM4
    4. 顶层文件

      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
      module VGA_display_pictures_top
      true(
      truetrueinput clk,reset,
      truetrueoutput [11:0] rgb_data,
      truetrueoutput hsync,vsync
      );

      true//信号声明
      truewire [14:0] addr;
      truewire [11:0] rom_data; //rom输出rgb信号
      truewire [9:0] pixel_x,pixel_y;
      truewire video_on;

      vga_sync U_vga_sync
      (
      .clk(clk),
      .reset(reset),
      .hsync(hsync),
      .vsync(vsync),
      .pixel_x(pixel_x),
      .pixel_y(pixel_y),
      .video_on(video_on),
      .p_tick(p_tick)
      true);

      vga_display U_vga_display
      (
      .clk(clk),
      .reset(reset),
      .pixel_x(pixel_x),
      .pixel_y(pixel_y),
      .rgb_in(rom_data),
      .rgb_out(rgb_data),
      .addr_out(addr),
      .video_on(video_on),
      .p_tick(p_tick)
      true);

      truevga_picture vga_picture
      true(
      truetrue.clka(clk),
      truetrue.addra(addr),
      truetrue.douta(rom_data)
      true);

      endmodule
  4. 整体连线图

    整体连线图

    总结

    • 为了防止自己忘记,所以写下来;
    • 暂时先写这么多,以后再补充。
-------The end of this article  Thank you for your reading-------