基于zynq的图像视频数据采集处理项目一
文章目录
1.整体的架构
2.整体的时钟和复位设计
时钟域分为:
1.图像预处理时钟:它由外部cmos芯片源同步传递过来,作用于传感器解析模块;byer转rgb模块;灰度域白均衡模块(74.25MHz)
2.axi 读写时钟:它由ps端给出的axi时钟200MHz
3.视频输出时钟:它由pll(clkin由ps端给出的100MHz)产生的148.5MHz(1920x1080 @60Hz)
4.给到serdes的管脚时钟:为视频输出时钟的5倍
复位分为:(都通过了异步复位同步释放)
1.图像预处理复位:它由ps通过gpio给出(与axi wr模块启动信号同源)
2.axi wr/rd复位:它由ps给出PS-PL configuration配置给出,通过复位ip核同步于ps端给出的axi时钟200MHz
3.视频输出复位:pll的locked信号
3.详细方案设计
3.1 ps端spi对摄像头的配置,数据的dvp口截断输入
fpga芯片配置imx222寄存器的接口采用spi接口协议,
spi详细解读以及配置请看我的博文
https://blog.youkuaiyun.com/weixin_45284871/article/details/141001874
主要是在vitis端用封装好的spi函数来对摄像头中的寄存器进行配置。这里 SPI 配置时序要求先发 LSB 最后发 MSB,与xilinx的zynq的PS端提供的驱动程序相反,我们再发送数据时需要进行高低位对调操作。
spi配置的是cmos模组12bit的模式,dvp并口数据(CMOS电平信号)的12根线接到FPC软排线然后再接到fpga,数据PIXEL_RGB只用12bit的高8位,PIXEL_RGB就只是绑定了高8位的管脚
3.2 看手册对 IMX2221080P 模式图像解析
图像传感器输出的是图像是那种嵌入式同步字机制(帧头或者帧尾一样的机制),要对输出的嵌入式同步字和有效行场信号进行解析。通过4个字节的无效行开始,4个字节的无效行结束,4个字节的有效行开始,4个字节的有效行结束,然后再根据相机的行场细节来解析出有效的图像1920*1080的模式。
用一个移位寄存器,高位先来,往左推箱子
always @(posedge clk) begin
shift_reg <= {shift_reg[23:0],bayer};
end
shift_reg来判断当前行是不是有效行
主要就是写状态机(因为事件发生有前后顺序),在不同的状态(无效行状态,有效行状态)处理图像数据。注意,为了后续的处理,需要多解析有效图像的的外围一圈像素,然后将有效图像的行场有效信号传递给bayer2rgb模块。
这里的输入的数据需要打一拍,以和解析出来的有效图像的行场有效信号,可以说是一级流水
3.3 拜耳图像格式转换为 RGB 图像
拜尔图像就是他一个点只有一个颜色通道分量的值,8bit,
主要就是用三个移位寄存器和两个fifo实现一个3X3的滑动矩形窗,矩形窗中心位置就是我们要差值出rgb三个通道的像素点,(怎么插值,需要根据像素的行列索引号来确定——奇行奇列是什么样的插值算法,偶行偶列是什么插值算法这样)
具体的过程描述
图像的第0行缓冲到fifo0,第1行缓冲到fifo1,第3行第一个有效像素来的时候,开始对fifo0和fifo1进行读出,fifo0的读出dout0送入24位的移位寄存器reg000102,fifo1的读出dout1送入24位的移位寄存器reg101112,pixel的直接送入24位的移位寄存器reg202122,与此同时pixel(第3行第一个有效像素来的时候)作为fifo1的输入,fifo1的输出dout1作为fifo0的输入,fifo0的输出dout0就扔掉不要了,直到整个一帧有效图像结束。
(这里的fifo深度就是能缓存一行图像就行)
fifo0的读写使能,和数据代码
//r_fifo_en
//第三行数据来临开始读出
always @(*) begin
if(frame_valid == 1'b1 && vcnt != 'd0 && vcnt != 'd1 && hsync)begin
r_fifo_en = 1'b1;
end
else begin
r_fifo_en =0;
end
end
//w_fifo_0_data
//先是缓存一帧图像的首行,后面缓存fifo1的输出dout1
always @(*)begin
if(frame_valid == 1'b1 && vcnt == 'd0) begin
w_fifo_0_data <= pixel;
end
else begin
w_fifo_0_data <= dout1;
end
end
//w_fifo_0_enfifo0在一帧数据pixel的0行和除了最后1行之外的时候保持写入
always @(*) begin
if(frame_valid == 1'b1 && vcnt != 'd1 && vcnt != VCNTMAX && hsync)begin
w_fifo_0_en = 1'b1;
end
else begin
w_fifo_0_en = 0;
end
end
//FIFO深度能缓存1行数据1920即可,这里是2048
sfifo_2048x8 sfifo_2048x8_0_inst (//
.clk(clk), // input wire clk
.din(w_fifo_0_data), // input wire [7 : 0] din
.wr_en(w_fifo_0_en), // input wire wr_en
.rd_en(r_fifo_en), // input wire rd_en
.dout(dout0), // output wire [7 : 0] dout
.full(), // output wire full
.empty() // output wire empty
);
fifo1的读写使能,和数据代码
//w_fifo_1_data 一直让它等于输入进来的pixel即可
always @(*)begin
w_fifo_1_data <= pixel;
end
//w_fifo_1_en fifo1从一帧数据pixel的第1行和除了最后1行之外的时候保持写入
always @(*) begin
if(frame_valid == 1'b1 && vcnt != 'd0 && vcnt != VCNTMAX && hsync)begin
w_fifo_1_en = 1'b1;
end
else begin
w_fifo_1_en =0;
end
end
//rd_en和fifo0的一样
//
sfifo_2048x8 sfifo_2048x8_1_inst (
.clk(clk), // input wire clk
.din(w_fifo_1_data), // input wire [7 : 0] din
.wr_en(w_fifo_1_en), // input wire wr_en
.rd_en(r_fifo_en), // input wire rd_en
.dout(dout1), // output wire [7 : 0] dout
.full(), // output wire full
.empty() // output wire empty
);
三个移位寄存器形成矩形窗
always @(posedge clk ) begin
if (r_fifo_en == 1'b1) begin
reg000102 <= {reg000102[15:0],dout0};
reg101112 <= {reg101112[15:0],dout1};
reg202122 <= {reg202122[15:0],pixel};
end
end
具体的设计时序如图:
3.4 灰度域的白平衡算法 white Balance 的 RTL 实现
白平衡算法的实现:是后面加的,因为出来的图像有点偏绿,色温有问题。所以采用灰度域的白平衡算法,一般得现在c语言里面做定点化验证效果(比如写一张图像到sd卡导出来做算法的定点化验证),然后再做的RTL的设计
RTL中的设计为:计算上一帧图像的所有像素的 R, B, G通道的平均值 Ravg, Bavg, Gavg。以及上一帧图像的 R/G/B 平均值的平均值又称作灰度平均值 Kavg。在得到上一帧图像的上述四个参数后,根据这四个参数,用这4个值算出增益,来对当下的这一帧图像进行白平衡处理(所以第一帧是处理不了的)。增益计算如下:
如果红色分量的平均值大于灰度平均值的话就会使 red 分量变少,反之增大一点,当计算结果大于 255(溢出)时,固定输出为 255,由此得到白平衡处理后的图像输出数据。过程中涉及到的除法器,如果误差不太大的话+除数为固定值,就可以直接截位。这样可以节约资源。
Ravg颜色通道是将进来的8位i_data_r分量全部相加,用ri_data_r_sum来寄存,
ri_data_r_sum的位宽既是19201080255,即29位即可,在一帧信号标志下降沿的时候清零上一帧的sum和并计算上一帧的颜色通道平均值(为了计算本帧的sum和),在有效行信号来的时候开始计算sum
代码如下:
// ri_data_r_sum
always @(posedge i_clk or posedge i_rst) begin
if (i_rst) begin
ri_data_r_sum <= 'd0;
end
else if(i_fv_flag_fall) begin//在一帧信号标志下降沿的时候清零上一帧的sum
ri_data_r_sum <= 'd0;
end
else if(i_hv_flag) begin//在有效行信号来的时候开始计算sum
ri_data_r_sum <= i_data_r + ri_data_r_sum;
end
else
ri_data_r_sum <= ri_data_r_sum;
end
// data_ravg
always @(posedge i_clk or posedge i_rst) begin
if (i_rst) begin
data_ravg <= 'd0;
end
else if(i_fv_flag_fall) begin//在一帧信号标志下降沿的时候计算上一帧的颜色通道平均值
data_ravg <= ri_data_r_sum[29:21];//这里直接截位计算,相当于除以1920*1080
end
else
data_ravg <= data_ravg;
end
其他的乘除就调用ip核,调用ip核的时候都选择了只使用lut和ff资源的模式,所以最后我们可以看到项目的dsp资源使用为0
最后数据有效信号传递给下一模块的即复用除法器除出来的valid信号就行