一、操作环境
板子:Dopi Hi3516DV300 开发板 + 京东方JD9366屏幕 + IMX307摄像头
电脑:Ubuntu 18.04
SDK版本: Hi3516CV500_SDK_V2.0.1.1
二、调试简述
一般常见的彩色LCD屏幕的接口模式有:MCU模式,RGB模式,SPI模式,VSYNC模式,DSI模式。第一种模式顾名思义,主要是用于单片机领域,主要的特点就是廉价,以Intel的8080总线协议标准通讯,没有时钟以及同步信号,主要由数据总线和控制总线构成,这种LCD驱动IC带有GRAM,所以显示的方便率一般比较低,难以做大。与muc屏幕相反,大屏幕一般多采用RGB模式接口,而且此模式除了数据和控制总线以外需要水平和垂直以及时钟的参与,相比MCU屏幕这个数据更快,一般自己通过LCD外设DMA搬运。SPI模式一般采用较少,有3线和4线的,连线为CS/,SLK,SDI,SDO四根线,连线少但是软件控制比较复杂。VSYNC模式其实就是就是在MCU模式上加了一个VSYNC信号,应用于运动画面更新,这样就与上述两个接口有很大的区别,该模式支持直接进行动画显示的功能,它提供了一个对MCU接口最小的改动,实现动画显示的解决方案,在这种模式下,内部的显示操作与外部VSYNC信号同步,可以实现比内部操作更高的速率的动画显示。最后的模式需要更高的CPU频率支持,常见于现今的智能手机,没有水平和垂直同步时钟,控制信号和RGB数据是以报文的形式通过MIPI传输的。
调试DSI的屏幕和CSI接口的摄像头类似,一般需要一个初始化序列,通过一定的初始化序列后,主控通过MIPI TX发送RGB数据屏幕才会正常的显示。
三、调试过程
- 选择合适的例程
以sample/vio例子的基础上进行修改,DOPI开发板直接连接一个摄像头,通过VI获取摄像头数据,VPSS处理,VO输出显示至MIPI TX接口的显示屏幕上。这个例子的SAMPLE_VIO_ViOnlineVpssOnlineRoute函数已经可以正常在VPSS取摄像头的流,然后VPSS与VENC和VO进行绑定,为了简便,将此函数的VENC相关业务逻辑先屏蔽。
/*config vo*/
SAMPLE_COMM_VO_GetDefConfig(&stVoConfig);
stVoConfig.enDstDynamicRange = enDynamicRange;
if (1 == u32VoIntfType)
{
stVoConfig.enVoIntfType = VO_INTF_BT1120;
stVoConfig.enIntfSync = VO_OUTPUT_1080P25;
}
else
{
stVoConfig.enVoIntfType = VO_INTF_HDMI;
}
stVoConfig.enPicSize = enPicSize;
对于DV300,走的是下面,可知默认的输出是HDMI,我们进行一以下配置。
- 需要开发者修改的
1、将VPSS的输出配置成与屏幕分辨率一致大小的图片流,只需在SAMPLE_COMM_VPSS_Start传入结构体设置便可,显示屏的分辨率为800*1280,摄像头是1920*1080,所以先缩小至1280*800,然后再旋转。
// 设置通道缩放属性
astVpssChnAttr[VpssChn].u32Width = 1280;
astVpssChnAttr[VpssChn].u32Height = 800;
/*start vpss*/
abChnEnable[VpssChn] = HI_TRUE;
s32Ret = SAMPLE_COMM_VPSS_Start(VpssGrp, abChnEnable, &stVpssGrpAttr, astVpssChnAttr);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("start vpss group failed. s32Ret: 0x%x !\n", s32Ret);
goto EXIT1;
}
s32Ret = HI_MPI_VPSS_SetChnRotation(VpssGrp, VpssChn, ROTATION_270);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("start HI_MPI_VPSS_SetChnRotation grp %d failed. s32Ret: 0x%x !\n",VpssGrp, s32Ret);
return s32Ret;
}
2、SAMPLE_COMM_VO_GetDefConfig函数添加对应分辨率的参数配置
#ifdef LCD_TYPE_JD9366
RECT_S stDefDispRect = {0, 0, 800, 1280};
SIZE_S stDefImageSize = {800, 1280};
#else
RECT_S stDefDispRect = {0, 0, 1920, 1080};
SIZE_S stDefImageSize = {1920, 1080};
#endif
...........
if(HI3516C_V500 == u32ChipId)
{
pstVoConfig->enVoIntfType = VO_INTF_BT1120;
}
else
{
#ifdef LCD_TYPE_JD9366
pstVoConfig->enVoIntfType = VO_INTF_MIPI;
#else
pstVoConfig->enVoIntfType = VO_INTF_HDMI;
#endif
}
#ifdef LCD_TYPE_JD9366
pstVoConfig->enIntfSync = VO_OUTPUT_USER;
#else
pstVoConfig->enIntfSync = VO_OUTPUT_1080P60;
#endif
修改后,stVoConfig.enVoIntfType为VO_INTF_MIPI类型,stVoConfig.enIntfSync为VO_OUTPUT_USER。
3、SAMPLE_COMM_VO_StartVO修改分辨率大小
4、SAMPLE_COMM_VO_StartVO中将SAMPLE_COMM_VO_StartDev函数换成SetUserIntfSyncInfo_VO_StartDev,具体实现后续说明。
//s32Ret = SAMPLE_COMM_VO_StartDev(VoDev, &stVoPubAttr);
s32Ret = SetUserIntfSyncInfo_VO_StartDev(VoDev, &stVoPubAttr);
SAMPLE_PRT("SetUserIntfSyncInfo_VO_StartDev s32Ret %x\n",s32Ret);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("SAMPLE_COMM_VO_StartDev failed!\n");
return s32Ret;
}
5、SAMPLE_COMM_VO_StartVO中的SAMPLE_COMM_VO_GetWH会根据stVoConfig.enIntfSync获取分辨率以及帧率信息,根据自己屏幕的参数进行修改。
case VO_OUTPUT_USER :
#ifdef LCD_TYPE_JD9366
*pu32W = 800;
*pu32H = 1280;
*pu32Frm = 60;
#else
*pu32W = 720;
*pu32H = 576;
*pu32Frm = 25;
#endif
break;
6、根据LCD驱动IC需要的时序对时序参数结构体进行配置,通过海思的文档进行输入,然后对应参数会自动计算出来。根据ReleaseDoc\zh\02.only for reference\software下的《RGB_MIPI屏幕时钟时序计算器.xlsx》进行设置。
注意的是下面切换到MIPI屏适配VDP时钟时序。
相关的时序信息绿色输入,相关配置红色输出。在SAMPLE_COMM_VO_StartVO函数实现的.c文件里面添加一个机构体:
combo_dev_cfg_t MIPI_TX_800X1280_USER_CONFIG =
{
.devno = 0,
.lane_id = {0, 1, 2, 3},
.output_mode = OUTPUT_MODE_DSI_VIDEO,
.output_format = OUT_FORMAT_RGB_24_BIT,
.video_mode = BURST_MODE,
.sync_info = {
.vid_pkt_size = 800, // hact
.vid_hsa_pixels = 4, // hsa
.vid_hbp_pixels = 40, // hbp
.vid_hline_pixels = 884, // hact + hsa + hbp + hfp
.vid_vsa_lines = 2, // vsa
.vid_vbp_lines = 22, // vbp
.vid_vfp_lines = 16, // vfp
.vid_active_lines = 1320, //vact
.edpi_cmd_size = 0,
},
.phy_data_rate = 421,
.pixel_clk = 70013,
};
相关参数信息有计算的得到的红色数据获取 ,里面还说明了TX Lane的使用数量以及ID、数据格式和输出模式。
7、根据剩下的参数实现SetUserIntfSyncInfo_VO_StartDev函数。
HI_S32 SetUserIntfSyncInfo_VO_StartDev(VO_DEV VoDev, VO_PUB_ATTR_S* pstPubAttr)
{
HI_S32 s32Ret = HI_SUCCESS;
HI_U32 u32Framerate = 60;
VO_USER_INTFSYNC_INFO_S stUserInfo = {0};
/* Fill pub attr */
pstPubAttr->enIntfType = VO_INTF_MIPI;
pstPubAttr->enIntfSync = VO_OUTPUT_USER;
pstPubAttr->stSyncInfo.bSynm = 0;
pstPubAttr->stSyncInfo.bIop = 1;
pstPubAttr->stSyncInfo.u8Intfb = 0;
pstPubAttr->stSyncInfo.u16Vact = 1280;
pstPubAttr->stSyncInfo.u16Vbb = 24;
pstPubAttr->stSyncInfo.u16Vfb = 16;
pstPubAttr->stSyncInfo.u16Hact = 800;
pstPubAttr->stSyncInfo.u16Hbb = 44;
pstPubAttr->stSyncInfo.u16Hfb = 40;
pstPubAttr->stSyncInfo.u16Hmid = 1;
pstPubAttr->stSyncInfo.u16Bvact = 1;
pstPubAttr->stSyncInfo.u16Bvbb = 1;
pstPubAttr->stSyncInfo.u16Bvfb = 1;
pstPubAttr->stSyncInfo.u16Hpw = 4;
pstPubAttr->stSyncInfo.u16Vpw = 2;
pstPubAttr->stSyncInfo.bIdv = 0;
pstPubAttr->stSyncInfo.bIhs = 0;
pstPubAttr->stSyncInfo.bIvs = 0;
s32Ret = HI_MPI_VO_SetPubAttr(VoDev, pstPubAttr);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_PRT("failed with %#x!\n", s32Ret);
return HI_FAILURE;
}
/* Fill user sync info */
stUserInfo.stUserIntfSyncAttr.enClkSource = VO_CLK_SOURCE_PLL;
stUserInfo.stUserIntfSyncAttr.stUserSyncPll.u32Fbdiv = 70;//PLL整数倍频系数 0-0xfff
stUserInfo.stUserIntfSyncAttr.stUserSyncPll.u32Frac= 0x346DC;
stUserInfo.stUserIntfSyncAttr.stUserSyncPll.u32Refdiv = 2;
stUserInfo.stUserIntfSyncAttr.stUserSyncPll.u32Postdiv1 = 4;
stUserInfo.stUserIntfSyncAttr.stUserSyncPll.u32Postdiv2 = 3;
stUserInfo.u32DevDiv = 1;
stUserInfo.u32PreDiv = 1;
/* Set user interface sync info */
s32Ret = HI_MPI_VO_SetUserIntfSyncInfo(VoDev, &stUserInfo);
if (s32Ret != HI_SUCCESS)
{
printf("Set user interface sync info failed with %x.\n",s32Ret);
return HI_FAILURE;
}
s32Ret = HI_MPI_VO_SetDevFrameRate(VoDev, u32Framerate);
s32Ret = HI_MPI_VO_Enable(VoDev);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_PRT("failed with %#x!\n", s32Ret);
return HI_FAILURE;
}
return s32Ret;
}
8、SAMPLE_COMM_VO_StartVO最后面调用了SAMPLE_COMM_VO_StartMipiTx函数,对此函数进行修改
case VO_OUTPUT_USER:
pstMipiTxConfig = &MIPI_TX_800X1280_USER_CONFIG;
break;
9、添加初始化序列,在SAMPLE_COMM_VO_StartVO最后面调用了SAMPLE_COMM_VO_StartMipiTx函数中调用了SAMPLE_PRIVATE_VO_InitMipiTxScreen对MIPI屏幕进行初始化。相关细节见下。
- 海思平台MIPI TX如何发送初始化序列
在文档:ReleaseDoc\zh\02.only for reference\software《屏幕对接 使用指南.pdf》中有说到MIPI TX三种模式配置方式,根据给的初始化列表选择合适的方式。拿JD9366屏幕初始化给的是往8Bit的寄存器地址里面写一个8Bit的数据,假设往0x11地址里面写0x22操作的方法是:
cmd_info.devno = 0;
cmd_info.cmd_size = 0x2211; // H:data L:reg
cmd_info.data_type = 0x15;
cmd_info.cmd = NULL;
s32Ret = ioctl(fd, HI_MIPI_TX_SET_CMD, &cmd_info);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("MIPI_TX SET CMD failed\n");
close(fd);
return -1;
}
假设就发送一个命令0x29:
cmd_info.devno = 0;
cmd_info.cmd_size = 0x0029;
cmd_info.data_type = 0x23;
cmd_info.cmd = NULL;
s32Ret = ioctl(fd, HI_MIPI_TX_SET_CMD, &cmd_info);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("MIPI_TX SET CMD failed\n");
close(fd);
return;
}
有的LCD需要一定的上电时序以及需要硬件复位,在初始化函数里面拉下IO对LCD硬件复位。
四、调试问题
1、不出图
不出图的原因有很多,可以参考《屏幕对接 使用指南.pdf》中显示colorbar,先用示波器看MIPI Lane有无数据输出以判断初始化是否正常,MIPI Lane的协议相对复杂,以本人的经验一般来说当看见MIPI Lane有一定规律波形输出可以近似看成刷的初始化序列是正常的,首先查看芯片端配置的时序是否合适,对时序进行修改下;从开始到显示逐步排查每个环节的问题以定位。如果直接Lane没有数据,查看电压、Reset等有效电压。时序参数不正常会出现一些色彩差等原因。
2、视频层正常但是UI层颜色不正常,UI层默认是RGB显示,所以要设置UI通道不要YUV转化显示,在VO初始化前加:
VO_CSC_S pstCSC;
HI_MPI_VO_GetGraphicLayerCSC(0, &pstCSC);
pstCSC.enCscMatrix = VO_CSC_MATRIX_IDENTITY;
HI_MPI_VO_SetGraphicLayerCSC(0, &pstCSC);
五、结语
源码与文档:戳这里
Dopi 开发板交流群:735884031