기존 파일에서 이어서 간다.
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 |