반응형

기존 파일에서 이어서 간다.

GitHub - westonb/OV7670-Verilog: Verilog modules required to get the OV7670 camera working

 

GitHub - westonb/OV7670-Verilog: Verilog modules required to get the OV7670 camera working

Verilog modules required to get the OV7670 camera working - westonb/OV7670-Verilog

github.com

 

SCCB 인터페이스 동작에 대해 전에 봤고, 이번에는 카메라를 볼 차례이다.

 

camera_config_tb.v 를 보도록 한다.

 

마찬가지로 modelsim 으로 파형을 보는데, continue 를 눌러 끝까지 실행시킨다.

 

 

내부 플래그 이름을 보면 버스와 클럭에 관계되는

전체를 관리하는 부분 같다.

 

어,,

조금 이따 보고 read 먼저 본다.

 

camera_read_tb.v 파일은 다음과 같은 구조다.

 

module camera_read_tb;

// inputs 
reg p_clock;
reg vsync;
reg href;
reg [7:0] p_data;

//outputs 
wire [15:0] pixel_data;
wire pixel_valid;
wire frame_done;

 camera_read camera_read_1 (
        .p_clock(p_clock), 
        .vsync(vsync), 
        .href(href),
        .p_data(p_data), 
        .pixel_data(pixel_data), 
        .pixel_valid(pixel_valid),
        .frame_done(frame_done)
    );
    
    always #5 p_clock = ~p_clock;
    
    initial begin 
    
    p_clock = 0;
    vsync = 0;
    href = 0;
    p_data = 0;
    #5;
    #100;
    
    vsync = 1;
    #10;
    vsync = 0;
    #10;
    href = 1; 
    p_data = 8'hFF;
    #10; 
    p_data = 8'h0;
    #10;
    href = 0;
    #10;
    vsync = 0; 
    
    
    end
    
    
endmodule

 

일단 이것만 봐서는 좀 이해가 안 된다.

 

파형을 한 번 살펴본다.

 

 

105ns 에서 Vsync가 1로 뜨고, 115ns 에서 vsync 가 0으로 내려간다.

125ns에서 href가 1로 뜨고, p_data가 0xFF 값이 들어간다.

135ns 에서 p_data 가 0x00 값이 들어간다.

145ns 에서 href 가 다시 0으로 내려간다.

155ns 에서 vsync 가 다시 0으로 내려가는(아까 전에 0이였음) 흐름으로 테스트벤치가 구성되어 있다.

 

camera_read.v 본문 내용을 한 번 살펴본다.

 

module camera_read(
	input wire p_clock,
	input wire vsync,
	input wire href,
	input wire [7:0] p_data,
	output reg [15:0] pixel_data =0,
	output reg pixel_valid = 0,
	output reg frame_done = 0
    );
	 
	
	reg [1:0] FSM_state = 0;
    reg pixel_half = 0;
	
	localparam WAIT_FRAME_START = 0;
	localparam ROW_CAPTURE = 1;
	
	
	always@(posedge p_clock)
	begin 
	
	case(FSM_state)
	
	WAIT_FRAME_START: begin //wait for VSYNC
	   FSM_state <= (!vsync) ? ROW_CAPTURE : WAIT_FRAME_START;
	   frame_done <= 0;
	   pixel_half <= 0;
	end
	
	ROW_CAPTURE: begin 
	   FSM_state <= vsync ? WAIT_FRAME_START : ROW_CAPTURE;
	   frame_done <= vsync ? 1 : 0;
	   pixel_valid <= (href && pixel_half) ? 1 : 0; 
	   if (href) begin
	       pixel_half <= ~ pixel_half;
	       if (pixel_half) pixel_data[7:0] <= p_data;
	       else pixel_data[15:8] <= p_data;
	   end
	end
	
	
	endcase
	end
	
endmodule

 

굉장히 짧게 구성된 코드이다.

다른것 볼 필요 없이 state machine 만 보도록 한다.

 

WAIT_FRAME_START: begin //wait for VSYNC
   FSM_state <= (!vsync) ? ROW_CAPTURE : WAIT_FRAME_START;
   frame_done <= 0;
   pixel_half <= 0;
end

 

VSYNC 가 1일 때 frame_start(현재 state) 를 유지하고, 0이면 ROW_CAPTURE state 로 넘어간다.

frame_done, pixel_half 는 아직 나온것이 없기 때문에 초기화 해 주는 내용이라고 생각하고 넘어간다.

 

ROW_CAPTURE: begin 
   FSM_state <= vsync ? WAIT_FRAME_START : ROW_CAPTURE;
   frame_done <= vsync ? 1 : 0;
   pixel_valid <= (href && pixel_half) ? 1 : 0; 
   if (href) begin
       pixel_half <= ~ pixel_half;
       if (pixel_half) pixel_data[7:0] <= p_data;
       else pixel_data[15:8] <= p_data;
   end
end

 

ROW_CAPTURE state 에서

VSYNC가 1이면 WAIT_FRAME_START, 0이면 ROW_CAPTURE(현재 유지)가 된다. 아까의 state 와 반대이다.

그리고, frame_done 도 VSYNC 에 따라 바뀐다.

VSYNC 가 1이 되면 state 를 프레임 시작을 기다리고, 현재 프레임을 끝났다 라고 생각하게 만든다. 라고 볼 수 있다.

 

href 가 활성화 된 경우

pixel_half 값 반전,

pixel_half 가 1이면 하위 8비트에 픽셀 데이터를 넣음.

pixel_half 가 0이면 상위 8비트에 픽셀 데이터를 넣음.

 

역시나 이것만으로는 동작에 대한 설명이 좀 부족하다.

vsync와 href 의 동작을 알아야 카메라에서 어떤 값을 어떤 방식으로 받아올 수 있는지 알 수 있을 것이다.

 

 

 

 

 


 

다음으로 ov7670_config.v 를 보도록 한다.(camera_configure.v 가 아니다)

 

module OV7670_config
#(
    parameter CLK_FREQ = 25000000
)
(
    input wire clk,
    input wire SCCB_interface_ready,
    input wire [15:0] rom_data,
    input wire start,
    output reg [7:0] rom_addr,
    output reg done,
    output reg [7:0] SCCB_interface_addr,
    output reg [7:0] SCCB_interface_data,
    output reg SCCB_interface_start
    );
    
    initial begin
        rom_addr = 0;
        done = 0;
        SCCB_interface_addr = 0;
        SCCB_interface_data = 0;
        SCCB_interface_start = 0;
    end
    
    localparam FSM_IDLE = 0;
    localparam FSM_SEND_CMD = 1;
    localparam FSM_DONE = 2;
    localparam FSM_TIMER = 3;
    
    reg [2:0] FSM_state = FSM_IDLE;
    reg [2:0] FSM_return_state;
    reg [31:0] timer = 0; 
    
    always@(posedge clk) begin
    
        case(FSM_state)
            
            FSM_IDLE: begin 
                FSM_state <= start ? FSM_SEND_CMD : FSM_IDLE;
                rom_addr <= 0;
                done <= start ? 0 : done;
            end
            
            FSM_SEND_CMD: begin 
                case(rom_data)
                    16'hFFFF: begin //end of ROM
                        FSM_state <= FSM_DONE;
                    end
                    
                    16'hFFF0: begin //delay state 
                        timer <= (CLK_FREQ/100); //10 ms delay
                        FSM_state <= FSM_TIMER;
                        FSM_return_state <= FSM_SEND_CMD;
                        rom_addr <= rom_addr + 1;
                    end
                    
                    default: begin //normal rom commands
                        if (SCCB_interface_ready) begin
                            FSM_state <= FSM_TIMER;
                            FSM_return_state <= FSM_SEND_CMD;
                            timer <= 0; //one cycle delay gives ready chance to deassert
                            rom_addr <= rom_addr + 1;
                            SCCB_interface_addr <= rom_data[15:8];
                            SCCB_interface_data <= rom_data[7:0];
                            SCCB_interface_start <= 1;
                        end
                    end
                endcase
            end
                        
            FSM_DONE: begin //signal done 
                FSM_state <= FSM_IDLE;
                done <= 1;
            end
                           
                
            FSM_TIMER: begin //count down and jump to next state
                FSM_state <= (timer == 0) ? FSM_return_state : FSM_TIMER;
                timer <= (timer==0) ? 0 : timer - 1;
                SCCB_interface_start <= 0;
            end
        endcase
    end
endmodule

 

처음 부분에서 초기화를 시키고 있다.

 

initial begin
    rom_addr = 0;
    done = 0;
    SCCB_interface_addr = 0;
    SCCB_interface_data = 0;
    SCCB_interface_start = 0;
end

 

State 로 넘어간다.

 

FSM_IDLE: begin 
    FSM_state <= start ? FSM_SEND_CMD : FSM_IDLE;
    rom_addr <= 0;
    done <= start ? 0 : done;
end

 

start 입력이 들어오면 다음 state 인 FSM_SEND_CMD 로 넘어가고, 아니면 유지한다.

특이하게 done 값을 유지하고 있다. start 가 시작되면 다시 done 은 0으로 초기화된다.

 

FSM_SEND_CMD: begin 
    case(rom_data)
        16'hFFFF: begin //end of ROM
            FSM_state <= FSM_DONE;
        end
        
        16'hFFF0: begin //delay state 
            timer <= (CLK_FREQ/100); //10 ms delay
            FSM_state <= FSM_TIMER;
            FSM_return_state <= FSM_SEND_CMD;
            rom_addr <= rom_addr + 1;
        end
        
        default: begin //normal rom commands
            if (SCCB_interface_ready) begin
                FSM_state <= FSM_TIMER;
                FSM_return_state <= FSM_SEND_CMD;
                timer <= 0; //one cycle delay gives ready chance to deassert
                rom_addr <= rom_addr + 1;
                SCCB_interface_addr <= rom_data[15:8];
                SCCB_interface_data <= rom_data[7:0];
                SCCB_interface_start <= 1;
            end
        end
    endcase
end

 

rom_data 를 보고 FFFF signal(롬 종료 시그널) 이 오면 init command 보내는 것을 종료한다.

이 부분은 OV7670 내부 레지스터 초기 세팅을 하는 부분이라고 봐도 좋다.

 

여기도 따로 delay state 를 생성해 유지한다. FFF0 signal 이 오면 delay 10ms 만큼 delay 시킨다.

 

기본적으로는 SCCB interface ready flag가 뜨면 command 를 계속 보낸다.

rom 파일을 보게 되면 이상하게도 16비트가 나오는데, 앞은 reg address, 뒤는 value 로 나뉘는 것을 여기서 볼 수 있다.

 

결론은 rom 파일 내부 init command 를 모두 버스로 보내기 위해 사용하는 모듈이라고 보면 되겠다.

 

Done 상태는 뭐 없으니 넘어가도록 한다.

 


 

다시 camera_configure 로 온다.

tb 먼저 보도록 한다.

 

module camera_configure_tb;

    // inputs

    reg clk;
    reg start;
    
    //outputs 
    
    wire done;
    wire sioc;
    wire siod;
    
    camera_configure dut1
        (
        .clk(clk),
        .start(start),
        .sioc(sioc),
        .siod(siod),
        .done(done)
        );
        
        always #5 clk = ~ clk;
        
        initial begin
        clk = 0;
        start = 0;
        #100;
        start = 1;
        #10;
        start = 0;
        @(posedge done) $finish;
        end
endmodule

 

그냥 start 만 넣어주는 코드라고 봐도 무방하겠다.

FPGA 에 구현했다면, 마치 버튼을 눌러 동작 시작을 시켰다와 같다.

 

camera_configure.v 로 가 본다.

 

module camera_configure
    #(
    parameter CLK_FREQ=25000000
    )
    (
    input wire clk,
    input wire start,
    output wire sioc,
    output wire siod,
    output wire done
    );
    
    wire [7:0] rom_addr;
    wire [15:0] rom_dout;
    wire [7:0] SCCB_addr;
    wire [7:0] SCCB_data;
    wire SCCB_start;
    wire SCCB_ready;
    wire SCCB_SIOC_oe;
    wire SCCB_SIOD_oe;
    
    assign sioc = SCCB_SIOC_oe ? 1'b0 : 1'bZ;
    assign siod = SCCB_SIOD_oe ? 1'b0 : 1'bZ;
    
    OV7670_config_rom rom1(
        .clk(clk),
        .addr(rom_addr),
        .dout(rom_dout)
        );
        
    OV7670_config #(.CLK_FREQ(CLK_FREQ)) config_1(
        .clk(clk),
        .SCCB_interface_ready(SCCB_ready),
        .rom_data(rom_dout),
        .start(start),
        .rom_addr(rom_addr),
        .done(done),
        .SCCB_interface_addr(SCCB_addr),
        .SCCB_interface_data(SCCB_data),
        .SCCB_interface_start(SCCB_start)
        );
    
    SCCB_interface #( .CLK_FREQ(CLK_FREQ)) SCCB1(
        .clk(clk),
        .start(SCCB_start),
        .address(SCCB_addr),
        .data(SCCB_data),
        .ready(SCCB_ready),
        .SIOC_oe(SCCB_SIOC_oe),
        .SIOD_oe(SCCB_SIOD_oe)
        );
    
endmodule

 

기본적으로 지금까지 봤던 모듈들을 재사용하는데, 한 가지 read 가 없다.

여기서는 SCCB interface 를 통해 동작시키는 것 까지가 메인이 되겠다.

 

전체 파형은 다음과 같다.

 

 

read 는 이 이후, SCCB init register setting 이 끝난 이후의 vsync, href 값을 판단해서 들고 와서 hdmi, vga 등 화면을 띄울 수 있는 인터페이스에 픽셀 데이터를 넘겨주면 될 것 같다.

반응형

'Verilog' 카테고리의 다른 글

[Verilog] SCCB & OV7670 - more1  (0) 2024.05.28
[Verilog] SCCB Interface & OV7670  (0) 2024.04.16

+ Recent posts