实现思路在实现上,由于bmp除去文件头后也只是把图像流数据按顺序放而已,所以这里
先用一个fifo缓存图像数据写一个状态机控制按顺序输出文件头和数据。注意fifo的读写和axis之间的握手和控制逻辑。因为看起来fifo是暂存数据的,但可预见fifo应该是有可能周期性空的,因为在每行的结束后tlast都是让valid拉低一个周期,这一小个周期在行多了之后一点会抵消文件头的大小。生成缓存fifo声明:
命名并不规范可以用原语(xpm)或其他同类型ip生成,这里不多赘述。在ip catalog的搜索框中写fifo,选fifo generator:
具体按下面设置:
这里注意必须选择first word fall through
选25位是因为,在数据结构上是1tuser + 2*8 data,选择把帧开始标志也丢进fifo可以避免错帧。
总体端口生成的bmp文件依然以axis格式输出,在tb中再以二进制格式写进文件:
module axis2bmp#( parameter pic_height = 1080, parameter pic_width = 1920)( // global signal input clk_i, // clock input rst_n_i, // reset // axi stream (slave) interface signal - > pixel data input [23:0] s_axis_video_tdata, // data input [0:0] s_axis_video_tvalid, // valid output [0:0] s_axis_video_tready, // ready input [0:0] s_axis_video_tuser, // sof input [0:0] s_axis_video_tlast, // eol // axi stream (master) interface signal - > bmp output reg [23:0] m_axis_video_tdata, // data output [ 0:0] m_axis_video_tvalid, // valid input [ 0:0] m_axis_video_tready, // (meaningless) output [ 0:0] m_axis_video_tlast // end of file stream);slave端为图像数据,master端为输出bmp文件流,这里需要注意master流中并不处理反压问题(即没有ready信号,懒得加fifo)
fifo接口逻辑// image pixel fifo dw=24, bram cap=512wire [24:0] bmp_header_din;wire [0:0] bmp_header_wr;wire [0:0] bmp_header_full;wire [0:0] bmp_header_empty;wire [0:0] bmp_header_rd;wire [24:0] bmp_header_dout;bmp_header bmp_header_inst ( .clk(clk_i), // input wire clk .srst(~rst_n_i), // input wire srst .din(bmp_header_din), // input wire [23 : 0] din .wr_en(bmp_header_wr), // input wire wr_en .rd_en(bmp_header_rd), // input wire rd_en .dout(bmp_header_dout), // output wire [23 : 0] dout .full(bmp_header_full), // output wire full .empty(bmp_header_empty) // output wire empty);// pixel fifo assignmentassign bmp_header_din = {s_axis_video_tuser,s_axis_video_tdata};assign s_axis_video_tready = ~bmp_header_full;assign bmp_header_wr = s_axis_video_tready && s_axis_video_tvalid;fifo的读使能放到后面再讲,这里先处理好数据进来就可以了
文件流处理状态机经典三板斧,不展开
包头数据准备需要搬回第一篇中的bmp文件格式,由于是输出,所以我们就不考虑调色板了:
这里先用一些localparam存起来,(这里考虑大小不变)
//--------------------------写bmp状态机------------------------// local parameterlocalparam [15:0] bftype = 16'h4d42;localparam [31:0] bfreserved = 32'h0000_0000;localparam [31:0] bisizeimage = pic_height * pic_width * 3;localparam [31:0] bisizeimage_cnt = pic_height * pic_width;localparam [31:0] bfoffset = 32'd54;localparam [31:0] bfsize = bisizeimage + bfoffset;localparam [31:0] bisize = 32'h28;localparam [31:0] biwidth = pic_width;localparam [31:0] biheight = pic_height;localparam [15:0] biplanes = 16'd1;localparam [15:0] bibitcount = 16'd24;localparam [31:0] bicompression = 32'd0;localparam [127:0] biuseless = 128'd0;localparam cnt_pixel = $clog2(pic_height*pic_width);转移状态//转移状态localparam s_wait = 3'b001 ; // 等待sof标记localparam s_write_header = 3'b010 ; // 写bmp包头localparam s_write_data = 3'b100 ; // 写bmp数据状态转移变量//状态转移变量reg [2:0] state, n_state; // 状态寄存器reg [4:0] header_cnt; // 包头计数器reg [cnt_pixel-1:0] pixel_cnt; // 像素计数器wire frame_start = bmp_header_dout[24]; // sof flagwire write_header_done = (header_cnt == 5'd17); // 18 -1 - > 18*3wire write_pixel_done = (pixel_cnt == bisizeimage_cnt -1'b1);这里需要注意 : 两个状态只由计数器指定跳转
状态转移//状态机初始化always @ (posedge clk_i) begin if(~rst_n_i) state <= s_wait; else state <= n_state;end状态机 状态转移always @ (*) begin case(state) s_wait : if(frame_start) n_state = s_write_header; else n_state = s_wait; s_write_header: if(write_header_done) n_state = s_write_data; else n_state = s_write_header; s_write_data: if(write_pixel_done) n_state = s_wait; else n_state = s_write_data; default: n_state = s_wait; endcaseend写bmp包头 处理逻辑这里直接按照文件格式,用计数器怼进去进行:
always @(posedge clk_i or negedge rst_n_i) begin if (~rst_n_i) header_cnt <= 5'd0; else if(state == s_write_header && header_cnt < 5'd17) header_cnt <= header_cnt + 1'd1; else header_cnt <= 5'd0;end在数据上,可参考(注意数据以小端输出):
case (header_cnt) 5'd0 : m_axis_video_tdata = {bfsize[0+:8], bftype}; 5'd1 : m_axis_video_tdata = bfsize[8+:24]; 5'd2 : m_axis_video_tdata = bfreserved[0 +:24]; 5'd3 : m_axis_video_tdata = {bfoffset[0+:16],bfreserved[24+:8]}; 5'd4 : m_axis_video_tdata = {bisize[0+:8], bfoffset[16+:16]}; 5'd5 : m_axis_video_tdata = bisize[8+:24]; 5'd6 : m_axis_video_tdata = biwidth[0+:24]; 5'd7 : m_axis_video_tdata = {biheight[0+:16],biwidth[24+:8]}; 5'd8 : m_axis_video_tdata = {biplanes[0+:8],biheight[16+:16]}; 5'd9 : m_axis_video_tdata = {bibitcount[0+:16],biplanes[8+:8]}; 5'd10 : m_axis_video_tdata = bicompression[0+:24]; 5'd11 : m_axis_video_tdata = {bisizeimage[0+:16],bicompression[24+:8]}; 5'd12 : m_axis_video_tdata = {biuseless[0+:8], bisizeimage[16+:16]}; 5'd13 : m_axis_video_tdata = biuseless[8+:24]; 5'd14 : m_axis_video_tdata = biuseless[32+:24]; 5'd15 : m_axis_video_tdata = biuseless[56+:24]; 5'd16 : m_axis_video_tdata = biuseless[80+:24]; 5'd17 : m_axis_video_tdata = biuseless[104+:24]; default: m_axis_video_tdata = 24'heeeeee;endcase其中+:和-:语法简介可以翻看笔者之前的文章或自行百度。这里这样写是为了看位宽方便一点
写图像数据这里直接放开fifo数据就可以了,注意握手逻辑:
计数器逻辑:
always @(posedge clk_i or negedge rst_n_i) begin if (~rst_n_i) pixel_cnt <= 'd0; else if(state == s_write_data && pixel_cnt < bisizeimage_cnt-1) begin if(bmp_header_empty) pixel_cnt <= pixel_cnt; else pixel_cnt <= pixel_cnt + 1'd1; end else pixel_cnt behav -> xsim下面放好图像就可以了。
AR/VR/MR/XR有何区别
所有单个传感器的基础知识
机器感知真正的彩色世界
北汽新能源的达尔文系统,大势所趋
一种6腿机器人可在没有GPS帮助的情形下找到回家的路
图像流AXI-Stream生成BMP文件的实现思路
GigasetMEpro评测 不论外形硬件和内在软件都是及为出色
电子变压器的EMI/EMC问题有哪些?有哪些解决办法呢?
光模块都是如何分类的?还有哪些类型?
汇总!三维点云去噪算法,涉及深度学习等
乐视在美国还有多少机会?
STC12C5A60S2串口演示程序(C语言版)
集中抄表系统的构造与组建方案的探讨
数字电路基础知识之加法器、减法器
声控鸟电路
基于方向引导优化的主动视觉导航参量计算方法
等离子体蚀刻和沉积问题的解决方案
工程师/科学家喜欢使用MATLAB开发产品的7大理由
英飞凌与汇川技术全面拓展合作,全球首个太空机器人将入太空
AI测温带火AI安防,市场“蓝海”无限大