为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

Davinci视频采集驱动文档

2010-12-27 15页 doc 264KB 14阅读

用户头像

is_339999

暂无简介

举报
Davinci视频采集驱动文档Davinci视频采集驱动文档 概述 Davinci的视频采集接口的驱动涉及到内容包括I2C,AD芯片,V4L2,视频采集等内容。下面主要分成视频采集接口描述,I2C和A/D芯片,V4L2采集驱动以及V4L2应用程序编程。 名词解释: A-low: YUV: 在现代彩色电视系统中,通常采用三管彩色摄像机或彩色CCD摄像机进行摄像,然后把摄得的彩色图像信号经分色、分别放大校正后得到RGB,再经过矩阵变换电路得到亮度信号Y和两个色差信号R-Y(即U)、B-Y(即V),最后发送端将亮度和色差三个信号分别进行编码,用同一信道发送出去。...
Davinci视频采集驱动文档
Davinci视频采集驱动文档 概述 Davinci的视频采集接口的驱动涉及到内容包括I2C,AD芯片,V4L2,视频采集等内容。下面主要分成视频采集接口描述,I2C和A/D芯片,V4L2采集驱动以及V4L2应用程序编程。 名词解释: A-low: YUV: 在现代彩色电视系统中,通常采用三管彩色摄像机或彩色CCD摄像机进行摄像,然后把摄得的彩色图像信号经分色、分别放大校正后得到RGB,再经过矩阵变换电路得到亮度信号Y和两个色差信号R-Y(即U)、B-Y(即V),最后发送端将亮度和色差三个信号分别进行编码,用同一信道发送出去。这种色彩的表示方法就是所谓的YUV色彩空间表示。 Auto focus: Auto white balance: ut Auto exposure: ITU-R BT.656: BT.656并行数据结构: BT.656并行接口除了传输4:2:2的YCbCr视频数据流外,还有行、列同步所用的控制信号。如图所示,一帧图像数据由一个625行、每行1 728字节的数据块组成。其中,23~311行是偶数场视频数据,336~624行是奇数场视频数据,其余为垂直控制信号。 BT.656每行的数据结构如图所示。 图中,每行数据包含水平控制信号和YCbCr--视频数据信号。视频数据信号排列顺序为Cb-Y-Cr-Y。每行开始的288字节为行控制信号,开始的4字节为EAV信号(有效视频结束),紧接着280个固定填充数据,最后是4字节的SAV信号(有效视频起始)。 SAV和EAV信号有3字节的前导:FF、FF、00;最后1字节XY表示该行位于整个数据帧的位置及如何区分SAV、EAV。XY字节各比特位含义见图。 图中,最高位bit7为固定数据1;F=0表示偶数场,F=1表示奇数场;V=0表示该行为有效视频数据,V=1表示该行没有有效视频数据;H=0表示为SAV信号,H=1表示为EAV信号;P3~P0为保护信号,由F、V、H信号计算生成;P3=V异或H;P2=F异或H;P1=F异或V;P0=F异或V异或H。 CCIR 656: REC656: Optical black clamp: Low-Pass Filter: Culling: CFA: Dark Fram write: SDTV/LDTV/HDTV:数字电视(Digital TV)包括数字HDTV、数字SDTV和数字LDTV三种。三者区别主要在于图像质量和信道传输所占带宽的不同。从视觉效果来看,数字 HDTV(1000线以上)为高清晰度电视(High Definition Television)的简称,图象质量可达到或接近35mm宽银幕电影的水平;SDTV(500-600线)即标准清晰度电视,主要是对应现有电视的分辨率量级,其图象质量为演播室水平;LDTV(200-300线)即普通清晰度电视,主要是对应现有VCD的分辨率量级。因为电视全数字化是今后的趋势,所以目前提HDTV以及SDTV、LDTV如无特别说明,均指全数字体制。 一 视频采集接口VPFE Davinci芯片提供一个视频采集接口VPFE主要可以接CMOS/CCD/video decoder等,还有一个视频后端处理接口VPBE主要是接视频输出设备。这里主要讨论视频采集接口VPFE。VPFE接口的结构框图如下图所示: 涉及的模块主要有: ​ CCDC控制器 ​ Preview 预览引擎 ​ Resizer模块 ​ H3A模块 ​ Histogram模块 1.​ CCDC控制器 CCDC控制器主要从CMOS/CCD中接收原始的视频数据,并且可以支持多种YUV视频格式。 2.​ preview预览引擎 预览引擎主要是传输从CMOS/CCD中原始的视频数据到YCbCr 422的显示设备或者编码器。通常预览引擎的数据输出到外部的显示/压缩设备如NTSC/PAL模拟编码器或者LCD上。 3. Resize模块 Resizer模块可以对图像进行裁剪和缩放功能。 二 I2C和A/D芯片 Davinci内置了I2C控制器和I2C总线,一般视频前端处理的A/D芯片都是挂载在I2C总线上,通过Davinci的I2C控制器对A/D芯片的I2C从设备进行读写操作。 Davinci的I2C控制器的内部框图如下图所示: 这里I2C只有2根信号线:SCL和SDA。SCL信号线产生clock时钟,SDA数据线通过内部的ICXSR/ICDXR和ICRSR/ICDRR发生和接收数据。 对于I2C在发送和接收数据的时候会产生START位和STOP位。当SCL为高时,SDA由高变低的时候产生START位;当SCL为低时,SDA由低变高的时候产生STOP位。另外I2C支持的数据格式有:7-bit地址模式,10-bit地址模式和Free data格式模式。 I2C外设可以产生下面几种中断事件: I2C中断 发生事件 丢失仲裁中断AL 当I2C仲裁丢失或者非法的START/STOP位发生 无应答中断NACK 当I2C从接收器中没有接收到应答信号 寄存器可以访问中断ARDY 当先前的编程地址,数据和命令已经执行和状态位已经更新了,I2C产生ARDY中断。这个中断让CPU知道I2C寄存器已经可以访问了。 接收中断ICRINT,ICRRDY 当ICRSR寄存器接收的数据已经拷贝到ICDRR寄存器中的时候发送接收中断。可以让CPU来查询ICRRDY位来从ICDRR中读接收数据。 发送中断ICXINT和ICXRDY 可以让CPU来查询ICXRDY位来往ICDXR中发送数据。 停止中断SCD 当STOP位发生 AAS中断 当I2C发现它的slave地址或者地址全为0。 2.1 Davinci的I2C控制器驱动 Davinci的I2C控制器驱动包括I2C总线注册,I2C总线读写操作,I2C总线中断处理几个部分,主要代码集中在/driver/i2c/busses/i2c-davinci.c。 把I2C控制器注册到I2C总线上是把davinci的I2C适配器的数据结构注册到linux 内核的I2C子系统中。I2C适配器的数据结构struct i2c_davinci_adap。 static struct i2c_adapter i2c_davinci_adap = { .owner = THIS_MODULE, .name = "DAVINCI I2C adapter", .id = I2C_ALGO_EXP, .algo = &i2c_davinci_algo, .algo_data = NULL, .client_register = NULL, .client_unregister = NULL, }; static int __init i2c_davinci_init(void) { ……………………. init_waitqueue_head(&i2c_davinci_dev.cmd_wait); status = (int)request_region(I2C_BASE, I2C_IOSIZE, MODULE_NAME); i2c_set_adapdata(&i2c_davinci_adap, &i2c_davinci_dev); status = i2c_add_adapter(&i2c_davinci_adap); request_irq(IRQ_I2C, i2c_davinci_isr, 0, "i2c",&i2c_davinci_dev); ………………….. driver_register(&davinci_i2c_driver) platform_device_register(&davinci_i2c_device) return 0; } 1. 初始化等待队列i2c_davinci_dev.cmd_wait 2. 为I2C寄存器组分配地址空间。这里可能有问题???????? 3. 把 davinci的I2C的适配器挂入linux内核的I2C子系统中。 4. 注册I2C中断。 5. 注册linux设备模型。 I2C的读写操作主要的数据结构struct i2c_davinci_alog: static struct i2c_algorithm i2c_davinci_algo = { .name = "DAVINCI I2C algorithm", .id = I2C_ALGO_EXP, .master_xfer = i2c_davinci_xfer, .smbus_xfer = NULL, .slave_send = NULL, .slave_recv = NULL, .algo_control = NULL, .functionality = i2c_davinci_func, }; I2C的主要操作在i2c_davinci_xfer函数中。这个函数主要实现代码片断如下: static int i2c_davinci_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { ………………….. if ((ret = i2c_davinci_wait_for_bb(1, adap)) < 0) return ret; ………………….. for (count = 0; count < num; count++) { i2c_davinci_xfer_msg(adap, &msgs[count],(count == (num - 1))); } ………………….. } 1. 判断I2C是否在忙 2. 通过i2c_davinci_xfer_msg函数来实现。 i2c_davinci_xfer > i2c_davinci_xfer_msg static int 2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) { dev->regs->icsar = msg->addr; -----------------------------------1 if (msg->len == 0) { ------------------------------------------------2 dev->buf = &zero_byte; dev->buf_len = 1; } else { dev->buf = msg->buf; dev->buf_len = msg->len; } dev->regs->iccnt = dev->buf_len; at = dev->regs->icivr; ----------------------------------------------------3 if (msg->flags & I2C_M_RD) -------------------------------------------------------4 dev->regs->icimr |= DAVINCI_I2C_ICIMR_ICRRDY_MASK; else dev->regs->icimr |= DAVINCI_I2C_ICIMR_ICXRDY_MASK; dev->regs->icmdr = flag; --------------------------------------- 5 wait_event_timeout (dev->cmd_wait, dev->cmd_complete, DAVINCI_I2C_TIMEOUT);---6 …………….. } 1. 设置slave地址。 2. 设置数据包的长度。 3. 读ICIVR中断向量寄存器表示清0。 4. 使能接收和发送寄存器。 5. 设置ICMDR寄存器。设置IRS,MST和STT位,具体含义看datasheet。 6. 等待数据读写完成。 当读写的数据完成的时候,会产生中断,进入中断服务例程ISR。 static irqreturn_t 2c_davinci_isr(int this_irq, void *dev_id, struct pt_regs *reg) { while ((stat = dev->regs->icivr) != 0) { ------------------------------------------------1 switch(stat) { case DAVINCI_I2C_ICIVR_INTCODE_RDR: -----------------------------------2 if (dev->buf_len) { *dev->buf++ = dev->regs->icdrr; dev->buf_len--; if (dev->buf_len) { continue; } else { dev->regs->icimr &= ~DAVINCI_I2C_ICIMR_ICRRDY_MASK; } } break; case DAVINCI_I2C_ICIVR_INTCODE_TDR: -----------------------------------3 if (dev->buf_len) { dev->regs->icdxr = *dev->buf++; dev->buf_len--; if (dev->buf_len) continue; else { dev->regs->icimr &= ~DAVINCI_I2C_ICIMR_ICXRDY_MASK; } } break; case DAVINCI_I2C_ICIVR_INTCODE_RAR: --------------------------------------------4 /*i2c_warn("i2c: RAR detected");*/ dev->regs->icstr |= DAVINCI_I2C_ICSTR_ARDY_MASK; i2c_davinci_complete_cmd(dev); break; } } } 1. 当有中断发生时候,icivr中断向量寄存器会告诉我们发生了那个中断。 2. 当接收中断发生的时候,从ICDRR寄存器中取数据,取完后清中断请求。 3. 发送中断处理。 4. 这里的中断说明先前的寄存器读写已经完成。这时候会唤醒等待队列i2c_davinci_dev.cmd_wait. 注意:中断处理程序要在ARDY中断(或者异常中断发生)才会唤醒等待队列。 2.2 ADV7180芯片驱动 ADV7180芯片的驱动主要是通过I2C来操作ADV7180本身的寄存器来完成工作的。首先需要把ADV7180做为I2C的从设备注册到I2C总线上。 ADV7180芯片寄存器的读写都是通过I2C来操作的。I2C读写操作如下图所示: 对于写操作,开始位START后,先写入7bit slave地址和一个LSB位,在ACK答应位之后,写入SUB地址和数值,止到STOP位。也就是一个START位后,写入地址和n个数据止到STOP位。 对于读操作,开始位START后,先写入7bit slave地址和一个LSB位,在ACK应答位之后,写入SUB ADDR和等待一个应答位。在开始另个START位之后,才是读出的数据。 static int i2c_write_reg(struct i2c_client *client, u8 reg, u8 val) { int err = 0; struct i2c_msg msg[1]; unsigned char data[2]; if (!client->adapter) { err = -ENODEV; } else { msg->addr = client->addr; msg->flags = 0; msg->len = 2; //这里长度是2,即1个START位,发送2个数据,一个是reg,另一个是val。 msg->buf = data; data[0] = reg; data[1] = val; err = i2c_transfer(client->adapter, msg, 1); } return err; } static int i2c_read_reg(struct i2c_client *client, u8 reg, u8 * val) { int err = 0; struct i2c_msg msg[1]; unsigned char data[1]; if (!client->adapter) { err = -ENODEV; } else { msg->addr = client->addr; msg->flags = 0; msg->len = 1; msg->buf = data; data[0] = reg; err = i2c_transfer(client->adapter, msg, 1); if (err >= 0) { msg->flags = I2C_M_RD; err = i2c_transfer(client->adapter, msg, 1); if (err >= 0) { *val = data[0]; } } } return err; } 三 V4L2采集驱动 对于视频采集,为了让上层应用程序能兼容很多不同的采集卡或者摄像头等设备,内核提供了一组同一的标准接口API,即V4L接口。应用程序只需要按照V4L接口标准来编写程序,那么就可以支持所以采用V4L驱动的设备。V4L是一套针对视频采集,音频radio等音视频设备的标准API。 在V4L的基础上,从Linux2.5内核开始发展了V4L2标准。V4L2的可扩展性和灵活性都得到大大的提高,并且能够支持更多的设备。V4L2对VL4进行了彻底的改造,很多关键的API发生了变化,所以V4L2和V4L不兼容。 Davinci的视频采集驱动采用V4L2 API来编写驱动。V4L2 API支持3种采集的方法:read/write, MMAP和USER POINTERS。我们的驱动只支持mmap的方式。 V4L2 API除了传统的字符设备的方法集外,还有很多的操作是特定的ioctl操作来实现的。对于mmap采集来说,比较常用的几个ioctl操作如下: Ioctl操作 用法 VIDIOC_QUERYCAP 查询设备参数 VIDIOC_CROPCAR 设备图像裁剪和缩放的能力 VIDIOC_S_CROP 获取或者设置当前裁剪方框的大小 VIDIOC_REQBUFS 初始化mmap映射的内存 VIDIOC_QUERYBUFS 内存初始化后查询buffer的状态 VIDIOC_QBUFS 把buffer挂入驱动incoming队列 VIDIOC_DQBUFS 从驱动outcoming队列中取buffer VIDIOC_STREAMON 开始capture VIDIOC_STREAMOFF 结束capture 1.​ VIDIOC_REQBUFS初始化buffer 这个ioctl操作主要目的是初始化mmap需要用到的buffers。V4L2为了简化驱动编程,抽象了一个buffer的管理buffer的文件/driver/video_buffer.c 文件中。这里会调用到video_buffer.c文件中相关函数来完成buffers的初始化。 ​ 初始化1个video_queue->stream队列。 ​ 初始化1个vpfe->dma_queue队列。 ​ 通过__get_free_pages来配置buffers,这里要分配几个buffer,可以由用户程序来控制。 ​ 分配struct videobuf_buffer,n个地址空间。 2.​ Mmap操作 这里mmap操作调用video-buffer.c文件中video_mmap_mapper()函数来实现。对于mmap操作,驱动程序需要为映射的地址范围建立合适的页表,这里主要通过 remap_pfn_range和nopage操作来实现。 3.​ VIDIOC_QBUF 这个ioctl操作主要实现初始化的时候把空buffers挂入queue->strream队列中和vpfe->dma_queue队列中;当应用程序读了1个buffer数据之后,把这个buffer归还。 4.​ VIDIOC_STEAMON: 这个ioctl操作主要是开始capture操作。 ​ 首先调用videobuf_streamon()函数。遍历queue->stream队列,找到组成队列的大结构struct videobuf_buffer。 ​ 从vpfe->dma_queue队列中取出队列成员vpfe->nextFrm = vpfe->curFrm。 ​ 设置vpfe->curFrm->state = STATE_ACTIVE。 ​ 设置AD芯片和配置CCDC控制 ​ 设置CCDC的输入地址为vpfe->curFrm->boff ​ 使能CCDC控制器。 5.​ VIDIOC_DQBUF: 应用程序通过这个ioctl调用来获取那个buffer数据已经准备好了,从驱动返回v4l2_buffer->index值。 ​ 从queue->stream队列中取一个video_buffer成员。 ​ 通过videobuf_waiton函数进行阻塞等待。 ​ 中断处理之后,把这个video_buffer成员从队列中删除,然后把video_buffer->index返回用户空间。 6.​ 中断处理 中断处理要注意“场“的概念。因为视频输入源的摄像头有分PROGRESSIVE和INTERLACED之分。PROGRESSIVE的摄像头一帧只有一场(field),而INTERLACED的摄像头一帧有2场,其中field=1即偶场,先执行,然后才是field=0的奇场,当奇场完成是需要唤醒等待队列,然后VIDIOC_DQBUF调用返回参数完成数据的读取。 下面是INTERLACED的摄像头的中断处理过程: if (fid == 1) { if (!list_empty(&vpfe->dma_queue) && vpfe->curFrm == vpfe->nextFrm) { vpfe->nextFrm = list_entry(vpfe->dma_queue.next, --------------------1 struct videobuf_buffer, queue); list_del(&vpfe->nextFrm->queue); vpfe->nextFrm->state = STATE_ACTIVE; ccdc_setfbaddr( (unsigned long)vpfe->nextFrm->boff); } if (fid == 0) { -------------------------------------------------2 if (vpfe->curFrm != vpfe->nextFrm) { vpfe->curFrm->state = STATE_DONE; wake_up_interruptible(&vpfe->curFrm->done); vpfe->curFrm = vpfe->nextFrm; } 1.​ 一帧数据,一般先是field=1的偶场先完成。在偶场中,取出下一帧buffer,并设置下一帧的ccdc输出地址。 2.​ 接着是field=0的奇场完成,这时候把当前buffer的状态标记为STATE_DONE,并唤醒等待队列,这时候VIDIOC_DQBUF调用返回v4l2-buffer->index参数,用户程序就可以读取数据了。最后把下一帧设置成当前帧。 四.V4L2应用程序编程 V4L2应用程序编程主要还是需要按照V4L2 API来完成。 1.​ 设备初始化 if (-1 == ioctl (fd, VIDIOC_QUERYCAP, &cap)) { if (EINVAL == errno) { fprintf (stderr, "%s is no V4L2 device\n", dev_name); exit (EXIT_FAILURE); } else { errno_exit ("VIDIOC_QUERYCAP"); } } if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { fprintf (stderr, "%s is no video capture device\n", dev_name); exit (EXIT_FAILURE); } if (!(cap.capabilities & V4L2_CAP_STREAMING)) { fprintf (stderr, "%s does not support streaming i/o\n", dev_name); exit (EXIT_FAILURE); } fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = 765 fmt.fmt.pix.height = 576 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) errno_exit ("VIDIOC_S_FMT"); 2.​ buffer请求和mmap系统调用 req.count = 4; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) { if (EINVAL == errno) { fprintf (stderr, "%s does not support " "memory mapping\n", dev_name); exit (EXIT_FAILURE); } else { errno_exit ("VIDIOC_REQBUFS"); } } if (req.count < 2) { fprintf (stderr, "Insufficient buffer memory on %s\n", dev_name); exit (EXIT_FAILURE); } buffers = calloc (req.count, sizeof (*buffers)); if (!buffers) { fprintf (stderr, "Out of memory\n"); exit (EXIT_FAILURE); } for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { struct v4l2_buffer buf; CLEAR (buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = n_buffers; if (-1 == xioctl (fd, VIDIOC_QUERYBUF, &buf)) errno_exit ("VIDIOC_QUERYBUF");//buf.m.offset buffers[n_buffers].length = buf.length; buffers[n_buffers].start = mmap (NULL /* start anywhere */, buf.length, PROT_READ | PROT_WRITE /* required */, MAP_SHARED /* recommended */, fd, buf.m.offset); if (MAP_FAILED == buffers[n_buffers].start) errno_exit ("mmap"); } 3.​ select系统调用来查询buffer是否可读 fd_set fds; struct timeval tv; int r; FD_ZERO (&fds); FD_SET (fd, &fds); /* Timeout. */ tv.tv_sec = 2; tv.tv_usec = 0; r = select (fd + 1, &fds, NULL, NULL, &tv); if (-1 == r) { if (EINTR == errno) continue; errno_exit ("select"); 4.​ 读数据 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) { exit(); } assert (buf.index < n_buffers); process_image (buffers[buf.index].start); if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)) errno_exit ("VIDIOC_QBUF"); 注意:这里的VIDIOC_DQBUF调用主要是从驱动中返回v4l2_buffer->index这个参数,有了这个参数就知道那个buffer可以读了。通过process_image读出数据。数据读完之后,需要调用VIDIOC_QBUF来把buffer返回给驱动。
/
本文档为【Davinci视频采集驱动文档】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索