1 摄像头数据分别传入两个bram中
`timescale 1ns / 1ps
//摄像头数据分别传入两个bram中
module camera_to_2bram (
input reset_n,
input camera_clk,
input camera_frame_valid_i,
input [13:0] camera_data_i,
output reg [16:0] write_addr,
output reg [13:0] camera_data_to_bram,
output buffer0_write_en,
output buffer1_write_en
);
localparam PIXEL_SIZE = 320 * 256; //输入的每帧像素数量
reg buffer0_write_flag, buffer1_write_flag;
reg camera_frame_valid_i_d0;
reg [13:0] camera_data_i_d0;
//对帧有效信号打一拍
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
camera_frame_valid_i_d0 <= 0;
end else begin
camera_frame_valid_i_d0 <= camera_frame_valid_i;
end
end
//检测帧有效信号的上升沿
assign frame_rise_edge = camera_frame_valid_i && !camera_frame_valid_i_d0;
//每一帧开始换另一个bram写
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
buffer0_write_flag <= 0;
buffer1_write_flag <= 1;
end else begin
if (frame_rise_edge == 1) begin
buffer0_write_flag <= ~buffer0_write_flag;
buffer1_write_flag <= ~buffer1_write_flag;
end else begin
buffer0_write_flag <= buffer0_write_flag;
buffer1_write_flag <= buffer1_write_flag;
end
end
end
//对于每一个bram的写有效信号
assign buffer0_write_en = buffer0_write_flag && camera_frame_valid_i_d0;
assign buffer1_write_en = buffer1_write_flag && camera_frame_valid_i_d0;
//计算写入地址
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
write_addr <= 0;
end else begin
if (camera_frame_valid_i_d0 == 0) begin
write_addr <= 0;
end else if (write_addr == PIXEL_SIZE - 1) begin
write_addr <= 0;
end else begin
write_addr <= write_addr + 1;
end
end
end
//对写入数据打一拍
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
camera_data_to_bram <= 0;
end else begin
camera_data_to_bram <= camera_data_i;
end
end
endmodule
2 帧缓存代码
`timescale 1ns / 1ps
//帧缓存模块,可以对输入的连续摄像头数据进行一帧的缓存,缓存完再输出出去
module image_frame_buffer (
input reset_n,
input camera_clk,
input camera_frame_valid_i,
input [13:0] camera_data_i,
output camera_frame_valid_o,
output [13:0] camera_data_o
);
localparam PIXEL_SIZE = 320 * 256; //输入的每帧像素数量&每个bram深度
reg buffer0_write_flag, buffer1_write_flag;
wire buffer0_write_en, buffer1_write_en;
reg [16:0] write_addr;
reg camera_frame_valid_i_d0;
wire frame_rise_edge;
reg [13:0] camera_data_to_bram;
//对帧有效信号打一拍
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
camera_frame_valid_i_d0 <= 0;
end else begin
camera_frame_valid_i_d0 <= camera_frame_valid_i;
end
end
//检测帧有效信号的上升沿
assign frame_rise_edge = camera_frame_valid_i && !camera_frame_valid_i_d0;
//每一帧开始换另一个bram写
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
buffer0_write_flag <= 0;
buffer1_write_flag <= 1;
end else begin
if (frame_rise_edge == 1) begin
buffer0_write_flag <= ~buffer0_write_flag;
buffer1_write_flag <= ~buffer1_write_flag;
end else begin
buffer0_write_flag <= buffer0_write_flag;
buffer1_write_flag <= buffer1_write_flag;
end
end
end
//对于每一个bram的写有效信号
assign buffer0_write_en = buffer0_write_flag && camera_frame_valid_i_d0;
assign buffer1_write_en = buffer1_write_flag && camera_frame_valid_i_d0;
//计算写入地址
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
write_addr <= 0;
end else begin
if (camera_frame_valid_i_d0 == 0) begin
write_addr <= 0;
end else if (write_addr == PIXEL_SIZE - 1) begin
write_addr <= 0;
end else begin
write_addr <= write_addr + 1;
end
end
end
//写入bram的数据要打一拍,对齐写使能和写地址
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
camera_data_to_bram <= 0;
end else begin
camera_data_to_bram <= camera_data_i;
end
end
//伪双端口bram,a写b读,宽度14,深度320*256,输出数据延迟一拍
image_buffer_bram_0 bram_0 (
.clka(camera_clk), // input wire clka
.ena(1), // input wire ena
.wea(buffer0_write_en), // input wire [0 : 0] wea
.addra(write_addr), // input wire [16 : 0] addra
.dina(camera_data_to_bram), // input wire [13 : 0] dina
.clkb(camera_clk), // input wire clkb
.addrb(read_addr), // input wire [16 : 0] addrb
.doutb(buffer0_read_data) // output wire [13 : 0] doutb
);
image_buffer_bram_1 bram_1 (
.clka(camera_clk), // input wire clka
.ena(1), // input wire ena
.wea(buffer1_write_en), // input wire [0 : 0] wea
.addra(write_addr), // input wire [16 : 0] addra
.dina(camera_data_to_bram), // input wire [13 : 0] dina
.clkb(camera_clk), // input wire clkb
.addrb(read_addr), // input wire [16 : 0] addrb
.doutb(buffer1_read_data) // output wire [13 : 0] doutb
);
reg [16:0] read_addr;
reg buffer0_read_flag, buffer1_read_flag;
wire [13:0] buffer0_read_data;
wire [13:0] buffer1_read_data;
reg frame_valid_o;
//设置对每块bram的读标志,读标志和读地址对齐,比读出的数据和帧有效信号早一拍
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
buffer0_read_flag <= 0;
buffer1_read_flag <= 0;
end else begin
if (read_addr == PIXEL_SIZE) begin
buffer0_read_flag <= 0;
buffer1_read_flag <= 0;
end else if (write_addr == PIXEL_SIZE - 1 && buffer0_write_flag == 1) begin
buffer0_read_flag <= 1;
buffer1_read_flag <= 0;
end else if (write_addr == PIXEL_SIZE - 1 && buffer1_write_flag == 1) begin
buffer0_read_flag <= 0;
buffer1_read_flag <= 1;
end else begin
buffer0_read_flag <= buffer0_read_flag;
buffer1_read_flag <= buffer1_read_flag;
end
end
end
//计算读地址,因为bram读出数据要在地址延后一拍才出,所以要把读地址+1,让数据全部从bram出来再清零
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
read_addr <= 0;
end else begin
if (buffer0_read_flag == 0 && buffer1_read_flag == 0) begin
read_addr <= 0;
end else if (read_addr == PIXEL_SIZE) begin
read_addr <= 0;
end else begin
read_addr <= read_addr + 1;
end
end
end
//设置帧有效标志
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
frame_valid_o <= 0;
end else begin
if (buffer0_read_flag == 0 && buffer1_read_flag == 0) begin
frame_valid_o <= 0;
end else if (read_addr == PIXEL_SIZE) begin
frame_valid_o <= 0;
end else begin
frame_valid_o <= 1;
end
end
end
//输出帧有效标志
assign camera_frame_valid_o = frame_valid_o;
//设置两个bram的输出标志
wire bram0_out_flag, bram1_out_flag;
assign bram0_out_flag = (buffer0_read_flag == 1) && (frame_valid_o == 1);
assign bram1_out_flag = (buffer1_read_flag == 1) && (frame_valid_o == 1);
//输出读到的数据
assign camera_data_o = bram0_out_flag ? buffer0_read_data : (bram1_out_flag ? buffer1_read_data : 0);
endmodule
3 帧缓存模块,并统计最大值最小值代码
`timescale 1ns / 1ps
//帧缓存模块,可以对输入的连续摄像头数据进行一帧的缓存,并统计最大值最小值,缓存完再和最大值最小值一起输出出去
module image_frame_buffer (
input reset_n,
input camera_clk,
input camera_frame_valid_i,
input [13:0] camera_data_i,
output camera_frame_valid_o,
output [13:0] camera_data_o,
output [13:0] data_max,
output [13:0] data_min
);
localparam PIXEL_SIZE = 320 * 256; //输入的每帧像素数量&每个bram深度
reg buffer0_write_flag, buffer1_write_flag;
wire buffer0_write_en, buffer1_write_en;
reg [16:0] write_addr;
reg camera_frame_valid_i_d0;
wire frame_rise_edge;
reg [13:0] camera_data_to_bram;
//对帧有效信号打一拍
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
camera_frame_valid_i_d0 <= 0;
end else begin
camera_frame_valid_i_d0 <= camera_frame_valid_i;
end
end
//检测帧有效信号的上升沿
assign frame_rise_edge = camera_frame_valid_i && !camera_frame_valid_i_d0;
//每一帧开始换另一个bram写
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
buffer0_write_flag <= 0;
buffer1_write_flag <= 1;
end else begin
if (frame_rise_edge == 1) begin
buffer0_write_flag <= ~buffer0_write_flag;
buffer1_write_flag <= ~buffer1_write_flag;
end else begin
buffer0_write_flag <= buffer0_write_flag;
buffer1_write_flag <= buffer1_write_flag;
end
end
end
//对于每一个bram的写有效信号
assign buffer0_write_en = buffer0_write_flag && camera_frame_valid_i_d0;
assign buffer1_write_en = buffer1_write_flag && camera_frame_valid_i_d0;
//计算写入地址
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
write_addr <= 0;
end else begin
if (camera_frame_valid_i_d0 == 0) begin
write_addr <= 0;
end else if (write_addr == PIXEL_SIZE - 1) begin
write_addr <= 0;
end else begin
write_addr <= write_addr + 1;
end
end
end
//写入bram的数据要打一拍,对齐写使能和写地址
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
camera_data_to_bram <= 0;
end else begin
camera_data_to_bram <= camera_data_i;
end
end
//伪双端口bram,a写b读,宽度14,深度320*256,输出数据延迟一拍
image_buffer_bram_0 bram_0 (
.clka(camera_clk), // input wire clka
.ena(1), // input wire ena
.wea(buffer0_write_en), // input wire [0 : 0] wea
.addra(write_addr), // input wire [16 : 0] addra
.dina(camera_data_to_bram), // input wire [13 : 0] dina
.clkb(camera_clk), // input wire clkb
.addrb(read_addr), // input wire [16 : 0] addrb
.doutb(buffer0_read_data) // output wire [13 : 0] doutb
);
image_buffer_bram_1 bram_1 (
.clka(camera_clk), // input wire clka
.ena(1), // input wire ena
.wea(buffer1_write_en), // input wire [0 : 0] wea
.addra(write_addr), // input wire [16 : 0] addra
.dina(camera_data_to_bram), // input wire [13 : 0] dina
.clkb(camera_clk), // input wire clkb
.addrb(read_addr), // input wire [16 : 0] addrb
.doutb(buffer1_read_data) // output wire [13 : 0] doutb
);
reg [16:0] read_addr;
reg buffer0_read_flag, buffer1_read_flag;
wire [13:0] buffer0_read_data;
wire [13:0] buffer1_read_data;
reg frame_valid_o;
//设置对每块bram的读标志,读标志和读地址对齐,比读出的数据和帧有效信号早一拍
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
buffer0_read_flag <= 0;
buffer1_read_flag <= 0;
end else begin
if (read_addr == PIXEL_SIZE) begin
buffer0_read_flag <= 0;
buffer1_read_flag <= 0;
end else if (write_addr == PIXEL_SIZE - 1 && buffer0_write_flag == 1) begin
buffer0_read_flag <= 1;
buffer1_read_flag <= 0;
end else if (write_addr == PIXEL_SIZE - 1 && buffer1_write_flag == 1) begin
buffer0_read_flag <= 0;
buffer1_read_flag <= 1;
end else begin
buffer0_read_flag <= buffer0_read_flag;
buffer1_read_flag <= buffer1_read_flag;
end
end
end
//计算读地址,因为bram读出数据要在地址延后一拍才出,所以要把读地址+1,让数据全部从bram出来再清零
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
read_addr <= 0;
end else begin
if (buffer0_read_flag == 0 && buffer1_read_flag == 0) begin
read_addr <= 0;
end else if (read_addr == PIXEL_SIZE) begin
read_addr <= 0;
end else begin
read_addr <= read_addr + 1;
end
end
end
//设置帧有效标志
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
frame_valid_o <= 0;
end else begin
if (buffer0_read_flag == 0 && buffer1_read_flag == 0) begin
frame_valid_o <= 0;
end else if (read_addr == PIXEL_SIZE) begin
frame_valid_o <= 0;
end else begin
frame_valid_o <= 1;
end
end
end
//输出帧有效标志
assign camera_frame_valid_o = frame_valid_o;
//设置两个bram的输出标志
wire bram0_out_flag, bram1_out_flag;
assign bram0_out_flag = (buffer0_read_flag == 1) && (frame_valid_o == 1);
assign bram1_out_flag = (buffer1_read_flag == 1) && (frame_valid_o == 1);
//输出读到的数据
assign camera_data_o = bram0_out_flag ? buffer0_read_data : (bram1_out_flag ? buffer1_read_data : 0);
reg [13:0] bram0_max, bram0_min;
reg [13:0] bram1_max, bram1_min;
reg [13:0] bram0_buffer_max, bram0_buffer_min;
reg [13:0] bram1_buffer_max, bram1_buffer_min;
//计算此时输入的图像数据的最大值最小值
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
bram0_max <= 0;
bram0_min <= 16383;
end else begin
if (buffer0_write_en == 0) begin
bram0_max <= 0;
bram0_min <= 16383;
end else begin
bram0_max <= (camera_data_to_bram > bram0_max) ? camera_data_to_bram : bram0_max;
bram0_min <= (camera_data_to_bram < bram0_min) ? camera_data_to_bram : bram0_min;
end
end
end
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
bram1_max <= 0;
bram1_min <= 16383;
end else begin
if (buffer1_write_en == 0) begin
bram1_max <= 0;
bram1_min <= 16383;
end else begin
bram1_max <= (camera_data_to_bram > bram1_max) ? camera_data_to_bram : bram1_max;
bram1_min <= (camera_data_to_bram < bram1_min) ? camera_data_to_bram : bram1_min;
end
end
end
//缓存最大值最小值
always @(posedge camera_clk or negedge reset_n) begin
if (!reset_n) begin
bram0_buffer_max <= 0;
bram0_buffer_min <= 0;
bram1_buffer_max <= 0;
bram1_buffer_min <= 0;
end else begin
if (write_addr == PIXEL_SIZE - 1) begin
bram0_buffer_max <= bram0_max;
bram0_buffer_min <= bram0_min;
bram1_buffer_max <= bram1_max;
bram1_buffer_min <= bram1_min;
end else begin
bram0_buffer_max <= bram0_buffer_max;
bram0_buffer_min <= bram0_buffer_min;
bram1_buffer_max <= bram1_buffer_max;
bram1_buffer_min <= bram1_buffer_min;
end
end
end
//输出最大值和最小值
assign data_max = bram0_out_flag ? bram0_buffer_max : bram1_out_flag ? bram1_buffer_max : 16383;
assign data_min = bram0_out_flag ? bram0_buffer_min : bram1_out_flag ? bram1_buffer_min : 0;
endmodule
4 直方图拉伸代码
`timescale 1ns / 1ps
module image_histogram_stretch_14_to_8 (
input reset_n,
input camera_clk,
input camera_frame_valid_i,
input [13:0] camera_data_i,
output camera_frame_valid_o,
output [7:0] camera_data_o,
output [13:0] img_data,
output img_valid,
output [16:0] bram_read_addr,
output [13:0] data_max,
output [13:0] data_min,
output reg_img_valid
);
wire [21:0] data_sub_min_m255;
wire [13:0] data_sub_min;
wire [13:0] max_sub_min;
image_frame_buffer testuu1 (
.reset_n(reset_n),
.camera_clk(camera_clk),
.camera_frame_valid_i(camera_frame_valid_i),
.camera_data_i(camera_data_i),
.camera_frame_valid_o(img_valid),
.camera_data_o(img_data),
.data_max(data_max),
.data_min(data_min),
.bram_read_addr(bram_read_addr)
);
直方图拉伸
//g = 255*(f - min)/(max - min)
assign data_sub_min = img_data - data_min;
assign data_sub_min_m255 = {data_sub_min, 8'b0} - data_sub_min;
assign max_sub_min = data_max - data_min;
div_gen_0 div (
.aclk(camera_clk), // input wire aclk
.s_axis_divisor_tvalid(img_valid), // input wire s_axis_divisor_tvalid
.s_axis_divisor_tdata(max_sub_min), // input wire [15 : 0] s_axis_divisor_tdata
.s_axis_dividend_tvalid(img_valid), // input wire s_axis_dividend_tvalid
.s_axis_dividend_tdata(data_sub_min_m255), // input wire [23 : 0] s_axis_dividend_tdata
.m_axis_dout_tvalid(camera_frame_valid_o), // output wire m_axis_dout_tvalid
.m_axis_dout_tdata(axis_dout_tdata) // output wire [39 : 0] m_axis_dout_tdata
);
wire [39 : 0] axis_dout_tdata;
assign camera_data_o = camera_frame_valid_o ? ((axis_dout_tdata[39:16]> 255) ? 255 : axis_dout_tdata[23:16]) : 0;
reg [23:0] delay_valid; //延时24个时钟单位
always @(posedge camera_clk or reset_n) begin
if (!reset_n) begin
delay_valid <= 0;
end else begin
delay_valid <= {delay_valid[22:0], img_valid};
end
end
assign reg_img_valid = delay_valid[23];
endmodule