系统包含两个部分,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所示。
图1 时序
表1:SAV EAV插入值
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
步骤:
- 编写FPGA代码;
- 修改DM8168采集代码;
编写最基本的采集显示代码,软件环境(DVRRDK开发环境),链路为
cap -> dei -> dis ,该链路主要对采集视频进行缩放操作。
执行的指令如下:
# target : ./init
# target : ./load
# target : ./cap_scale_dis.out 1920 1080
Bt656最终测试波形图