[HARMAN] 세미콘 아카데미/VGA

[Harman 세미콘 아카데미] Day_73(VGA)

uiop1716 2025. 5. 28. 09:12

오늘부터 영상처리에 대한 VGA를 배웁니다.

 

 

먼저, 디스플레이의 기초에 대해 알아 보았습니다.

브라운 박사가 진행한 음극선 실험으로 처음 영상에 대한 개념이 등장하였습니다. 진공관 내에서 가속화된 전자를 형광 물질 (= 음극선)에 쏘았을 때 발생하는 빛을 발견하였고, 이를 통해 CRT가 등장하였습니다. 저희가 옛날에 쓰던 브라운관이 되는 것이죠

브라운관

 

필라멘트 코일을 통해 전자를 활성화하고, Annode를 통해 전자를 모으고 가속화합니다. 과속화된 전자들을 상하, 좌우 편향판을 거쳐 방향을 조절하여 형광물질이 발린 화면에 쏘게 됩니다. 전자를 받은 화면은 빛을 방출하게 되고, 이 빛의 조합으로 영상이 출력됩니다.

Cathod Ray Tube

 

 

 

FPGA와 디스플레이를 연결해줄 VGA Port에 대해 간략히 설명하면

 

- R, G, B : 아날로그 전압으로 색상을 표현, 전압 레벨에 따라 모니터에 색상 구현

- H Sync (Horizontal Syncronizer) : Screen의 하나의 가로줄에 전자를 쏘는 것의 끝지점을 알림

- V Sync (Vertical Syncronizer) : Screen의 하나의 세로줄에 전자를 쏘는 것의 끝지점을 알림

VGA Analog Port

 

 - porch : 디스플레이가 꺼지는 부분 (back porch, front porch)   -> 디스플레이 출력 X

 - sync : porch 사이에 Horizontal Sync가 떨어지는 부분 -> 동기화 신호에 대한 부분

 

 

 

 

아래의 그림을 통해 porch 부분은 디스플레이 되지 않고, 나머지 부분에만 빛이 발생하게 됩니다.

 

 

 

이러한 개념을 Code로 구현하기 편하게 하기 위해 아래의 그림과 같이 시점을 변화시켜 줍니다.

Display Time을 먼저 실행하고 back porch, sync, front porch를 차례로 구현하여 줍니다. 이렇게 변화시켜서 counter의 개념으로 접근한다면 Code상에서 구현하기 쉬울 것 같습니다.

Code로 구현하기 쉽게 변경
가벼운 설명

 

 

아래의 그림을 통해, 1개의 line에 전자를 쏘는데 31.77us가 소모되고, Display time은 25.17us 임을 확인할 수 있습니다. Front porch의 소모 시간은 약 1us정도, sync는 약 3.7us 정도 소모 되는 것을 확인할 수 있습니다.

640X480의 해상도를 나타내기 위해서 800 pixel이 525개의 line이 필요합니다. 이를 1초로 계산하여 800 * 525 * 60(frame)을 진행하여 25.2MHz의 주파수가 필요합니다. 이 주파수를 통해 pixel을 찍어 디스플레이를 만들게 됩니다.

 

아래의 그림을 통해 해상도에 따라 pixel 규격이 존재함을 확인합니다.

Horizontal에 관해

       -  A :  총 가로 line = 800 pixel

       -  B :  H-Sync = 96 pixel

       -  C :  Back Porch = 48 pixel

       -  D :  Vidieo Time = 640 pixel 

       -  E :  Front Porch = 16 pixel

 

Veritcal은

       -  O :  총 세로 line = 525 pixel

       -  P :  V-Sync = 2 pixel

       -  Q :  Back Porch = 33 pixel

       -  R :  Video Time = 480 pixel

       -  S :  Front Porch = 10 pixel

VGA Signal Timing

 

 

Basys3 보드는 VGA 포트와 연결 시, 각 색상(R, G, B)에 대해 4비트씩 디지털 신호를 출력합니다. 이 신호들은 각각 다른 저항값을 가지는 DAC를 통해 아날로그 전압으로 변환하여 VGA 디스플레이로 전달합니다. VGA 포트에는 이러한 아날로그 RGB 신호를 받는 핀과, 논리값(0 or 1)으로 동작하는 수평/수직 동기화(HSync, VSync) 핀이 존재합니다. 이러한 값들을 통해 색상을 조합하여 픽셀을 모니터 화면에 출력하게 됩니다.

VGA Port에 대한 설명

 

 

 

640 x 480 @ 60Hz의 standard Timing을 확인하면, 25MHz의 간격으로 1개의 pixel이 생성됩니다. FPGA 보드에서는 100MHz를 사용하고 있으므로, 이를 분주하여 25MHz의 clk를 생성합니다. 이 clk에 대해 counter를 추가하여, 가로로는 800까지 세로로는 525까지 세도록 하여 전체 1frame을 구현하게 됩니다.

가로로 800, 세로로 525까지 세는 counter를 가로로 640, 세로로 480을 세어 Display Time을 생성하고 나머지 가로 160, 세로 45 pixel에 대해 세어 retrace time에 대해 구현합니다. 또한, 각 4bit의 Data를 받아 빛의 세기를 조절하는 RGB도 구현하여 스위치 조절을 통해 총 12bit를 받아 색의 밝기 조절 및 색상 조합을 나타내게 됩니다.

(주의할 점은, Display Time에만 DE신호를 'High'로 유지하여, 해당 시간에만 빛이 출력되도록 합니다) 

 

 

 

이를 Block Diagram으로 나타내면 아래와 같습니다.

 

 

pixel_clk_ged 모듈은 100MHz를 4번 Count 할때 마다, 25MHz의 pclk를 생성해 줍니다.

25MHz Clk_Divider

 

 

그리고, 이 25MHz의 pclk를 활용하여 가로로 800, 세로로 525의 pixel을 세도록 하는 Counter를 설계합니다.

가로 선인 Horizontal counter는 디스플레이를 기준으로 좌에서 우로 1pixel씩 찍으며, 총 800 pixel을 찍어 1개의 가로 line을 채우고 다시 왼쪽으로 돌아갑니다. 세로선인 Vertical counter는 디스플레이의 상에서 하로 이동시키며 가로선을 찍으므로, 가로 선이 800pixel을 찍고 왼쪽으로 돌아와 초기화되면 counter값을 증가시키도록 설계합니다.

즉, 좌우로 800개의 pixel을 찍으면 아래의 방향으로 움직이며 총 800*525 pixel로 되어있는 1frame을 구성하게 됩니다.

Pixel_Counter 설계

 

 

Counter까지 설계하고, 이를 활용하여 Sync 신호를 만들어 주어야 합니다.

H-Sync를 보게 되면, (Display_Time + Front porch)  ~  (Display_Time + Front porch + H-sync Time) 사이에서 low로 떨어지게 됩니다. V-Sync도 마찬가지로  (Display_Time + Front porch)  ~  (Display_Time + Front porch + V-sync Time) 사이에서 low로 떨어지게 됩니다. 이를 assign문으로 할당하여 H/V의 Sync에 대해 생성합니다.

 

또한, Display Time에서 (즉, 640 * 480)의 구간에서만 counter의 값을 받아 RGB에 Data를 전달하는 x_pixel과 y_pixel을 DE 신호로 조절해 줍니다. DE는 Display Time 구간에서만 동작하는 조건을 가지고 pixel data를 조절합니다.

vga_decoder Code (Sync, Data 통로 역할)

 

 

위에서 생성한 모듈들을 하나의 Decoder로 모아 1frame을 구성하기 위한 pixel들에 대해 기술하였습니다.

Top Module Vga Decoder

 

그리고, Switch를 통해 RGB 빛의 세기를 조절하는 모듈을 연결하였습니다. 각 색상마다 4개의 Switch를 할당받게 되고, VGA port와 연결된 각 bit들은 저항이 다르게 연결되어 있어 세기 조절이 가능합니다.

Switch를 통한 RGB 색상 및 세기 조절

 

 

 

아래와 같은 Schematic이 생성됨을 확인할 수 있고, 이를 Testbench를 통해 알아보겠습니다.

Schematic

 

 

 

기본적인 파형을 보면 (RGB에 아무 값을 넣지 않았을 때)

x_pixel이 640에 도달하여 h_sync가 1번 발생하게 됩니다. 이 h_sync가 총800번 발생하면 v_sync가 1번 발생하게 됩니다.

이렇게 1개의 line을 처리하게 되었고, 이를 총 525번 반복해야 1화면 (= 1 frame)을 출력할 수 있습니다.

Zoom In

 

Zoom Out해서 확인한 결과 입니다.

Zoom Out

 

 

 

VGA port에 대한 Constraint를 설정해주고, HDMI를 연결하여 Switch 조절을 통해 화면에 출력되는 색을 조절할 수 있습니다.

Constraint 설정

 

 

 

그리고 실습으로, 화면 조정을 출력하도록 설계를 해보았습니다. 삐소리가 나면서 나오는 화면 아시죠?

640 * 480의 화면에 x,y에 대해 각각 구간별로 지정해서 RGB 색 조합을 통해 나타내었습니다.

 

코드:

https://github.com/Heeju99/Code_Verilog_SystemVerilog/tree/main/workspace/250528_VGA_start/250528_VGA_start.srcs/sources_1/new

 

 

Code_Verilog_SystemVerilog/workspace/250528_VGA_start/250528_VGA_start.srcs/sources_1/new at main · Heeju99/Code_Verilog_System

Contribute to Heeju99/Code_Verilog_SystemVerilog development by creating an account on GitHub.

github.com

 

 

코드는 아래와 같이 작성했는데, RGB값에 대해 직접 할당해 주었습니다. 이보다 더 편한 방법이 있을 것 같습니다.

module vga_jojung(
    input logic DE,
    input logic [9:0] x_pixel,
    input logic [9:0] y_pixel,

    output logic [3:0] red_port2,
    output logic [3:0] green_port2,
    output logic [3:0] blue_port2
);

    logic [3:0] red_port;
    logic [3:0] green_port;
    logic [3:0] blue_port;

    assign red_port2   = DE ? red_port : 4'b0;
    assign blue_port2  = DE ? blue_port : 4'b0;
    assign green_port2 = DE ? green_port : 4'b0;

    always_comb begin
            if(y_pixel < 320) begin
                if(x_pixel < 91) begin  //white
                    red_port = 4'b1111;
                    green_port = 4'b1111;
                    blue_port = 4'b1111;
                end else if (x_pixel < 182) begin //Yellow
                    red_port = 4'b1111;
                    green_port = 4'b1111;
                    blue_port = 4'b0000;
                end else if (x_pixel < 273) begin //Cyan
                    red_port = 4'b0000;
                    green_port = 4'b1111;
                    blue_port = 4'b1111;
                end else if (x_pixel < 364) begin //Green
                    red_port = 4'b0000;
                    green_port = 4'b1111;
                    blue_port = 4'b0000;
                end else if (x_pixel < 455) begin //Magenta
                    red_port = 4'b1111;
                    green_port = 4'b0000;
                    blue_port = 4'b1111;
                end else if (x_pixel < 546) begin  //Red
                    red_port = 4'b1111;
                    green_port = 0;
                    blue_port = 0;
                end else if (x_pixel <= 639) begin //Blue
                    red_port = 0;
                    green_port = 0;
                    blue_port = 4'b1111;
                end
            //2번째 줄
            end else if(y_pixel < 375) begin
                if(x_pixel < 91) begin      //blue
                    red_port = 0;
                    green_port = 0;
                    blue_port = 4'b1111;
                end else if (x_pixel < 182) begin //black
                    red_port = 0;
                    green_port = 0;
                    blue_port = 0;
                end else if (x_pixel < 273) begin //magenta
                    red_port = 4'b1111;
                    green_port = 0;
                    blue_port = 4'b1111;
                end else if (x_pixel < 364) begin //black
                    red_port = 0;
                    green_port = 0;
                    blue_port = 0;
                end else if (x_pixel < 455) begin //Cyan
                    red_port = 0;
                    green_port = 4'b1111;
                    blue_port = 4'b1111;
                end else if (x_pixel < 546) begin //black
                    red_port = 0;
                    green_port = 0;
                    blue_port = 0;
                end else if (x_pixel <= 639) begin //white
                    red_port = 4'b1111;
                    green_port = 4'b1111;
                    blue_port = 4'b1111;
                end
            //3번째 줄
            end else if(y_pixel <= 479) begin
                if(x_pixel < 100) begin //navy
                    red_port = 4'b0000;
                    green_port = 4'b0101;
                    blue_port = 4'b1111;
                end else if (x_pixel < 200) begin //white
                    red_port = 4'b1111;
                    green_port = 4'b1111;
                    blue_port = 4'b1111;
                end else if (x_pixel < 300) begin //purple
                    red_port = 4'b1101;
                    green_port = 4'b0001;
                    blue_port = 4'b1111;
                end else if (x_pixel < 400) begin //black
                    red_port = 0;
                    green_port = 0;
                    blue_port = 4'b0001;
                //3분할 시작
                end else if (x_pixel < 448) begin //black
                    red_port = 0;
                    green_port = 0;
                    blue_port = 0;
                end else if (x_pixel < 496) begin //charcol
                    red_port = 0;
                    green_port = 0;
                    blue_port = 4'b0001;
                end else if (x_pixel < 546) begin //more white
                    red_port = 0;
                    green_port = 4'b0001;
                    blue_port = 4'b0001;
                //3분할 종료
                end else if (x_pixel <= 639) begin //black
                    red_port = 0;
                    green_port = 0;
                    blue_port = 0;
                end
            end
        end
endmodule

 

VGA 화면 조정 생성