基于Linux操作系统的视频采集卡驱动程序设计

发布者:来来去去来来最新更新时间:2012-05-21 来源: 微计算机信息 关键字:Linux  v4l2  视频采集  驱动程序 手机看文章 扫描二维码
随时随地手机看文章

1 引言

Linux 操作系统以其本身强大的性能、卓越的稳定性和开放源代码的优点正在得到越来越广泛的应用。设备驱动程序在linux内核中扮演着特殊的角色,它们是一个个独立的“黑盒子”,使得特定的硬件响应一个定义良好的内部编程接口,这些接口完全隐藏了设备的工作细节。用户的操作通过一组标准化的调用执行,而这些调用独立于特定的驱动程序。将这些调用映射到作用于实际硬件的设备特有操作上,则是驱动程序的任务[1]。

本文撰写的背景是源于我们自己开发的CDMA无线视频传输系统,该传输系统的视频采集模块使用Philip SAA7146+ SAA7111a,本文主要介绍linux环境下视频采集设备驱动程序的编写。

2 采集芯片简介[2][3]

开发驱动程序的第一步就是详细了解硬件设备的规格,这样才能具体操纵硬件,实现硬件特定的操作,因此首先介绍一下Philip SAA7146及7111A芯片。

Philip SAA7146是PCI总线控制设备,它负责初始化并处理PCI总线上的数据传输,在这个意义上说它是主设备;而7111a是从设备,它只是所谓的视频解码器,负责将模拟视频信号解码为数字比特流。对于后者,只需要通过I2C总线进行编程,而不必直接控制该设备。它并不具有总线控制能力,因此不能在PCI 总线上找到它。因此7111a并不需要专门的驱动程序,它是通过主控芯片(SAA7146)经由I2C总线来驱动的,我们只需要编写主控芯片的驱动程序即可。

3 V4L规范[4]

V4L与V4L2是Linux下开发视频采集设备驱动程序的一套规范,这套规范使用分层的方法给驱动程序的开发提供了清晰的模型和一致的接口。应用程序处于最上层,V4L或V4L2处于中间层,而实际的硬件设备处于下层。

3.1 V4L

V4L是Video for Linux的简写,它是Alan Cox为了给Linux下视频采集设备驱动程序的编写提供统一的接口而提出的一套规范(API),它将所有的视频采集设备的驱动程序都纳入它的管理之中,给驱动程序编写者带来极大的方便。

3.2 V4L2

鉴于V4L的种种不足,Bill Dirks重新设计了一套API和数据结构,并把它称作Video for Linux Two(V4L2)。与V4L相比,它的扩展性和灵活性都得到了极大的提高,并且支持的硬件设备也更多。但是也由于它对V4L做了彻底的改造,使得它与 V4L并不兼容。

V4L2是一个两层驱动结构:上层是videodev模块,当videodev初始化后,它把自己注册一个主设备号为81的字符设备,同时注册自己的字符驱动成员函数;下层是V4L2驱动程序,它实际上是videodev的客户端,videodev通过V4L2驱动程序的成员函数来调用V4L2驱动程序。当V4L2驱动程序初始化后,它把一个包含V4L2 驱动程序成员函数,次设备号以及其他相关信息的结构传递给videodev,从而把它要处理的设备注册到videodev。当应用程序触发了一个驱动程序调用时,控制权首先传递给videodev中的函数,videodev负责将应用程序传递的文件或i节点结构指针转化为相应V4L2结构的指针,并调用 V4L2驱动中的处理函数。

当V4L2驱动程序初始化时候,它首先会枚举它将处理的系统中的设备,然后为每个设备填充struct v4l2_device结构,并把指向该结构的指针传递给v4l2_register _device ()函数,该函数调用v4l2_device结构中的初始化函数对设备进行初始化。

Struct v4l2_device结构中的主要域说明如下:

Char name[32]:设备的名字,该名字会出现在/proc/Videodev文件中;

Int type:V4L2设备类型;

Int minor:设备得次设备号;

Int(*open)():当打开新的文件描述符时调用;

Int(*close)():当关闭文件描述符时调用;

Int(*read)():调用read();

Int(*write)():调用write();

Int(*ioctl)():调用ioctl();

Int(*mmap)():调用mmap();

Int(*poll)():调用select();

Int(*initialize)():当设备注册时调用;

Int busy:设备的打开计数,由videodev维护;

设备通过函数v4l2_unregister_device()取消注册;V4L2允许设备多次打开,上面的v4l2_device结构中的成员函数都具有一个id参数,该参数可以把设备的多次打开区分开来。

4 采集卡驱动程序的基本结构及实现

4.1 驱动程序基本结构

   整个驱动程序分为三层:

模块Saa7146_v4l2直接操作硬件设备,它主要提供基于SAA7146芯片的采集设备的核心功能,也可将其称作核心驱动程序;同时驱动程序还提供了扩展机制,用于扩充核心驱动程序的功能,这样做的好处就是可以在extension模块当中实现自己想要的附加功能,而不用修改核心部分。显然,该扩展模块也对硬件设备具有完全的控制权。[page]

4.2 驱动程序的实现

下面讨论视频采集卡驱动程序的具体实现。

4.2.1 PCI驱动程序

本文采用的视频采集卡是基于PCI总线的,因此在这里首先讨论PCI驱动程序的结构。

PCI设备是无跳线设备,可在引导阶段自动配置。这样设备驱动程序必须能够访问设备中的配置信息以便完成初始化。对于PCI设备来说,这些工作无需探测就可以完成[1][5]。

所有的PCI设备都有至少256字节的地址空间,前64字节是标准化的,其余的是设备相关的。这个空间也叫做PCI配置空间,它包含厂商标识,设备标识,设备类别,基地址寄存器,中断引脚等等,编写PCI的驱动程序就是利用这些域与设备进行“沟通”。

尽管PCI设备千差万别,但是控制他们的结构基本是类似的。

1)为了正确注册到内核,所有的PCI驱动程序需要创建pci_driver结构体,该结构体由许多回调函数和变量组成。初始化该结构体如下:

struct pci_driver saa7146_v4l2_driver = {

    name:       "saa7146 v4l2",

    id_table:   saa7146_v4l2_pci_tbl,

    probe:      saa7146_v4l2_init_one,

    remove: saa7146_v4l2_remove_one,

    suspend:    saa7146_v4l2_suspend,

    resume: saa7146_v4l2_resume,

};

为了把struct pci_driver注册到PCI核心,需要调用以其为参数的pci_module_init函数:

pci_module_init(&saa7146_v4l2_driver);

2)如果上述函数返回为0,表示初始化成功,此时在驱动程序可以访问PCI设备的任何设备资源之前(I/O区域或中断),驱动程序必须调用pci_enable_device函数:

int pci_enable_device(struct pci_dev *dev);

3)上述函数将激活设备,此时驱动程序就需要读取或写入三个地址空间:内存,I/O端口,配置空间。Linux把I/O空间和内存空间都看作是系统的资源,使用前必须申请,即在系统中进行登记,避免资源使用的混乱。简述如下:

首先调用pci_resource_start()和pci_resource_len()函数获取资源信息,然后调用 request_mem_region()函数分配I/O内存区域,为了确保该内存对内核而言是可访问的,必须还要建立映射,映射的建立由 ioremap()函数完成,这样设备驱动程序就可以访问任意的I/O内存地址。

4)与设备通信,即通过访问I/O内存的函数,诸如writel(),readl()等;以及进行I2C操作的函数对SAA7111a进行初始化。

4.2.2 驱动模块的设计与实现

采集卡驱动程序主要由saa7146_v4l2,v4l2_extension,saa7111a三个模块构成,这三个模块都要调用i2c- core模块(Kernel提供)中的函数,即它们依赖于i2c-core模块;saa7111a模块具体负责通过I2C控制SAA7111a芯片,v4l2_extension模块依赖于saa7146_v4l2模块,它把自己注册到saa7146核心模块中。

重要数据结构声明

采集设备的扩展部分结构:

struct saa7146_v4l2_extension{

char    name[64];       /* 设备名 */

struct saa7146_v4l2_extension_ioctls* ioctls;

u32 irq_mask;   /* 扩展部分处理的IRQ */

void    (*irq_func)(struct capture_device*, u32* irq_mask);

/* 扩展部分的函数主要就是ioctl()用于实现各种类型硬件控制 */

int (*ioctl)(void *id, unsigned int cmd, void *arg);

};

采集设备核心结构:

struct capture_device{

struct v4l2_device      v4l2;  

struct v4l2_capability      capability;

struct pci_dev          *pci_device;

……

}[page]

DMA结构:

struct  saa7146_video_dma {

    u32 base_odd;

    u32 base_even;

    u32 prot_addr;

    u32 pitch;

    u32 base_page;

    u32 num_line_byte;

};

 

工作流程

v4l2_extension调用v4l2_register_device()函数注册设备,V4l2_register_device()函数进而调用v4l2_init_done()函数(v4l2_device结构中的int(*initialize)()字段已被初始化为该函数)通过写 I/O地址空间具体的初始化设备,设置采集图像的默认参数等。这时候设备已经做好了采集图像的准备工作。

下面通过典型的read一桢图像来分析具体的工作流程:

应用程序首先调用系统调用open()来打开设备,v4l2将该调用映射为初始化设备时已经设置好的v4l2_device结构中的 int(*open)(),在本文中即为v4l2_open();打开设备成功read一桢图像数据的命令,此时系统通过v4l2_device结构中已经设置好的int(*open)()字段调用相应的函数v4l2_read(),该调用负责分配内核内存缓冲区,并将采集到的数据从内核空间复制到用户空间,这样应用程序就获得了一桢数据;

当 v4l2_capability结构中的V4L2_CAP_STREAMING标志被设置时,这表明设备支持流采集。V4L2 的流驱动程序维护两个组织成FIFO的缓冲区队列:发送队列和接收队列。由于应用程序受到网络延迟,进程抢占或随机磁盘存储的影响,维护两个队列就可以把异步的视频采集或输出操作与应用程序分离开,从而降低丢失数据的可能性。设备采集到图像后可以用DMA 方式直接将数据放入应用程序分配好的缓冲区中,这就大大提升了整个系统的性能。

4.2.3 测试驱动程序[6]

首先编译上述模块,然后通过命令insmod链接进内核。用于测试的简单应用程序主体部分如下所示:

vid = open(device, O_RDONLY);/*打开设备*/

err = ioctl(vid, VIDIOC_QUERYCAP, &cap); /*查询设备支持的功能*/

err = ioctl(vid, VIDIOC_G_FMT, &fmt);/*设置采集图像的格式*/

data = malloc(fmt.fmt.pix.sizeimage);/*分配用户空间缓冲区*/

n = read(vid, data, fmt.fmt.pix.sizeimage);/*获取一桢数据*/

该应用程序运行后经检查得到了预期的结果,并且在基于该驱动程序的CDMA无线视频传输系统中满足了应用的需要,获得了理想的效果。

5 结论

本文作者创新点:详细阐述了Linux环境下利用V4L2API开发视频采集设备驱动程序的流程,并将该驱动程序实际的应用到我们自己研发的CDMA无线视频传输系统中,获得了满意的实时效果,在此也希望对从事同类开发的人员有所裨益。

参考文献

1 Alessandro Rubini & Jonathan Corbet,Linux device driver,2nd Edition,O’Reilly,2001.7

2   Philip SAA7146A datasheet,1998.4

3   Philip SAA7111A datasheet,1998.5

4   http://linux.bytesex.org/v4l2/

5   PCI SIG,PCI Local Bus Specification Revision 2.2,1998.12

6   王多智,嵌入式linux下sram驱动程序的开发原理及应用,微计算机信息,2005年第5期

关键字:Linux  v4l2  视频采集  驱动程序 引用地址:基于Linux操作系统的视频采集卡驱动程序设计

上一篇:基于嵌入式WEB的AllLightSYS系统的设计与实现
下一篇:基于ETX与VxWorks的嵌入式车辆导航系统的设计

推荐阅读最新更新时间:2024-05-02 22:04

Linux嵌入式系统设计的3个层次
  嵌入式系统设计有3个不同层次:    1. 第1层次:以PCB CAD软件和ICE为主要工具的设计方法。   这是过去直至现在我国单片机应用系统设计人员一直沿用的方法,其步骤是先抽象后具体。   抽象设计主要是根据嵌入式应用系统要实现的功能要求,对系统功能细化,分成若干功能模块,画出系统功能框图,再对功能模块进行硬件和软件功能实现的分配。   具体设计包括硬件设计和软件设计。硬件设计主要是根据性能参数要求对各功能模块所需要使用的元器件进行选择和组合,其选择的基本原则就是市场上可以购买到的性价比最高的通用元器件。必要时,须分别对各个没有把握的部分进行搭试、功能检验和性能测试,从模块到系统找到相对优化的方案,画出电
[嵌入式]
ARM Linux 交叉编译 工具链 制作攻略
制作之前确保你的机子上有如下几个工具:bison flex build-essential。 build-essential主要是用于提供GCC、GLIBC等必要的编译资源,一般做开发的人员机子上应该都会有的。如果没有,对于UBUNTU用 户:sudo apt-get install build-essential bison flex,其它用户自己看着办,! 1、从http://kegel.com/crosstool处下载crosstool-0.43.tar.gz并解开存于$HOME下。 假如你想要arm9tdmi上的工具链(其它工具链方法相同),进入crosstool-0.43目录,用文本编辑器打开d
[单片机]
基于Linux的嵌入式网络摄像机设计
  本嵌入式网络摄像机采用高性能ARM9芯片微处理器,内置嵌入式Web服务器。   通过嵌入式多任务操作系统采集摄像机视频数据;采集的视频信号数字化后经MJPEG算法压缩,再通过内部总线送到内置的Web服务器;使用者可以直接用浏览器观看Web服务器上的摄像机图像;通过通用网关接口CGI,授权用户还可以控制摄像机、云台和镜头的动作或直接通过Web页面对系统进行配置。    引言   基于同轴电缆的视频监控系统结构复杂、稳定性差、可靠性低且价格昂贵,因而出现了嵌入式网络摄像机等远程Web视频监控系统。本嵌入式网络摄像机,采用高性能的ARM9芯片作微处理器,内置嵌入式Web服务器—Boa,通过嵌入式多任务操作系统—Linux采集摄
[安防电子]
2017上海车展创新技术:延锋入门级车载娱乐系统
入门级车载娱乐系统通过Hypervisor集成了Android和Linux双操作系统,实现Android系统支持应用程序下载和应用生态圈,Linux系统负责稳定快速的运行基础应用(如后视系统)。此外,该系统还集成全新手机互联方式——趣驾,可支持驾员车内安全使用手机微信。值得注意的是,该车载娱乐系统实现了一块芯片驱动多个操作系统,确保了系统的开放性、稳定性和安全性。
[汽车电子]
嵌入式系统中CMOS图像传感器接口技术
背 景 目前数字摄像技术,主要采用两种方式:一种是使用CCD(电容耦合器件)图像传感器,另一种是使用CMOS(互补金属氧化物半导体)图像传感器。 CCD图像传感器具有读取噪声低、动态范围大、响应灵敏度高等优点。但CCD技术难以与主流的CMOS技术集成于同一芯片之中。因而CCD图像传感器具有体积大、功耗高等缺点。 CMOS 图像传感器是近些年发展较快的新型图像传感器,由于采用了CMOS技术,可以将像素阵列与外围支持电路(如图像传感器核心、单一时钟、所有的时序逻辑、可编程功能和模数转换器)集成在同一块芯片上。因此与CCD相比,CMOS图像传感器将整个图像系统集成在一块芯片上,具有体积小、重量轻、功耗低、编程方便、易于控制等优点。
[单片机]
嵌入式系统中CMOS图像传感器接口技术
微软面向物联网设备发布定制Linux 内核和发行版
微软首次发布了自己的定制 Linux 内核和发行版。在旧金山举行的新闻发布会上,微软宣布了针对物联网设备的解决方案 Azure Sphere。 Azure Sphere 包含三个组件。其中之一是微软设计的 Sphere MCU,将免费提供给厂商,联发科将在今年晚些时候推出首款产品 MT3620,微软称它的芯片具有 ARM Cortex A 系列芯片设计的通用性和处理能力,同时更小开销更低。Sphere MCU 整合了应用处理器,实时处理器,Flash 存储、内存以及微软的安全模块 Pluton。 另一个组件是 Azure Sphere IoT OS 是基于 Linux 内核,微软称它使用定制内核和受 Windows 启发的
[嵌入式]
S3C2440移植linux3.4.2内核之内核框架介绍及简单修改
uboot启动内核分析 进入cmd_bootm.c,找到对应的bootm命令对应的do_bootm(): int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv ) { boot_os_fn *boot_fn; //boot_fn是个数组函数 ... .. boot_fn(0, argc, argv, &images); //调用数组函数 ... ... } boot_os_fn是个typedef型,如下图所示: 由于定义了宏CONFIG_BOOTM_LINUX,最终会跳转到do_bootm - do_bootm_linu
[单片机]
LCD驱动芯片——BL55072A驱动程序
初始化子程序: START 0x7C; I2C子地址 0xEA;ICSET,软件复位芯片 0xC0;MODSET,关显示 0xF0;BLKCTL,关闪烁 0xA3;DISCTL,80Hz Line inversion,High Power mode 0xE8;ICSET,清复位bit1 STOP 显示刷新子程序: START 0x7C;I2C子地址 0xF0;BLKCTL,关闪烁 0xA3;DISCTL,80Hz Line inversion,High Power mode 0xE8或0xEC;ICSET,显存高位地址为0或1 0xXX;ADSET,设置显存刷新起始地址,通常为0x00,从头开始刷新,此时上一条指令一般为0xE
[单片机]
小广播
最新嵌入式文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved