DM6467的OV5642 Linux驱动程序开发(三)——驱动测试

来源:互联网 发布:vba连接oracle数据库 编辑:程序博客网 时间:2024/04/30 15:17

 


 

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测试例程都会获得错误的视频帧,但之后不管运行多少次都是正常的,这个问题还没有解决。

原创粉丝点击