Thông báo

Collapse
No announcement yet.

VGA(DVI) Graphic

Collapse
X
 
  • Lọc
  • Giờ
  • Show
Clear All
new posts

  • VGA(DVI) Graphic

    Chúng ta có thể làm một project màu mè một chút với video display, dùng Python để tạo font bitmap, và viết text hoặc load graphic lên display qua cổng RS-232. Xilinx kit ML506 sử dụng DVI, nhưng phần video horizontal / vertical sync chắc cũng không khác so với VGA interface. Có lẽ không cần thiết với VGA, nhưng chúng ta cần phải configure DVI controller qua bus I2C. Project này cần hai modules rất cơ bản, VGA / DVI timing generator, và I2C master.

    Một I2C master vừa đủ xài chỉ cần 10 flip-flops, 9 SRL16 và một 8-to-1 multiplexer.

    VGA / DVI timing generator rất dễ thực hiện, chỉ cần hai counter (vertical và horizontal) và một số phép so sánh để assert/de-assert sync.

    Các thông số cho front-porch, back-porch, sync width / polarity, và pixel clock chúng ta có thể tìm được trên Video Control Panel (nên test xem monitor có support các thông số mà chúng ta muốn dùng hay là không).

    Project này sẽ sử dụng 1024x768 resolution với refresh rate = 60 Hz.



    Code:
      type DVI_CONFIG_TYPE is record
        width   : integer;
        h_fp    : integer;
        h_sync  : integer;
        h_bp    : integer;
        height  : integer;
        v_fp    : integer;
        v_sync  : integer;
        v_bp    : integer;
        h_polar : std_logic;
        v_polar : std_logic;
        pixel_clock     : real;
        clkfx_divide    : integer;
        clkfx_multiply  : integer;
      end record;
    
      constant DVI_1440_900_60 : DVI_CONFIG_TYPE := (1440, 80, 152, 232, 900, 3, 6, 25, '0', '0', 106.5, 32, 17);
    
      constant DVI_1024_768_60 : DVI_CONFIG_TYPE := (1024, 24, 136, 144, 768, 3, 6, 29, '0', '0', 65.0, 6, 2);
      constant DVI_1024_768_70 : DVI_CONFIG_TYPE := (1024, 24, 136, 144, 768, 3, 6, 29, '0', '0', 75.0, 8, 3);
    
      constant DVI_CONFIG : DVI_CONFIG_TYPE := DVI_1024_768_60;
    Các thông số trên được save lại để dễ dàng hoán đổi resolution. Digital Clock Manager được sử dụng để tạo ra clock rate cần thiết (không cần thiết phải chính xác hoàn toàn). Clock đầu vào là 200MHz, cho nên để đạt được pixel clock = ~106.5MHz cho resolution 1440x900, chúng ta có thể lấy 200 / 32 * 17 như configuration bên trên.
    Attached Files
    Last edited by nemesis21; 16-05-2009, 12:27.

  • #2
    Với BRAM, chúng ta không thể chứa một tấm ảnh với 1024x768 pixel, cho nên chúng ta sẽ sử dụng 128x128 tiles (128x128x24-bit).

    Chúng ta sẽ sử dụng 8x16 font, và với resolution 1024x768, chúng ta sẽ có 48 rows và 128 columns cho text.

    Nếu sử dụng black/white font bitmap, để chứa 128 font characters, chúng ta cần 128x8x16x1-bit. Project này dùng 24-bit color bitmap, nên chúng ta cần 128x8x16x24-bit. Nên thiết kế lại cho phù hợp với resource của bạn.

    Để store text characters, chúng ta sẽ cần 48x128x8-bit.

    Dual-port block ram được infer theo template của Xilinx. Chúng ta sẽ viết vào cổng A, và đọc từ cổng B. Data sẽ được viết vào qua cổng RS-232, vì thế clk_uart được sử dụng cho cổng A.

    Code:
      type RAM_TYPE_1 is array (natural range <>) of std_logic_vector (23 downto 0);
      type RAM_TYPE_2 is array (natural range <>) of std_logic_vector (7 downto 0);
    
      shared variable video_ram : RAM_TYPE_1(16383 downto 0) := (others => (others => '0'));
    
      shared variable font_ram : RAM_TYPE_1(16383 downto 0) := (others => (others => '0'));
      shared variable char_ram : RAM_TYPE_2(8091 downto 0) := (others => (others => '0'));
    Code:
      RAM_PORT_A : process(clk_uart)
      begin
        if (rising_edge(clk_uart)) then
          if (vram_we_a = '1') then
            video_ram(conv_integer(vram_addr_a)) := vram_data_a;
          end if;
          if (fram_we_a = '1') then
            font_ram(conv_integer(fram_addr_a)) := fram_data_a;
          end if;
          if (cram_we_a = '1') then
            char_ram(conv_integer(cram_addr_a)) := cram_data_a;
          end if;
        end if;
      end process;
    
      RAM_PORT_B : process(clk_dvi)
      begin
        if (rising_edge(clk_dvi)) then
          vram_data_b <= video_ram(conv_integer(vram_addr_bb));
          fram_data_b <= font_ram(conv_integer(fram_addr_b));
          cram_data_b <= char_ram(conv_integer(cram_addr_b));
        end if;
      end process;
    Mỗi 5 8-bit data word từ RS-232 port sẽ được gọp lại thành một nhóm. 2 data word đầu tiên được sử dụng làm BRAM address, và 3 data word sau là BRAM data, nên chúng ta có được 16-bit address và 24-bit data. Với 16-bit address, 14 LSB được dùng làm RAM address, và 2 MSB được dùng làm RAM select, để viết đến chỉ một trong ba khối RAM (video / font / character).

    Code:
    UART_DATA_DECODER : process(clk_uart)
      begin
        if (rising_edge(clk_uart)) then
          -------
          if (uart_data_count = 4) then
            vram_we_a <= uart_data_valid_out and ram_select(0);
            fram_we_a <= uart_data_valid_out and ram_select(1);
            cram_we_a <= uart_data_valid_out and ram_select(2);
          else
            vram_we_a <= '0';
            fram_we_a <= '0';
            cram_we_a <= '0';
          end if;
          -------
          if (uart_data_valid_out = '1') then
            -------
            if (uart_data_count = 1) then
              case uart_data_out(7 downto 6) is
                when "00" => ram_select <= "001";
                when "01" => ram_select <= "010";
                when others => ram_select <= "100";
              end case;
            end if;
            -------
            if (uart_data_count = 4) then
              uart_data_count <= 0;
            else
              uart_data_count <= uart_data_count + 1;
            end if;
            -------
            case uart_data_count is
              when 0 =>
                vram_addr_a(7 downto 0) <= uart_data_out;
                fram_addr_a(7 downto 0) <= uart_data_out;
                cram_addr_a(7 downto 0) <= uart_data_out;
              when 1 =>
                vram_addr_a(13 downto 8) <= uart_data_out(5 downto 0);
                fram_addr_a(13 downto 8) <= uart_data_out(5 downto 0);
                cram_addr_a(12 downto 8) <= uart_data_out(4 downto 0);
              when 2 =>
                vram_data_a(7 downto 0) <= uart_data_out;
                fram_data_a(7 downto 0) <= uart_data_out;
                cram_data_a(7 downto 0) <= uart_data_out;
              when 3 =>
                vram_data_a(15 downto 8) <= uart_data_out;
                fram_data_a(15 downto 8) <= uart_data_out;
                cram_data_a(7 downto 0) <= cram_data_a(7 downto 0);
              when others =>
                vram_data_a(23 downto 16) <= uart_data_out;
                fram_data_a(23 downto 16) <= uart_data_out;
                cram_data_a(7 downto 0) <= cram_data_a(7 downto 0);
            end case;
          end if;
        end if; -- clk
      end process UART_DATA_DECODER;
    RS-232 rất chậm, nên nếu bạn có đường chuyền nhanh hơn thì nên sử dụng.

    Comment


    • #3
      Lưu ý là data enable được delay 6 clock cycles bên dưới. Nếu delay = 0, data enable sẽ được assert ngay khi x = 0, nhưng vì x và y values được dùng để tra display data, nên chúng ta cần delay 6 cycles.

      Code:
      dvi_timing_generator_inst : entity work.dvi_timing_generator(rtl)
        generic map (
          G_WIDTH     =>  DVI_CONFIG.width,
          G_H_FP      =>  DVI_CONFIG.h_fp,
          G_H_SYNC    =>  DVI_CONFIG.h_sync,
          G_H_BP      =>  DVI_CONFIG.h_bp,
          G_HEIGHT    =>  DVI_CONFIG.height,
          G_V_FP      =>  DVI_CONFIG.v_fp,
          G_V_SYNC    =>  DVI_CONFIG.v_sync,
          G_V_BP      =>  DVI_CONFIG.v_bp,
          G_H_POLAR   =>  DVI_CONFIG.h_polar,
          G_V_POLAR   =>  DVI_CONFIG.v_polar,
          G_DE_DELAY  =>  6
        )
        port map (
          clk => clk_dvi,
          rst => rst,
          x_coord_o => dvi_xcoord,
          y_coord_o => dvi_ycoord,
          hsync_o => hsync,
          vsync_o => vsync,
          data_enable_o => data_enable
        );
      RAM read address được tính ra từ x và y values.

      Code:
      vram_addr_b <= dvi_ycoord_dly02(6 downto 0) & dvi_xcoord_dly02(6 downto 0) when rising_edge(clk_dvi);
        fram_addr_b <= cram_data_b(6 downto 0) & dvi_ycoord_dly02(3 downto 0) & dvi_xcoord_dly02(2 downto 0) when rising_edge(clk_dvi);
        cram_addr_b <= dvi_ycoord(9 downto 4) & dvi_xcoord(9 downto 3) when rising_edge(clk_dvi);
      Cổng data cho DVI chỉ có 12-bit, nên 24-bit data được output với cả rising and falling clock edges.

      Code:
      display_data <= vram_data_b xor fram_data_b when falling_edge(clk_dvi);
      
      DVI_DATA_GEN : for i in 0 to 11 generate
          ODDR_inst : ODDR port map ( R => '0', S => '0', CE => '1', C => clk_dvi, D1 => display_data(i+12), D2 => display_data(i), Q => DVI_DATA(i));
        end generate;
      Phần trên FPGA chỉ có vậy.

      Comment


      • #4
        Chúng ta sẽ sử dụng PySerial và Python Image Library trên Python để kết nối và load data.

        Python Image Library có thể được sử dụng để tạo ra font bitmap.



        Bên dưới là font bitmap 'X' (8x16) đã được zoom in.



        Chúng ta sẽ dùng for loop để tạo ra 8x16 image cho các character, và đưa vào font memory qua cổng RS-232. Chúng ta tuần tự ghi 2 8-bit word cho address, và 3 8-bit word cho data.



        Tạo thêm một string nửa để display, và chúng ta sẽ dùng một for loop khác để viết text lên monitor:



        Bên dưới là một phần của màn ảnh:



        Chúng ta cũng có thể load 128x128 image lên video memory:






        Một project nhỏ để các bạn tham khảo.

        Comment

        Về tác giả

        Collapse

        nemesis21 Tìm hiểu thêm về nemesis21

        Bài viết mới nhất

        Collapse

        Đang tải...
        X