1 编写ov5642测试例程
在编写好ov5642驱动之后,需要编写程序对其进行测试,这就是V4L2应用层程序。要编写V4L2应用层程序,首先需要理解V4L2提供的各种ioctl函数,要知道如何配置视频设备的初始化参数,特别注意的是配置是有一定顺序的,如果顺序错了那肯定会有错误。在初始化视频设备之后,需要建立缓冲区来接收视频设备传过来的帧,V4L2提供了两种模式来获取视频帧:MMAP和UserPtr。由于在vpif驱动程序中只提供了对MMAP的支持,所以我们在编写测试例程时也是使用该模式。对于V4L2的MMAP和UserPtr两种模式的理解其实有点困难,现在我也还没有完全弄清,但是这两种方法的使用却比较简单。
ov5642测试例程的编写主要包括以下几个部分,必须严格按照该顺序来编写程序。
(1) 打开视频设备。
视频设备在linux的/dev目录中,我们这里只有一个视频设备,所以是/dev/video0,使用open函数打开。
static int ov5642 = -1; ov5642 = open ("/dev/video0", O_RDWR, 0); |
(2)设置vpif channel 0的输入。
由于vpif的channel 0可以有三个输入:tvp5150、tvp7002和ov5642,所以需要选通对应的输入模式。
unsigned int input = 1; // 0: tvp5150; 1: 0v5642. defined in board-dm646x-evm.c ioctl (ov5642, VIDIOC_S_INPUT, &input); |
(3)设置视频采集格式。
现在ov5642只支持VGA格式。
vid_std_id = 0x10000000; // V4L2_STD_CAMERA_VGA ioctl (ov5642, VIDIOC_S_STD, &vid_std_id); |
(4)设置视频的像素点格式。
现在ov5642驱动程序只支持YUYV格式的像素点格式,分辨率为VGA,即640 X 480,逐行扫描,每行640 X2 X 2个字节(因为ov5642输出10-bit视频数据)。
memset(&fmt, 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = 640; fmt.fmt.pix.height = 480; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; fmt.fmt.pix.field = V4L2_FIELD_NONE; fmt.fmt.pix.bytesperline = 640* 2 * 2; fmt.fmt.pix.sizeimage = 640 * 480 * 2 * 2; ioctl(ov5642, VIDIOC_S_FMT, &fmt); |
(5)申请缓冲区。
使能视频输出之前需要申请缓冲区用来存储视频数据。
memset (&req, 0, sizeof(req)); req.count = num_bufs; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; ioctl (ov5642, VIDIOC_REQBUFS, &req); |
(6)查询缓冲区。
申请缓冲区之后,需要查询缓冲区,设置每个缓冲区的地址。
buf_ptr = calloc(req.count, sizeof(*buf_ptr)); assert(buf_ptr != NULL); for (i = 0; i < req.count; i++) { memset (&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; ioctl (ov5642, VIDIOC_QUERYBUF, &buf)) < 0); buf_ptr[i].length = buf.length; buf_ptr[i].start = mmap (NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, ov5642, buf.m.offset); } |
(7)将申请的缓冲区放到V4L2视频缓冲队列。
接下来需要将申请的缓冲区放到V4L2缓冲队列,让驱动程序将buffer填满。
for (i = 0; i < req.count; i++) { memset (&qbuf, 0, sizeof(qbuf)); qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; qbuf.memory = V4L2_MEMORY_MMAP; qbuf.index = i; ioctl (ov5642, VIDIOC_QBUF, &qbuf); } |
(8)使能视频设备输出视频流。
在完成所有初始化配置之后,就可以使能视频设备输出视频流了。
ioctl (ov5642, VIDIOC_STREAMON, &buf_type); |
(9)获取视频帧。
要获取视频帧,需要使用VIDIOC_DQBUF这个ioctl函数。获得数据之后,将其存储到文件中,方便后期处理现实图像。
if ((vid_file = fopen("test.264", "wb")) == NULL) printf ("Open file test.264 failed.\n"); else printf ("Open file test.264 succeed!\n");
for (i = 0; i < 1; i++) { memset (&buf, 0 ,sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if ((err = ioctl (ov5642, VIDIOC_DQBUF, &buf)) < 0) printf ("IOCTL dqbuf failed. error code:%d\n", err); else printf ("IOCTL dqbuf succeed!\n"); fwrite(buf_ptr[buf.index].start, buf.bytesused, 1, vid_file); if ((err = ioctl (ov5642, VIDIOC_QBUF, &buf)) < 0) printf ("IOCTL qbuf failed. error code:%d\n", err); else printf ("IOCTL qbuf succeed!\n"); }
fclose(vid_file); |
(10)关闭视频流。
通过VIDIOC_STREAMOFF这个ioctl函数实现。
ioctl (ov5642, VIDIOC_STREAMOFF, &buf_type); |
(11)释放内存。
for (i = 0; i < req.count; i++) { if ((err = munmap(buf_ptr[i].start, buf_ptr[i].length)) < 0) printf ("munmap[%d] failed. error code:%d\n", i, err); else { buf_ptr[i].start = NULL; printf ("munmap[%d] succeed!\n", i); } } free (buf_ptr); close (ov5642); |
2 调试中遇到的问题
以下是调试过程中的一些问题,由于调试时没有及时记录,很多错误都忘了怎么调好的了。
(1) 内核启动时初始化ov5642失败。
编写完驱动程序后,编译内核,下载到开发板上运行,得到下面的输出提示信息。
经过查看代码,发现在ov5642_probe()函数中对ov5642的两个ID寄存器进行了访问,而在系统上电后默认是没有使能ov5642的,所以访问肯定失败,所以,在ov5642驱动程序中注释掉相关代码就好了。
(2)运行ov5642测试例程时无法访问I2C寄存器。
在测试ov5642例程时,发现仍然无法访问其I2C寄存器。
经过检查代码,发现是在vpif_streamon()函数中还没有通过CPLD使能ov5642就对其进行了访问,这个问题和第一个类似,解决办法就是在该函数中先使能ov5642.
(3)图片纯绿色。
测试例程获得的图像为纯绿色,如下所示。
通过使用ultraedit查看文件数据,发现所有数据都为0。后来,发现是vpif寄存器配置错误,通过多次修改之后,成功获取了视频图像。
(4)图像偏绿。
在修改好vpif寄存器之后,能够成功获取图像,但是必须运行两次程序,获得的图像偏绿,很不清楚。
可以看出,图像中有轮廓,但是整体颜色不对。后来在网线发现有人说这可能是因为多次配置ov5642造成的,所以在ov5642驱动程序中添加了一个变量用以记录是否已经进行了I2C配置。当系统第一次配置ov5642,将该变量置1,如果程序要再次配置,只要该变量为1,就不会产生重复配置。修改好之后再次运行程序,图像颜色正常,如下图所示。
(5)经常无法获取视频帧。
ov5642测试例程在运行时经常是多次运行后有一次能够成功获取数据,其他都是失败。
root@dm6467t-evm:/opt/dvsdk/dm6467# ./ov5642 Open /dev/video0 succeed! IOCTL query cap succeed! VIDIOC_QUERYCAP Capability:0x04000001 IOCTL set input succeed! IOCTL get input succeed! VIDIOC_G_INPUT:1 IOCTL query standard succeed! VIDIOC_QUERYSTD:0x100000Configure CMOS mode 00 VPIF_CH0_CTRL:0x91e05485ed!
IOCTL get standard succeed! VIVPIF_CH1_CTRL:0x80000485 DIOC_G_STD:0x10000000 IOCTL setVPIF_CH2_CTRL:0x00000000 format succeed! IOCTL request VPIF_CH3_CTRL:0x00000000 buf succeed! IOCTL querybuf[0] VPIF_INTEN:0x00000013 succeed! IOCTL querybuf[1] succVPIF_INTEN_SET:0x00000013 eed! IOCTL querybuf[2] succeed!VPIF_INTEN_CLR:0x00000000
IOCTL qbuf[0] succeed! IOCTL VPIF_STATUS:0x00000003 qbuf[1] succeed! IOCTL qbuf[2] VPIF_REQ_SIZE:0x00000080 succeed! IOCTL set stream on succeed! Open file test.264 succeed! IOCTL dqbuf failed. error code:-1 IOCTL qbuf failed. error code:-1 IOCTL set stream off succeed! munmap[0] succeed! munmap[1] succeed! munmap[2] succeed! |
经过很长时间的调试,发现其实是因为在打开ov5642设备时使用了一个O_NONBLOCK参数,该参数表示不阻塞。也就是说,在ov5642测试例程试图获取视频帧时,如果当时缓冲区里没有数据,它就直接返回了,并提供错误信息。所以,修改方法就是将该参数去掉,之后再运行测试例程就一直都不会出现上面那样的错误提示信息了。
(6)首次运行例程总是失败。
到现在为止,每次系统上点之后第一次运行ov5642测试例程都会获得错误的视频帧,但之后不管运行多少次都是正常的,这个问题还没有解决。