天天看点

DM8168 - BT656格式视频采集

系统包含两个部分,1)FPGA采集卡端,配置输出视频格式为BT656; 2)DM8168端,配置输入格式为BT656。

1、bt656格式简介:

BT656为YUV422格式的视频数据定义了并行或串行接口,分辨率为720*486或720*576。BT656并行接口使用8bit或10bit的复合YUV数据和27MHz的时钟,不需使用传统的视频时序信号(HSYNC,VSYNC和BLANK),将时序代码嵌入到视频流中,可以减少芯片接口的引脚数量。这里采用的是720*576的8bit格式,时序如图1所示。

DM8168 - BT656格式视频采集

                                                                                                        图1 时序

表1:SAV EAV插入值

DM8168 - BT656格式视频采集

2、DM8168端代码:

#include <stdbool.h>
#include <signal.h>

#include "osa.h"
#include "ti_vsys.h"
#include "ti_vcap.h"
#include "ti_vdis.h"
#include "mcfw/src_linux/mcfw_api/usecases/multich_common.h"

#define NUM_CAPTURE_DEVICES    1

static void mcfw_chain_init(UInt32 scaleWidth, UInt32 scaleHeight);
static void mcfw_chain_deinit();

/* signal handle for ctrl + c */
volatile bool g_quit = false;
static void sig_handle(int signo)
{
    printf("recived quit signal\n");
    g_quit = true;
}

int main(int argc, char **argv)
{
    if (argc != 3) {
        printf("Usage: %s <scale width> <scale height>\n", argv[0]);
        exit(1);
    }
    UInt32 scaleWidth  = atoi(argv[1]);
    UInt32 scaleHeight = atoi(argv[2]);

    signal(SIGINT, sig_handle);

    /* init System, Capture and Display Module */
    VSYS_PARAMS_S prms_sys;
    VCAP_PARAMS_S prms_vcap;
    VDIS_PARAMS_S prms_vdis;
    Vsys_params_init(&prms_sys);
    Vcap_params_init(&prms_vcap);
    Vdis_params_init(&prms_vdis);

    prms_vdis.enableConfigExtVideoEncoder = FALSE;

    Vsys_init(&prms_sys);
    Vcap_init(&prms_vcap);
    Vdis_init(&prms_vdis);

    Vsys_configureDisplay(); // configure display

    /* construct capture and display chain */
    mcfw_chain_init(scaleWidth, scaleHeight); 

    /* make chain start working */
    Vdis_start();
    Vcap_start();

    /* wait ctrl + c */
    while (! g_quit) { 
        printf("Display frames... CTRL+C\n"); 
        sleep(1);
    } 

    /* stop working and deinit */
    Vcap_stop();
    Vdis_stop();

    mcfw_chain_deinit();

    Vsys_deConfigureDisplay();

    Vcap_exit();
    Vdis_exit();
    Vsys_exit();

    return 0;
}

static void mcfw_chain_init(UInt32 scaleWidth, UInt32 scaleHeight)
{
    UInt32 i;

    System_linkControl( SYSTEM_LINK_ID_M3VPSS, SYSTEM_M3VPSS_CMD_RESET_VIDEO_DEVICES, NULL, 0, TRUE);

    /* chain: CAPTURE -> DEI -> DISPLAY */
    gVcapModuleContext.captureId    = SYSTEM_LINK_ID_CAPTURE;
    gVcapModuleContext.deiId[0]     = SYSTEM_LINK_ID_DEI_0;
    gVdisModuleContext.displayId[0] = SYSTEM_LINK_ID_DISPLAY_0; 

    /* capture link init */
    CaptureLink_CreateParams    prm_capture;
    CaptureLink_CreateParams_Init(&prm_capture);
    prm_capture.outQueParams[0].nextLink = gVcapModuleContext.deiId[0];
    prm_capture.numVipInst               = 1;
    prm_capture.tilerEnable              = FALSE;
    prm_capture.numBufsPerCh             = 8;
    prm_capture.maxBlindAreasPerCh       = 4;
    prm_capture.isPalMode = Vcap_isPalMode();

    prm_capture.doCropInCapture     = FALSE;
    prm_capture.enableSdCrop        = FALSE;


    /* capture instance init */
    CaptureLink_VipInstParams  *prm_cap_inst = &prm_capture.vipInst[0];
    prm_cap_inst->vipInstId          = (SYSTEM_CAPTURE_INST_VIP0_PORTA + 0 ) % SYSTEM_CAPTURE_INST_MAX;
    prm_cap_inst->inDataFormat       = SYSTEM_DF_YUV422P;
    prm_cap_inst->numOutput          = 1;

    prm_cap_inst->standard          = SYSTEM_STD_576I;  
    prm_cap_inst->videoIfMode       = DEVICE_CAPT_VIDEO_IF_MODE_8BIT;
    prm_cap_inst->videoCaptureMode = DEVICE_CAPT_VIDEO_CAPTURE_MODE_SINGLE_CH_NON_MUX_EMBEDDED_SYNC;


    /* capture out init */
    CaptureLink_OutParams *prm_cap_out = &prm_cap_inst->outParams[0];
    prm_cap_out->dataFormat          = SYSTEM_DF_YUV422I_YUYV;
    prm_cap_out->scEnable            = FALSE;
    prm_cap_out->scOutWidth          = 720;
    prm_cap_out->scOutHeight         = 576;
    prm_cap_out->outQueId            = 0;

    /* De-Interleave link init */
    DeiLink_CreateParams        prm_dei;
    MULTICH_INIT_STRUCT(DeiLink_CreateParams, prm_dei);
    prm_dei.inQueParams.prevLinkId = gVcapModuleContext.captureId;
    prm_dei.inQueParams.prevLinkQueId = 0;
    // Set the scale parameters.
    prm_dei.outScaleFactor[DEI_LINK_OUT_QUE_DEI_SC][0].scaleMode = DEI_SCALE_MODE_ABSOLUTE;
    prm_dei.outScaleFactor[DEI_LINK_OUT_QUE_DEI_SC][0].absoluteResolution.outWidth  = scaleWidth;
    prm_dei.outScaleFactor[DEI_LINK_OUT_QUE_DEI_SC][0].absoluteResolution.outHeight = scaleHeight;
    for (i=1; i < DEI_LINK_MAX_CH; i++)
        prm_dei.outScaleFactor[DEI_LINK_OUT_QUE_DEI_SC][i] = prm_dei.outScaleFactor[DEI_LINK_OUT_QUE_DEI_SC][0];

    prm_dei.enableOut[DEI_LINK_OUT_QUE_DEI_SC]              = TRUE; 
    prm_dei.outQueParams[DEI_LINK_OUT_QUE_DEI_SC].nextLink  = gVdisModuleContext.displayId[0];
    prm_dei.comprEnable                                     = FALSE;
    prm_dei.setVipScYuv422Format                            = FALSE;
    prm_dei.enableDeiForceBypass = TRUE;


    /* Display link init */
    DisplayLink_CreateParams    prm_dis;
    MULTICH_INIT_STRUCT(DisplayLink_CreateParams, prm_dis);
    prm_dis.inQueParams[0].prevLinkId    = gVcapModuleContext.deiId[0];
    prm_dis.inQueParams[0].prevLinkQueId = DEI_LINK_OUT_QUE_DEI_SC;
    prm_dis.displayRes                   = VSYS_STD_1080P_60;

    /* create link */
    System_linkCreate(gVcapModuleContext.captureId, &prm_capture, sizeof(prm_capture));
    System_linkCreate(gVcapModuleContext.deiId[0], &prm_dei, sizeof(prm_dei));
    System_linkCreate(gVdisModuleContext.displayId[0], &prm_dis, sizeof(prm_dis));

    /* setting frames from which channel should be display */
    DisplayLink_SwitchChannelParams prm_switch_ch;
    prm_switch_ch.activeChId = 0;
    System_linkControl(gVdisModuleContext.displayId[0], DISPLAY_LINK_CMD_SWITCH_CH, &prm_switch_ch, sizeof(prm_switch_ch), TRUE);
}

static void mcfw_chain_deinit()
{
    System_linkDelete(gVcapModuleContext.captureId);
    System_linkDelete(gVcapModuleContext.deiId[0]);
    System_linkDelete(gVdisModuleContext.displayId[0]);
}
           

重点注意cap模块的参数配置。

2、FPGA采集卡端代码:RGB888 to BT656

module bt656 (
					output [7: 0] oData,
					output [10:0] oRow,
					output [10:0] oCol,
					input [7: 0] iY,
					input [7: 0] iCb,
					input [7: 0] iCr,
					input iRST_N,
					input iCLK_27);
					
reg [10:0] col_n;
reg [10:0] row_n;
reg [7:0] sdata;
reg [7:0] ssdata;
reg [9:0] y1,cb1,cr1;
assign oData = sdata;

parameter ROW_MAX = 624;
parameter COL_MAX = 1727;

reg [7: 0] mY;
reg [7: 0] mCb;
reg [7: 0] mCr;

assign oRow = (row_n >= 335) ? row_n - 335: ( row_n >= 22 ? row_n - 22: 0);
assign oCol = (col_n >  287) ? ((col_n - 288) >> 1): 0;

always@(posedge iCLK_27)                                         
begin                                                       
	y1 = (66*iY + 129*iCb + 25*iCr+4096)>>8;  
	mY <= (y1[9:8]==2'b00) ? y1[7:0] : (y1[9]==0) ? 8'b11101011 : y1[9:2];
end                                                        
//================================================================
always@(posedge iCLK_27)                                            
begin  
	cb1 = (112*iCr-38*iY - 75*iCb+32768)>>8; 
	mCb <= (cb1[9:8]==2'b00) ? cb1[7:0] : (cb1[9]==0) ? 8'b11110000 : cb1[9:2];
end                                                           
//================================================================
always@(posedge iCLK_27)                                           
begin                                                        
  	cr1 = (112*iY - 94*iCb - 18*iCr+32768)>>8 ; 
	mCr <= (cr1[9:8]==2'b00) ? cr1[7:0] : (cr1[9]==0) ? 8'b11110000 : cr1[9:2];
end   

`endif

// Create ROW AND COL
always @ (posedge iCLK_27 or negedge iRST_N )
begin
	if (!iRST_N) begin		
		col_n <= 0; //285;
		row_n <= 0; //ROW_MAX;		
	end
	else begin
		if (col_n == COL_MAX) begin	
			col_n <= 0;
			row_n <= (row_n == ROW_MAX) ? 0: (row_n + 1);
		end
		else begin
			col_n <= col_n + 1;
		end
	end
end

always @ (posedge iCLK_27 or negedge iRST_N )
begin
	if (!iRST_N) begin			
		sdata <= 8'h80;
		ssdata <= 8'h80;
	end
	else begin		
		// FF0000
		if (col_n == 0 || col_n == 284)
			sdata <= 8'hFF;
		else if (col_n == 1 || col_n == 2 || col_n == 285 || col_n == 286)
			sdata <= 8'h00;					
		
		// EAV
		else if (col_n == 3) begin
			if ( ((row_n >= 0) && (row_n < 22)) || (row_n == 310) || (row_n == 311))
				sdata <= 8'hB6;
			else if ((row_n >= 22) && (row_n < 310))
				sdata <= 8'h9D;
			else if ( ((row_n >= 312) && (row_n < 335)) || (row_n == 623) || (row_n == 624))
				sdata <= 8'hF1;
			else
				sdata <= 8'hDA;
		end	
		
		// BLACKING
		else if ((col_n > 3) &&(col_n < 284) ) begin
			sdata <= col_n[0]? 8'h10: 8'h80;	
		end
		
		// SAV		
		else if (col_n == 287) begin
			if ( ((row_n >= 0) && (row_n < 22)) || (row_n == 310) || (row_n == 311))
				sdata <= 8'hAB;
			else if ((row_n >= 22) && (row_n < 310))
				sdata <= 8'h80;
			else if ( ((row_n >= 312) && (row_n < 335)) || (row_n == 623) || (row_n == 624))
				sdata <= 8'hEC;
			else
				sdata <= 8'hC7;
		end
		
		// DATA
		else begin
			if (((row_n >= 22) && (row_n < 310)) || ((row_n >= 335) && (row_n < 623)) ) begin
				case (col_n[1: 0])
				2'b00: sdata <= mCb;
				2'b01: sdata <= mY;
				2'b10: sdata <= mCr;				
				2'b11: sdata <= mY;
				default: sdata <= 8'h80;
				endcase	
			end
			else begin
				case (col_n[1: 0])
				2'b00: sdata <= 8'h80;
				2'b01: sdata <= 8'h10;
				2'b10: sdata <= 8'h80;				
				2'b11: sdata <= 8'h10;
				default: sdata <= 8'h80;
				endcase	
			end
		end
					
	end		
end

endmodule
           

步骤:

  1. 编写FPGA代码;
  2. 修改DM8168采集代码;

编写最基本的采集显示代码,软件环境(DVRRDK开发环境),链路为

cap -> dei -> dis ,该链路主要对采集视频进行缩放操作。

执行的指令如下:

# target : ./init

# target : ./load

# target : ./cap_scale_dis.out 1920 1080

Bt656最终测试波形图

DM8168 - BT656格式视频采集

继续阅读