AXI_Stream接口注意事项
这里需要注意到:
1**、在EOL(AXIS_TLAST)被(拉高)后,VALID是需要在下一个时刻置低的。
2、无论是EOL还是SOF,拉高时数据通道依然是有效的,如果需要对行计数时,需要对LAST信号进行延时,等待延时信号后对行清零。**## AXI_Stream接口编写
#s_axis_tvaild控制
reg [5:0] rst_cnt;
//赋初值再加一个初始延时
always @(posedge clk or negedge rst_n) begin
if (~rst_n)
rst_cnt <= 6'd0;
else if (rst_cnt == 6'd30)
rst_cnt <= rst_cnt;
else
rst_cnt <= rst_cnt + 1'b1;
end
//s_axis_tvalid信号在一行的结束拉低
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
s_axis_tvalid <= 1'b0;
else if(s_axis_tlast)
s_axis_tvalid <= 1'b0;
else if(rst_cnt == 6'd30)
s_axis_tvalid <= 1'b1;
else
s_axis_tvalid <= s_axis_tvalid;
end
s_axis_tlast在一行的最后一个数据拉高
reg [10:0] w_cnt;
always @(posedge clk or negedge rst_n) begin
if(~rst_n)begin
w_cnt <= 11'b0;
end
else if(s_axis_tvalid && s_axis_tready && w_cnt < iBmpWidth - 1 ) begin
w_cnt <= w_cnt + 1'b1;
end
else if(w_cnt == iBmpWidth-1) begin
w_cnt <= 11'b0;
end
else begin
w_cnt <= w_cnt;
end
end
assign s_axis_tlast = (w_cnt == iBmpWidth - 1) ? 1'b1: 1'b0 ;
s_axis_tuser在第一行的第一列数据拉高
reg [10:0] h_cnt;
always @(posedge clk or negedge rst_n) begin
if(~rst_n)begin
h_cnt <= 1'b0;
end
else if(s_axis_tlast) begin
h_cnt <= h_cnt + 1'b1;
end
else if(h_cnt == iBmpHight - 1'd1) begin
h_cnt <= 1'b0;
end
else begin
h_cnt <= h_cnt;
end
end
assign s_axis_tuser = (h_cnt == 0 && w_cnt == 0) ? 1'b1: 1'b0 ;
从数组中以视频格式输出像素数据
reg [23:0] cmos_data;
reg [31:0] cmos_index;
always@(posedge clk or negedge rst_n)begin
if(!rst_n) begin
cmos_index <= 0;
cmos_data <= 24'd0;
end
else begin
//cmos_index <= y_pos * 960 + x_pos*3 + 54; // 3*(y*320 + x) + 54
cmos_index <= h_cnt * 1920 + w_cnt * 3 + 54; // 3*(y*640 + x) + 54
cmos_data <= {rBmpData[cmos_index], rBmpData[cmos_index+1] , rBmpData[cmos_index+2]};
end
end
wire [7:0] img_red = cmos_data[ 7: 0];
wire [7:0] img_green = cmos_data[15: 8];
wire [7:0] img_blue = cmos_data[23:16];
assign s_axis_tdata = (s_axis_tvalid && s_axis_tready) ? {img_red, img_green, img_blue} : 24'd0;
以灰度转换为例
//--------------------
//VIP算法——彩色转灰度
wire per_frame_tvalid ;
wire per_frame_tready ;
wire per_frame_tuser ;
wire per_frame_tlast ;
wire [23:0] per_frame_tdata ;
wire pose0_frame_tvalid ;
wire pose0_frame_tready ;
wire pose0_frame_tuser ;
wire pose0_frame_tlast ;
wire [23:0] pose0_frame_tdata ;
wire [7:0] post0_img_Y ;
wire [7:0] post0_img_Cb ;
wire [7:0] post0_img_Cr ;
assign per_frame_tvalid = s_axis_tvalid ;
assign per_frame_tuser = s_axis_tuser ;
assign per_frame_tlast = s_axis_tlast ;
assign per_frame_tdata = s_axis_tdata ;
//s_axis_tready信号由主机输出
assign s_axis_tready = per_frame_tready;
assign pose0_frame_tvalid = m_axis_tvalid ;
assign pose0_frame_tuser = m_axis_tuser ;
assign pose0_frame_tlast = m_axis_tlast ;
assign post0_img_Y = pose0_frame_tdata[23:16];
assign post0_img_Cb = pose0_frame_tdata[15:8];
assign post0_img_Cr = pose0_frame_tdata[7:0];
rgb_ycbcr u_rgb_ycbcr(
//AXI4_Stream Slave接口0,来自摄像头,获取当前帧数据
.clk (clk ),
.rst_n (rst_n ),
.s0_axis_tdata (per_frame_tdata ),
.s0_axis_tready (per_frame_tready ), //输出
.s0_axis_tvalid (per_frame_tvalid ),
.s0_axis_tuser (per_frame_tuser ), //tuser == start of frame(SOF)
.s0_axis_tlast (per_frame_tlast ), //tlast == end of line(EOL)
//AXIS4_Stream Master接口,输出到VDMA
// input m_axis_aclk ,
// input m_axis_aresetn ,
.m_axis_tdata (pose0_frame_tdata ),
.m_axis_tvalid (pose0_frame_tvalid ),
.m_axis_tready (pose0_frame_tready ),
.m_axis_tuser (pose0_frame_tuser ),
.m_axis_tlast (pose0_frame_tlast )
);
## 生成BMP图片
```c
//寄存图像处理之后的像素数据
//-------------------------------------
//第一张图
reg [31:0] PIC1_vip_cnt;
reg PIC1_vip_out_en; //寄存VIP处理图像的使能信号,仅维持一帧的时间
reg m_axis_tuser_r0; //采集上升沿
reg m_axis_tuser_r1;
wire m_axis_tuser_p;
reg PIC1_vip_vsync_r; //寄存VIP输出的场同步
always@(posedge clk or negedge rst_n)begin
if(!rst_n) begin
m_axis_tuser_r0 <= 1'd0;
m_axis_tuser_r1 <= 1'd0;
end
else begin
m_axis_tuser_r0 <= PIC1_vip_out_tuser ;
m_axis_tuser_r1 <= m_axis_tuser_r0 ;
end
end
assign m_axis_tuser_p = (~m_axis_tuser_r1) && m_axis_tuser_r0;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
PIC1_vip_vsync_r <= 1'b0;
else if (m_axis_tuser_p)
PIC1_vip_vsync_r <= ~PIC1_vip_vsync_r;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
PIC1_vip_out_en <= 1'b1;
else if(PIC1_vip_vsync_r & m_axis_tuser_p) //第一帧结束之后,使能拉低
PIC1_vip_out_en <= 1'b0;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n) begin
PIC1_vip_cnt <= 32'd0;
end
else if (PIC1_vip_out_en) begin
if(PIC1_vip_out_tvalid & PIC1_vip_out_tready) begin
PIC1_vip_cnt <= PIC1_vip_cnt + 3;
vip_pixel_data_1[PIC1_vip_cnt+0] <= PIC1_vip_out_img_R;
vip_pixel_data_1[PIC1_vip_cnt+1] <= PIC1_vip_out_img_G;
vip_pixel_data_1[PIC1_vip_cnt+2] <= PIC1_vip_out_img_B;
end
end
end
//输出第一张
for (iIndex = 0; iIndex < iBmpSize; iIndex = iIndex + 1) begin
if(iIndex < 54)
Vip_BmpData_1[iIndex] = rBmpData[iIndex];
else
Vip_BmpData_1[iIndex] = vip_pixel_data_1[iIndex-54];
end
//将数组中的数据写到输出BMP图片中
for (iIndex = 0; iIndex < iBmpSize; iIndex = iIndex + 4) begin
rBmpWord = {Vip_BmpData_1[iIndex+3],Vip_BmpData_1[iIndex+2],Vip_BmpData_1[iIndex+1],Vip_BmpData_1[iIndex]};
$fwrite(oBmpFileId_1,"%u",rBmpWord);
end
效果展示
参考文献
https://hwbyyds.blog.csdn.net/article/details/124319453?spm=1001.2014.3001.5502
b站大磊FPGA图像处理,基于Modelsim的图像仿真处理平台搭建