??準備用Tang Prime 20K開發板進行OV2640攝像頭采集驗證。
??Tang Primer 20K是由開源硬件廠商SiPEED矽速科技推出,是一款以 GW2A-LV18PG256C8/I7 為主芯片的核心板,準備了 2 個擴展板,Dock 和 Lite。板卡包含有HDMI輸出,DVP接口(外接OV2640/OV5640攝像頭),5個按鍵,6個LED燈。另外JTAG下載器已集成在板上,只需一根USB Type-C的線就可以實現下載調試,板卡供電也是依靠這條Type-C的線。板卡相關資料可參考官方網址:https://wiki.sipeed.com/hardware/en/tang/tang-primer-20k/primer-20k.html
1. 運行camera_hdmi例程
(1)程序主要結構框圖
(2)github上的例程是OV5640攝像頭,這里修改成OV2640攝像頭。主要是I2c寄存器設置發生改變,OV2640的寄存器地址是8bit,按RGB565方式采集。
module sccb_Registers #
(parameter HACT = 12'd640 ,parameter VACT = 12'd480 ,parameter HTOL = 13'd1632,parameter VTOL = 13'd1232
)
(input clk, input rst, input advance, output [23:0] command, output finished
);// Internal signals
reg [23:0] sreg;
reg finished_temp;
reg [9:0] address = 10'd0;// Assign values to outputs
assign command = sreg;
assign finished = finished_temp;// When register and value is FFFF
// a flag is asserted indicating the configuration is finished
always @ (sreg) beginif(sreg == 24'hFFFFFF) beginfinished_temp <= 1'b1;endelse beginfinished_temp <= 1'b0;end
end// Get value out of the LUT
always @ (posedge clk) beginif(rst == 1'b1) begin // reset the configurationaddress <= 10'd0;endelse if(advance == 1'b1) begin // Get the next valueaddress <= address+1'b1;endcase (address) 000 : sreg <= 24'h00FF_01; //選擇 sensor相關寄存器組001 : sreg <= 24'h0012_80;002 : sreg <= 24'h00FF_00; //選擇 DSP相關寄存器組003 : sreg <= 24'h002C_FF; // 0x2c 保留004 : sreg <= 24'h002E_DF; // VSYNC脈沖寬MSB 8位 默認每個VSYNC 的脈沖為 4 x T_line ,MSB 每增加 一個計數 VSYNC 活動周期增加 256 x T_line005 : sreg <= 24'h00FF_01; //選擇 sensor相關寄存器組006 : sreg <= 24'h003C_32; // 0x3c 保留007 : sreg <= 24'h0011_80;/* Set PCLK divider */008 : sreg <= 24'h0009_02;/* Output drive x2 */009 : sreg <= 24'h0004_28;010 : sreg <= 24'h0013_E5; // 曝光的相干設置 條狀過濾器選擇,手動/自動 增益控制 , 曝光控制011 : sreg <= 24'h0014_48; // 設置 AGC 增益上限012 : sreg <= 24'h0015_00;//Invert VSYNC013 : sreg <= 24'h002c_0c;014 : sreg <= 24'h0033_78;015 : sreg <= 24'h003a_33;016 : sreg <= 24'h003b_fb;017 : sreg <= 24'h003e_00;018 : sreg <= 24'h0043_11;019 : sreg <= 24'h0016_10;020 : sreg <= 24'h0039_02;021 : sreg <= 24'h0035_88;022 : sreg <= 24'h0022_0a;023 : sreg <= 24'h0037_40;024 : sreg <= 24'h0023_00;025 : sreg <= 24'h0034_a0;026 : sreg <= 24'h0006_02;027 : sreg <= 24'h0006_88;028 : sreg <= 24'h0007_c0;029 : sreg <= 24'h000d_b7;030 : sreg <= 24'h000e_01;031 : sreg <= 24'h004c_00;032 : sreg <= 24'h004a_81;033 : sreg <= 24'h0021_99;034 : sreg <= 24'h0024_40;035 : sreg <= 24'h0025_38;036 : sreg <= 24'h0026_82;/* AGC/AEC fast mode operating region */ 037 : sreg <= 24'h0048_00;/* Zoom control 2 MSBs */038 : sreg <= 24'h0049_00;/* Zoom control 8 MSBs */039 : sreg <= 24'h005c_00;040 : sreg <= 24'h0063_00;041 : sreg <= 24'h0046_00;042 : sreg <= 24'h0047_00;043 : sreg <= 24'h000C_3A;/* Set banding filter */044 : sreg <= 24'h005D_55;045 : sreg <= 24'h005E_7d;046 : sreg <= 24'h005F_7d;047 : sreg <= 24'h0060_55;048 : sreg <= 24'h0061_70;049 : sreg <= 24'h0062_80;050 : sreg <= 24'h007c_05;051 : sreg <= 24'h0020_80;052 : sreg <= 24'h0028_30;053 : sreg <= 24'h006c_00;054 : sreg <= 24'h006d_80;055 : sreg <= 24'h006e_00;056 : sreg <= 24'h0070_02;057 : sreg <= 24'h0071_94;058 : sreg <= 24'h0073_c1;059 : sreg <= 24'h003d_34;060 : sreg <= 24'h005a_57;061 : sreg <= 24'h004F_bb;062 : sreg <= 24'h0050_9c;063 : sreg <= 24'h00FF_00;064 : sreg <= 24'h00e5_7f;065 : sreg <= 24'h00F9_C0;066 : sreg <= 24'h0041_24;067 : sreg <= 24'h00E0_14;068 : sreg <= 24'h0076_ff;069 : sreg <= 24'h0033_a0;070 : sreg <= 24'h0042_20;071 : sreg <= 24'h0043_18;072 : sreg <= 24'h004c_00;073 : sreg <= 24'h0087_D0;074 : sreg <= 24'h0088_3f;075 : sreg <= 24'h00d7_03;076 : sreg <= 24'h00d9_10;077 : sreg <= 24'h00D3_82;078 : sreg <= 24'h00c8_08;079 : sreg <= 24'h00c9_80;080 : sreg <= 24'h007C_00;081 : sreg <= 24'h007D_00;082 : sreg <= 24'h007C_03;083 : sreg <= 24'h007D_48;084 : sreg <= 24'h007D_48;085 : sreg <= 24'h007C_08;086 : sreg <= 24'h007D_20;087 : sreg <= 24'h007D_10;088 : sreg <= 24'h007D_0e;089 : sreg <= 24'h0090_00;090 : sreg <= 24'h0091_0e;091 : sreg <= 24'h0091_1a;092 : sreg <= 24'h0091_31;093 : sreg <= 24'h0091_5a;094 : sreg <= 24'h0091_69;095 : sreg <= 24'h0091_75;096 : sreg <= 24'h0091_7e;097 : sreg <= 24'h0091_88;098 : sreg <= 24'h0091_8f;099 : sreg <= 24'h0091_96;100 : sreg <= 24'h0091_a3;101 : sreg <= 24'h0091_af;102 : sreg <= 24'h0091_c4;103 : sreg <= 24'h0091_d7;104 : sreg <= 24'h0091_e8;105 : sreg <= 24'h0091_20;106 : sreg <= 24'h0092_00;107 : sreg <= 24'h0093_06;108 : sreg <= 24'h0093_e3;109 : sreg <= 24'h0093_03;110 : sreg <= 24'h0093_03;111 : sreg <= 24'h0093_00;112 : sreg <= 24'h0093_02;113 : sreg <= 24'h0093_00;114 : sreg <= 24'h0093_00;115 : sreg <= 24'h0093_00;116 : sreg <= 24'h0093_00;117 : sreg <= 24'h0093_00;118 : sreg <= 24'h0093_00;119 : sreg <= 24'h0093_00;120 : sreg <= 24'h0096_00;121 : sreg <= 24'h0097_08;122 : sreg <= 24'h0097_19;123 : sreg <= 24'h0097_02;124 : sreg <= 24'h0097_0c;125 : sreg <= 24'h0097_24;126 : sreg <= 24'h0097_30;127 : sreg <= 24'h0097_28;128 : sreg <= 24'h0097_26;129 : sreg <= 24'h0097_02;130 : sreg <= 24'h0097_98;131 : sreg <= 24'h0097_80;132 : sreg <= 24'h0097_00;133 : sreg <= 24'h0097_00;134 : sreg <= 24'h00a4_00;135 : sreg <= 24'h00a8_00;136 : sreg <= 24'h00c5_11;137 : sreg <= 24'h00c6_51;138 : sreg <= 24'h00bf_80;139 : sreg <= 24'h00c7_10;140 : sreg <= 24'h00b6_66;141 : sreg <= 24'h00b8_A5;142 : sreg <= 24'h00b7_64;143 : sreg <= 24'h00b9_7C;144 : sreg <= 24'h00b3_af;145 : sreg <= 24'h00b4_97;146 : sreg <= 24'h00b5_FF;147 : sreg <= 24'h00b0_C5;148 : sreg <= 24'h00b1_94;149 : sreg <= 24'h00b2_0f;150 : sreg <= 24'h00c4_5c;151 : sreg <= 24'h00a6_00;152 : sreg <= 24'h00a7_20;153 : sreg <= 24'h00a7_d8;154 : sreg <= 24'h00a7_1b;155 : sreg <= 24'h00a7_31;156 : sreg <= 24'h00a7_00;157 : sreg <= 24'h00a7_18;158 : sreg <= 24'h00a7_20;159 : sreg <= 24'h00a7_d8;160 : sreg <= 24'h00a7_19;161 : sreg <= 24'h00a7_31;162 : sreg <= 24'h00a7_00;163 : sreg <= 24'h00a7_18;164 : sreg <= 24'h00a7_20;165 : sreg <= 24'h00a7_d8;166 : sreg <= 24'h00a7_19;167 : sreg <= 24'h00a7_31;168 : sreg <= 24'h00a7_00;169 : sreg <= 24'h00a7_18;170 : sreg <= 24'h007f_00;171 : sreg <= 24'h00e5_1f;172 : sreg <= 24'h00e1_77;173 : sreg <= 24'h00dd_7f;174 : sreg <= 24'h00C2_0E;175 : sreg <= 24'h00FF_01; //選擇 sensor相關寄存器組176 : sreg <= 24'h00FF_00; //選擇 DSP相關寄存器組177 : sreg <= 24'h00E0_04;178 : sreg <= 24'h00DA_08;//08:RGB565 04:RAW10179 : sreg <= 24'h00D7_03;180 : sreg <= 24'h00E1_77;181 : sreg <= 24'h00E0_00;182 : sreg <= 24'h00FF_00; //選擇 DSP相關寄存器組183 : sreg <= 24'h0005_01;184 : sreg <= {16'h005A,HACT[9:2]};//(w>>2)&0xFF //28:w=160 //A0:w=640 //C8:w=800185 : sreg <= {16'h005B,VACT[9:2]};//(h>>2)&0xFF //1E:h=120 //78:h=480 //96:h=600186 : sreg <= 24'h005C_00;//((h>>8)&0x04)|((w>>10)&0x03) 187 : sreg <= 24'h00FF_01; //選擇 sensor相關寄存器組188 : sreg <= 24'h0011_80;//clkrc=0x83 for resolution <= SVGA 189 : sreg <= 24'h00FF_01;190 : sreg <= 24'h0012_40;/* DSP input image resoultion and window size control */191 : sreg <= 24'h0003_0A;/* UXGA=0x0F, SVGA=0x0A, CIF=0x06 */192 : sreg <= 24'h0032_09;/* UXGA=0x36, SVGA/CIF=0x09 */193 : sreg <= 24'h0017_11;/* UXGA=0x11, SVGA/CIF=0x11 */194 : sreg <= 24'h0018_43;/* UXGA=0x75, SVGA/CIF=0x43 */195 : sreg <= 24'h0019_00;/* UXGA=0x01, SVGA/CIF=0x00 */196 : sreg <= 24'h001A_4b;/* UXGA=0x97, SVGA/CIF=0x4b */197 : sreg <= 24'h003d_38;/* UXGA=0x34, SVGA/CIF=0x38 */198 : sreg <= 24'h0035_da;199 : sreg <= 24'h0022_1a;200 : sreg <= 24'h0037_c3;201 : sreg <= 24'h0034_c0;202 : sreg <= 24'h0006_88;203 : sreg <= 24'h000d_87;204 : sreg <= 24'h000e_41;205 : sreg <= 24'h0042_03;206 : sreg <= 24'h00FF_00; //選擇 DSP相關寄存器組/* Set DSP input image size and offset. The sensor output image can be scaled with OUTW/OUTH */207 : sreg <= 24'h0005_01;208 : sreg <= 24'h00E0_04;209 : sreg <= 24'h00C0_64;/* Image Horizontal Size 0x51[10:3] */ //11_0010_0000 = 800210 : sreg <= 24'h00C1_4B;/* Image Vertiacl Size 0x52[10:3] */ //10_0101_1000 = 600 211 : sreg <= 24'h008C_00;/* {0x51[11], 0x51[2:0], 0x52[2:0]} */212 : sreg <= 24'h0053_00;/* OFFSET_X[7:0] */213 : sreg <= 24'h0054_00;/* OFFSET_Y[7:0] */214 : sreg <= 16'h0051_C8;/* H_SIZE[7:0]= 0x51/4 */ //200215 : sreg <= 16'h0052_96;/* V_SIZE[7:0]= 0x52/4 */ //150 216 : sreg <= 24'h0055_00;/* V_SIZE[8]/OFFSET_Y[10:8]/H_SIZE[8]/OFFSET_X[10:8] */217 : sreg <= 24'h0057_00;/* H_SIZE[9] */218 : sreg <= 24'h0086_3D;219 : sreg <= 24'h0050_80;/* H_DIVIDER/V_DIVIDER */ 220 : sreg <= 24'h00D3_80;/* DVP prescalar */221 : sreg <= 24'h0005_00;222 : sreg <= 24'h00E0_00;223 : sreg <= 24'h00FF_00;224 : sreg <= 24'h0005_00;225 : sreg <= 24'h00FF_00;226 : sreg <= 24'h00E0_04;227 : sreg <= 24'h00E1_77;228 : sreg <= 24'h00E0_00; default : sreg <= 24'hFFFF_FF; // End configurationendcase
end
endmodule
(3)實際顯示效果如下,整體分辨率為1280x720,攝像頭畫面大小為640x480,圖像是RGB565彩色圖。
2.轉灰度圖
??通過以下RGB轉YC模塊,獲取亮度信息Y,用Y代替R,G,B分量,就可以得到灰度圖。
module rgb_yc444
(input pix_clk ,input rst_n ,input [7:0] r_in ,input [7:0] g_in ,input [7:0] b_in ,input hs_i ,input vs_i ,input din_valid ,output dout_valid ,output hs_o ,output vs_o ,output reg [23:0] ycbcr444_out //{Y,Cb,Cr}
);//---------------------------------------------------------
// =>Ycustom = 0.213R′ + 0.715G′ + 0.072B′
// =>Cb = –0.114R′ – 0.384G′ + 0.498B′ + 128
// =>Cr = 0.498R′ – 0.452G′ – 0.046B′ + 128
//---------------------------------------------------------wire [15:0] y_r_tmp ;wire [15:0] y_g_tmp ;wire [15:0] y_b_tmp ;wire [15:0] cb_b_tmp;wire [15:0] cb_g_tmp;wire [15:0] cb_r_tmp;wire [15:0] cr_r_tmp;wire [15:0] cr_g_tmp;wire [15:0] cr_b_tmp;reg [15:0] y_r_reg = 16'd0;reg [15:0] y_g_reg = 16'd0;reg [15:0] y_b_reg = 16'd0;reg [15:0] cb_b_reg = 16'd0;reg [15:0] cb_g_reg = 16'd0;reg [15:0] cb_r_reg = 16'd0;reg [15:0] cr_r_reg = 16'd0;reg [15:0] cr_g_reg = 16'd0;reg [15:0] cr_b_reg = 16'd0;wire [8:0] Y_tmp ;wire [8:0] Cb_tmp;wire [8:0] Cr_tmp;reg dout_valid1 = 1'b0;reg dout_valid2 = 1'b0;reg dout_valid3 = 1'b0;reg hs_o_1 = 1'b0;reg hs_o_2 = 1'b0;reg hs_o_3 = 1'b0;reg vs_o_1 = 1'b0;reg vs_o_2 = 1'b0;reg vs_o_3 = 1'b0; reg [7:0]Y_node;reg [7:0]Cb_node;reg [7:0]Cr_node;//=======================================================assign y_r_tmp = (r_in<<6) - (r_in<<3) - (r_in<<1);assign y_g_tmp = (g_in<<8) - (g_in<<6) - (g_in<<3) - g_in;assign y_b_tmp = (b_in<<4) + (b_in<<1);assign cb_r_tmp = (r_in<<5) - (r_in<<1) - r_in;assign cb_g_tmp = (g_in<<6) + (g_in<<5) + (g_in<<1) ;assign cb_b_tmp = (b_in<<7) - b_in;assign cr_r_tmp = (r_in<<7) - r_in;assign cr_g_tmp = (g_in<<7) -(g_in<<3) -(g_in<<2) -g_in;assign cr_b_tmp = (b_in<<3) +(b_in<<2);always@(posedge pix_clk)beginy_r_reg <= y_r_tmp ;y_g_reg <= y_g_tmp ;y_b_reg <= y_b_tmp ;cb_b_reg <= cb_b_tmp;cb_g_reg <= cb_g_tmp;cb_r_reg <= cb_r_tmp;cr_r_reg <= cr_r_tmp;cr_g_reg <= cr_g_tmp;cr_b_reg <= cr_b_tmp;end// --------+128為四舍五入操作-----------------------------assign Y_tmp = (y_r_reg +y_g_reg +y_b_reg+8'd128 )>>8;assign Cb_tmp= (cb_b_reg-cb_g_reg-cb_r_reg+8'd128+ 32768)>>8;assign Cr_tmp= (cr_r_reg-cr_g_reg-cr_b_reg+8'd128+ 32768)>>8;//=====================================================
//overflow controlalways@(posedge pix_clk or negedge rst_n)begin if(!rst_n)Y_node<=8'b0;else if(Y_tmp[8]==1'b1)Y_node<=8'hff;elseY_node<=Y_tmp[7:0];endalways@(posedge pix_clk or negedge rst_n)begin if(!rst_n)Cb_node<=8'b0;else if(Cb_tmp[8]==1'b1)Cb_node<=8'hff;elseCb_node<=Cb_tmp[7:0];endalways@(posedge pix_clk or negedge rst_n)beginif(!rst_n) Cr_node<=8'b0;else if(Cr_tmp[8]==1'b1)Cr_node<=8'hff;elseCr_node<=Cr_tmp[7:0];endalways@(posedge pix_clk or negedge rst_n)beginif(!rst_n) ycbcr444_out<=24'b0;else ycbcr444_out<={Y_node,Cb_node,Cr_node}; end//----------------------------------------------------- always@(posedge pix_clk)begindout_valid1<=din_valid;dout_valid2<=dout_valid1;dout_valid3<=dout_valid2;endalways@(posedge pix_clk)begin hs_o_1<=hs_i;hs_o_2<=hs_o_1;hs_o_3<=hs_o_2;end always@(posedge pix_clk)begin vs_o_1<=vs_i;vs_o_2<=vs_o_1;vs_o_3<=vs_o_2;endassign dout_valid = dout_valid3;assign hs_o = hs_o_3;assign vs_o = vs_o_3;endmodule
3.轉二值圖
??利用以下簡單sobel算子計算,還可以得到邊緣信息的二值圖。
module sobel(input wire clk,input wire rst_n,input wire Y_de,input wire Y_hsync, input wire Y_vsync,input wire [ 7:0] Y_data,output wire sobel_de,output wire sobel_hsync,output wire sobel_vsync,output wire [ 7:0] sobel_data
);wire de_o; //matrix_3x3 ----------------------------------------wire [ 7:0] matrix_11 ;wire [ 7:0] matrix_12 ;wire [ 7:0] matrix_13 ;wire [ 7:0] matrix_21 ;wire [ 7:0] matrix_22 ;wire [ 7:0] matrix_23 ;wire [ 7:0] matrix_31 ;wire [ 7:0] matrix_32 ;wire [ 7:0] matrix_33 ;//sobel ------------------作為行列的寄存,最終取值
reg [ 9:0] Gx1,Gx3,Gy1,Gy3,Gx,Gy ;
reg [10:0] G ; //閾值
wire [ 7:0] value ; //信號同步 ----------------------------------------------
reg [ 3:0] Y_de_r ;
reg [ 3:0] Y_hsync_r ;
reg [ 3:0] Y_vsync_r ;shift_matrix3_3 #
(.BITS (8 ), .WIDTH (640) //
)
shift_matrix3_3_inst
(.clk (clk), .rst_n (rst_n ),.vs_i (Y_vsync ), .de_i (Y_de ), .din (Y_data ), //.vs_o ( ), .de_o (de_o ), .mat_p11(matrix_11), //line1 .mat_p12(matrix_12), .mat_p13(matrix_13), .mat_p21(matrix_21), //line2 .mat_p22(matrix_22), .mat_p23(matrix_23), .mat_p31(matrix_31), //line3 .mat_p32(matrix_32), .mat_p33(matrix_33)
);//sobel處理
/*-1 0 +1 gx = -2 0 +2 -1 0 +1 +1 +2 +1gy = 0 0 0 -1 -2 -1
*/
//延時1個clk
always @ (negedge rst_n or posedge clk)
begin// Reset whenever the reset signal goes low, regardless of the clockif (!rst_n)beginGx1 <= 10'd0;Gx3 <= 10'd0;Gy1 <= 10'd0;Gy3 <= 10'd0; end// If not resetting, update the register output on the clock's rising edgeelsebeginGx1 <= matrix_11 + (matrix_21 << 1) + matrix_31; //矩陣相乘,第一列為x1+2x2+x1,Gx3 <= matrix_13 + (matrix_23 << 1) + matrix_33;Gy1 <= matrix_11 + (matrix_12 << 1) + matrix_13;Gy3 <= matrix_31 + (matrix_32 << 1) + matrix_33;end
end// G = |Gx| + |Gy|
//延時1個clk//取 Gx 、Gy的絕對值
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginGx <= 10'd0;Gy <= 10'd0;endelse begin //也可判斷bit[7]來確定Gx <= (Gx1 > Gx3) ? (Gx1 - Gx3) : (Gx3 - Gx1);Gy <= (Gy1 > Gy3) ? (Gy1 - Gy3) : (Gy3 - Gy1);end
end //得G
//延時1個clk
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginG <= 10'd0;endelse beginG <= Gx + Gy;end
end//設置判決閾值
assign value = 8'd100;assign sobel_data = (G > value) ? 8'h00 : 8'hFF;// white backgroud, black frontgroud
// assign sobel_data = (G > value) ? 8'hFF : 8'h00;// black backgroud, white frontgroud//== 信號同步
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginY_de_r <= 4'b0;Y_hsync_r <= 4'b0;Y_vsync_r <= 4'b0;endelse begin Y_de_r <= {Y_de_r[2:0], de_o};//Y_de};Y_hsync_r <= {Y_hsync_r[2:0], Y_hsync};Y_vsync_r <= {Y_vsync_r[2:0], Y_vsync};end
end
assign sobel_de = Y_de_r[3];
assign sobel_hsync = Y_hsync_r[3];
assign sobel_vsync = Y_vsync_r[3];endmodule