L'intégration d'un module caméra OV5640 nécessite de traiter le flux de données brut qu'il émet. Après son initialisation via une interface de type I2C, ce capteur CMOS délivre des données au format RGB565. Cependant, les premières images capturées après la mise sous tension sont souvent instables. Il est donc courant d'ignorer un certain nombre de trames initiales avant de traiter le signal vidéo utile.
Un défi technique majeur réside dans la gestion des domaines d'horloge distincts. Le pixel clock (pclk) fourni par la caméra à l'interface FPGA est asynchrone par rapport à l'horloge système principale. Un copmosant de type FIFO asynchrone est indispensable pour assurer un transfert de données robuste entre ces deux domaines horaires.
Le module de premier niveau (CameraDataBridge) orchestre cette opération. Il instancie un décodeur vidéo et une FIFO pour produire un flux de données aligné sur l'horlogee système. Le format de sortie combine les données pixel et des marqueurs de trame/ligne.
`timescale 1ns / 1ps
module CameraDataBridge #(
parameter DISCARD_FRAMES = 15,
parameter FIFO_DEPTH = 2048,
parameter IMAGE_WIDTH = 1280,
parameter IMAGE_HEIGHT = 720
)(
input wire sys_clk,
input wire sys_rst_n,
input wire cam_pixel_clk,
input wire cam_data_clk,
input wire cam_vsync,
input wire cam_href,
input wire [7:0] cam_din,
output wire cam_mclk,
input wire fifo_rd_ready,
output wire [31:0] stream_out_data,
output wire stream_out_valid,
output wire frame_reset,
output wire vsync_out
);
wire decoded_vsync;
wire decoded_href;
wire decoded_de;
wire [15:0] rgb_pixel;
wire frame_start;
wire line_end;
// Reset synchronizer
reg [5:0] sys_rst_sync;
always @(posedge cam_data_clk) begin
sys_rst_sync <= {sys_rst_sync[4:0], sys_rst_n};
end
// Video Decoder Instance
VideoSignalProcessor #(
.DISCARD_FRAMES(DISCARD_FRAMES),
.IMAGE_WIDTH(IMAGE_WIDTH),
.IMAGE_HEIGHT(IMAGE_HEIGHT)
) u_video_decoder (
.clk(cam_data_clk),
.rst_n(sys_rst_n),
.vsync_in(cam_vsync),
.href_in(cam_href),
.data_in(cam_din),
.vsync_out(decoded_vsync),
.href_out(decoded_href),
.data_enable(decoded_de),
.rgb_out(rgb_pixel),
.frame_start(frame_start),
.line_end(line_end)
);
// Vsync edge detector for FIFO reset
reg [3:0] vsync_pipe;
always @(posedge sys_clk) begin
vsync_pipe <= {vsync_pipe[2:0], cam_vsync};
end
wire fifo_reset = (~vsync_pipe[2] & vsync_pipe[3]) | (~sys_rst_sync[5]);
// FIFO input assembly
wire [17:0] fifo_wr_data = {frame_start, line_end, rgb_pixel};
wire fifo_empty;
// Async FIFO for clock domain crossing
xpm_fifo_async #(
.FIFO_MEMORY_TYPE("block"),
.ECC_MODE("no_ecc"),
.RELATED_CLOCKS(0),
.FIFO_WRITE_DEPTH(FIFO_DEPTH),
.WRITE_DATA_WIDTH(18),
.READ_MODE("fwft"),
.FIFO_READ_LATENCY(0),
.READ_DATA_WIDTH(18),
.CDC_SYNC_STAGES(2)
) u_async_fifo (
.rst(fifo_reset),
.wr_clk(cam_data_clk),
.wr_en(decoded_de),
.din(fifo_wr_data),
.rd_clk(sys_clk),
.rd_en(fifo_rd_ready),
.dout(fifo_rd_data),
.empty(fifo_empty),
// Unused ports omitted for brevity
.full(),
.overflow(),
.underflow()
);
// Output assignments
assign cam_mclk = cam_pixel_clk;
assign stream_out_valid = ~fifo_empty & fifo_rd_ready;
assign vsync_out = decoded_vsync;
assign frame_reset = fifo_reset;
// Expand 18-bit FIFO data to 32-bit AXI-Stream format
assign stream_out_data = {fifo_rd_data[17:16], 6'b0,
fifo_rd_data[15:11], 3'b0,
fifo_rd_data[10:5], 2'b0,
fifo_rd_data[4:0], 3'b0};
endmodule
Le module de décodage (VideoSignalProcessor) se concentre sur le traitemant du signal brut émis par le capteur. Sa tâche principale est de synchroniser les signaux de contrôle et d'assembler les octets consécutifs en pixels RGB de 16 bits. Il génère également les indicateurs de début et de fin nécessaires au flux de sortie.
`timescale 1ns / 1ps
module VideoSignalProcessor #(
parameter DISCARD_FRAMES = 15,
parameter IMAGE_WIDTH = 640,
parameter IMAGE_HEIGHT = 480
)(
input wire clk,
input wire rst_n,
input wire vsync_in,
input wire href_in,
input wire [7:0] data_in,
output reg vsync_out,
output reg href_out,
output wire data_enable,
output reg [15:0] rgb_out,
output wire frame_start,
output wire line_end
);
// Input synchronization
reg [2:0] vsync_reg;
reg [2:0] href_reg;
reg [7:0] data_reg;
always @(posedge clk) begin
vsync_reg <= {vsync_reg[1:0], vsync_in};
href_reg <= {href_reg[1:0], href_in};
data_reg <= data_in;
end
// Edge detection
wire vsync_rising = ~vsync_reg[2] & vsync_reg[1];
wire vsync_falling = ~vsync_reg[1] & vsync_reg[2];
wire href_falling = ~href_reg[1] & href_reg[2];
// Frame discard logic
reg [6:0] frame_counter;
reg processing_active;
always @(posedge clk) begin
if (~rst_n) begin
frame_counter <= 0;
processing_active <= 1'b0;
end else begin
if (vsync_rising && frame_counter < DISCARD_FRAMES) begin
frame_counter <= frame_counter + 1;
end
if (frame_counter >= DISCARD_FRAMES) begin
processing_active <= 1'b1;
end
end
end
// Pixel assembly (8-bit to 16-bit RGB565)
reg byte_select; // 0: High byte, 1: Low byte
reg [7:0] pixel_byte;
always @(posedge clk) begin
if (~rst_n) begin
byte_select <= 1'b0;
pixel_byte <= 8'b0;
rgb_out <= 16'b0;
end else begin
if (href_reg[1]) begin
byte_select <= ~byte_select;
pixel_byte <= data_reg;
// Latch assembled pixel on the second byte
if (byte_select) begin
rgb_out <= {pixel_byte, data_reg};
end
end else begin
byte_select <= 1'b0;
rgb_out <= 16'b0;
end
end
end
// Pixel counter for active line
reg [11:0] pixel_x_counter;
always @(posedge clk) begin
if (~rst_n || ~href_reg[2]) begin
pixel_x_counter <= 0;
end else if (byte_select) begin // Increment once per pixel
pixel_x_counter <= pixel_x_counter + 1;
end
end
// Output control signals
assign data_enable = processing_active & href_reg[2] & (pixel_x_counter < IMAGE_WIDTH);
assign frame_start = processing_active & href_reg[2] & (pixel_x_counter == 0);
assign line_end = processing_active & (pixel_x_counter == IMAGE_WIDTH - 1);
// Delayed output of control signals
always @(posedge clk) begin
vsync_out <= processing_active ? vsync_reg[2] : 1'b0;
href_out <= processing_active ? href_reg[2] : 1'b0;
end
endmodule
Cette architecture sépare clairement les fonctions de décodage et de synchronisation inter-horloges. Les signaux de contrôle (frame_start, line_end) encapsulés dans le flux permettent au traitement aval (comme des opérations de filtrage ou de détection de contours) de reconnaître la structure de l'image sans dépendre directement des signaux physiques de la caméra.