前言
本文记录一下这几天调试海思UVC的心得,本次使用的芯片及SDK是海思3531DV100,最终的结果是两路USB摄像头接入海思的两个USB(不同的root hub),HDMI输出四分屏,显示两个摄像头的画面
参考过的文章如下:
https://blog.csdn.net/zhenglie110/article/details/89360312
https://blog.csdn.net/zhenglie110/article/details/89360423
https://blog.csdn.net/zhenglie110/article/details/89361644
http://bbs.ebaina.com/thread-37986-1-1.html
但是上面这些文章里面也不全对,而且每个人需求都不太一样,因此下面我会重头再解释一遍
内核的修改
配置menuconfig增加驱动
make ARCH=arm CROSS_COMPILE=arm-hisiv500-linux- menuconfig
Device Drivers --->
[*] USB support --->
<*> Support for Host-side USB
[*] Enable USB persist by default
<*> xHCI HCD (USB 3.0) support
<*> xHCI support for Hisilicon SoCs
<*> EHCI HCD (USB 2.0) support
[*] Improved Transaction Translator scheduling
<*> Generic EHCI driver for a platform device
<*> OHCI HCD (USB 1.1) support
<*> OHCI support for PCI-bus USB controllers
<*> Generic OHCI driver for a platform device
<*> USB Mass Storage support
<*> USB Gadget Support --->
<*> Multimedia support --->
[*] Cameras/video grabbers support
[*] Media Controller API
[*] V4L2 sub-device userspace API
[*] Media USB Adapters --->
<*> USB Video Class (UVC)
[*] UVC input events device support
<*> GSPCA based webcams --->
[*] Media PCI Adapters --->
[*] V4L platform devices --->
<M> Marvell 88ALP01 (Cafe) CMOS Camera Controller support
<*> SoC camera support
<*> platform camera support
[*] Autoselect ancillary drivers (tuners, sensors, i2c, frontends)
我目前的配置就上面这些,而且是将驱动编译进了内核,网上有人把 Device Drivers -> PHY Subsystem —> Hisilicon Inno USB2 PHY support
打开了,经过我的测试打开这个选项将会导致USB2.0的接口无法识别设备,具体原因不清楚
驱动的修改
将USB摄像头插入板子上,观察打印信息
usb 1-1: USB disconnect, device number 15
usb 1-1: new high-speed USB device number 16 using xhci-hcd
uvcvideo: quirks = 512
uvcvideo: Found UVC 1.00 device USB Camera (0bda:3035)
input: USB Camera as /devices/soc/11000000.xhci/usb1/1-1/1-1:1.0/input/input15
上面已经报告了VID 和 PID,因此不需要在windows里面费那么大劲去找VID和PID
修改文件 linux-3.18.y\drivers\media\usb\uvc\uvc_driver.c
在 struct usb_device_id uvc_ids[]
的末尾模仿之前的加上自己的USB设备信息,如果不加的话,设备插入时调用probe将会按默认的id_table来加载驱动,也就是这个uvc_ids末尾说的Generic USB Video Class
/* my test USB Camera */
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0bda,
.idProduct = 0x3035,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_RESTRICT_FRAME_RATE},
/* Generic USB Video Class */
{
USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
注意一下这个driver_info的赋值,可以用来限制帧率,UVC_QUIRK_RESTRICT_FRAME_RATE
的值是512,这个设置好像是跟带宽有关系,没有深入了解,如果设的过小,将导致无法出图。而且USB2.0的带宽上限也只有480Mbit/s,连一个摄像头都够呛了。
sample_uvc
直接上sample代码了,原本SDK是没有uvc例子的,这个是我自己实现的,代码上传到附件,包括以下几个文件:
- sample_comm.h
- sample_comm_sys.c
- sample_comm_vdec.c
- sample_comm_vo.c
- sample_comm_vpss.c
- sample_uvc.c
下载地址:https://download.csdn.net/download/whitefish520/13216318
有一处错误,上传后才发现:tv_fmt.fmt.pix.height = uvcParam.u32inputHeight;
以下只粘贴了sample_uvc.c在这里,因为其它的内容基本上都是SDK里面原本就提供的,基本上没有改动,sample_uvc.c是这几天以来的心血,从0开始写的,关于程序中一些设置的解释,放在下一个章节,如果程序看的不太懂,先看后文的解读。
/******************************************************************************
A simple program of Hisilicon Hi35xx video input and output implementation.
Copyright (C), 2014-2015, Hisilicon Tech. Co., Ltd.
******************************************************************************
Modification: 2015-1 Created
******************************************************************************/
#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* End of #ifdef __cplusplus */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include "sample_comm.h"
#include "hi_comm_vb.h"
#include "mpi_vb.h"
// usb video camera number
#define UVC_NUM 2
// save stream as file, do not store at flash
#define SAVE_FILE 0
// video memory
typedef struct{
void *start;
int length;
}uvc_buf_t;
struct hiUvcParam
{
HI_S32 fd[UVC_NUM];
HI_CHAR cFileName[UVC_NUM][128];
HI_CHAR cNodeName[UVC_NUM][128];
HI_U32 u32inputWidth;
HI_U32 u32inputHeight;
HI_U32 u32Width;
HI_U32 u32Height;
HI_U32 pixelformat;
HI_U32 V4L2_buffer_num;
HI_U32 buffer_num[UVC_NUM];
uvc_buf_t *uvc_buf[UVC_NUM];
HI_U32 u32BlkSize;
HI_U32 u32BlkCnt;
VB_POOL VbPool;
VB_BLK VbBlk;
HI_U32 u32phyAddr;
HI_U8 *pVirAddr;
VDEC_CHN VdChn[UVC_NUM];
VPSS_GRP VpssGrp[UVC_NUM];
VPSS_CHN VpssChn;
VO_DEV VoDev;
VO_CHN VoChn[UVC_NUM];
VO_LAYER VoLayer;
SAMPLE_VO_MODE_E enMode;
HI_BOOL pthRun;
pthread_t ptuvc;
}uvcParam;
/******************************************************************************
* function : YUV422P(YUYV) 转 YUV422SP(NV16)
* YUYVYUYV -> YYYYUVUV
******************************************************************************/
HI_VOID yuv422p_to_yuv422sp(HI_U8* yuv, HI_S32 width, HI_S32 height)
{
HI_S32 i, j, k;
HI_U8 yuv422p[width*height*2];
memcpy(yuv422p, yuv, width*height*2);
HI_U8* y = yuv;
HI_U8* uv = &yuv[width*height];
for(i=0, j=0, k=0; i<width*height*2; i+=4, j+=2, k+=2)
{
y[j] = yuv422p[i];
y[j+1] = yuv422p[i+2];
uv[k] = yuv422p[i+3];
uv[k+1] = yuv422p[i+1];
}
}
/******************************************************************************
* function : usb video camera parameter set
******************************************************************************/
HI_S32 HI_UVC_Param_Set(HI_VOID)
{
HI_S32 i, j=0;
for(i=0; i<UVC_NUM; i++)
{
while(j < 64)
{
sprintf(uvcParam.cNodeName[i], "/dev/video%d", j++);
if(0 == access(uvcParam.cNodeName[i], F_OK))
break;
}
sprintf(uvcParam.cFileName[i], "/app/uvc/uvcVideo%d.yuv", i);
uvcParam.fd[i] = -1;
uvcParam.buffer_num[i] = 0;
uvcParam.uvc_buf[i] = NULL;
uvcParam.pVirAddr = NULL;
uvcParam.VdChn[i] = i;
uvcParam.VpssGrp[i] = i;
uvcParam.VpssChn = i;
}
uvcParam.u32inputWidth = HD_WIDTH;
uvcParam.u32inputHeight = HD_HEIGHT;
uvcParam.u32Width = HD_WIDTH;
uvcParam.u32Height = HD_HEIGHT;
//uvcParam.pixelformat = V4L2_PIX_FMT_YUYV;
uvcParam.pixelformat = V4L2_PIX_FMT_MJPEG;
uvcParam.V4L2_buffer_num = 10;
uvcParam.u32BlkSize = uvcParam.u32Width * uvcParam.u32Height * 2;
uvcParam.u32BlkCnt = 15;
uvcParam.VpssChn = 0;
uvcParam.enMode = VO_MODE_4MUX;
uvcParam.pthRun = HI_TRUE;
if(j >= 64)
return HI_FAILURE;
else
return HI_SUCCESS;
}
/******************************************************************************
* function : open usb video camera device
******************************************************************************/
HI_S32 HI_UVC_Open(HI_VOID)
{
struct v4l2_input inp;
HI_S32 i, j;
for(i=0; i<UVC_NUM; i++)
{
uvcParam.fd[i] = open(uvcParam.cNodeName[i], O_RDWR, 0);
if(uvcParam.fd[i] < 0)
{
SAMPLE_PRT("camera[%d] : %s open failed ! \n", i, uvcParam.cNodeName[i]);
return HI_FAILURE;
}
for(j=0;j<16;j++)
{
inp.index = j;
if (-1 == ioctl (uvcParam.fd[i], VIDIOC_S_INPUT, &inp))
{
SAMPLE_PRT("camera[%d] : VIDIOC_S_INPUT failed %d !\n", i, j);
}
else
{
printf("camera[%d] : VIDIOC_S_INPUT success %d !\n", i, j);
break;
}
}
}
return HI_SUCCESS;
}
/******************************************************************************
* function : close usb video camera device
******************************************************************************/
HI_S32 HI_UVC_Close(HI_VOID)
{
HI_S32 i;
for(i=0; i<UVC_NUM; i++)
{
if(uvcParam.fd[i] > 0)
close(uvcParam.fd[i]);
}
return HI_SUCCESS;
}
/******************************************************************************
* function : usb video camera init
******************************************************************************/
HI_S32 HI_UVC_Init(HI_VOID)
{
HI_S32 i;
struct v4l2_capability cap; /* decive fuction, such as video input */
struct v4l2_fmtdesc fmtdesc; /* detail control value */
struct v4l2_format fmt;
HI_S32 ret = HI_FAILURE;
/* get width and height*/
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
for(i=0; i<UVC_NUM; i++)
{
if(ret = ioctl(uvcParam.fd[i], VIDIOC_G_FMT, &fmt)<0)
{
SAMPLE_PRT("camera[%d] : fail to ioctl VIDIOC_G_FMT\n", i);
return HI_FAILURE;
}
printf