百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

基于FIMC接口的CMOS摄像头驱动分析与设计

ahcoder 2025-06-15 14:01 2 浏览

摘 要: 目前的嵌入式系统中,USB摄像头使用比较普遍,但其应用会受到传输速度的限制。本文采用一款高速CMOS摄像头,其驱动利用S3C6410内置的FIMC接口技术,采用DMA和ping-pong缓冲池机制,结合内存共享策略,有效提高了传输速率并充分利用了有限的内存资源。深入分析了该驱动的原理和实现细节,并提出了改进设计,最终应用在嵌入式图像采集系统中,能够为应用程序提供高清、高速图像。

0 引言

嵌入式系统具有体积小、功耗低和成本低等天然性的优势,因而得到广泛应用,甚至在许多场合得以取代传统工控机,比如视频监控系统和安防系统。目前嵌入式系统中最常用、嵌入式Linux内核支持最广泛的是USB摄像头。然而受到嵌入式处理器性能的限制,USB摄像头接口的传输速率限制为12 Mb/s,对于常用的640×480分辨率的YUV图像,其最高帧率为3.75帧/s,无法满足实时性要求。因此,基于CMOS图像传感器的高速摄像头正在被推广应用。CMOS高速摄像头可以为嵌入式系统实时地提供高分辨率图像[1],很适合进行识别、跟踪等实时图像处理作业。采集640×480的VGA图像,CMOS摄像头最高帧率可以达到30帧/s。基于OV系列的CMOS摄像头应用很多,比如监控[2]和人数检测[3]都使用了OmniVision公司的OV9650摄像头。参考文献[4]介绍了基于S3C6410和OV9650的V4L2图像采集系统的设计,参考文献[5]和[6]介绍了基于S3C2440中相机接口(Camera Interface,CAMIF)的OV9650摄像头驱动设计。

本文涉及的S3C6410[7]的完全交互式移动相机(Fully Integrated Mobile Camera,FIMC)接口是由S3C2440的CAMIF发展而来,但是目前关于FIMC驱动的原理分析和设计实现的文献仍然很少。本文对OV9650及FIMC接口驱动的原理和实现细节作了深入分析,并对原有驱动进行了改进,使之适用于视线检测系统。

本文首先分析摄像头驱动所依赖的硬件接口,然后重点分析其驱动软件设计原理和实现细节,并给出改进设计,最后对下一步的改进工作提出展望。

1 摄像头驱动系统的硬件接口

本文所涉及的驱动系统基于OK6410嵌入式开发板,采用S3C6410作为中央处理器。S3C6410内置的FIMC接口为开发板与CMOS摄像头的连接提供了可靠便利的接口。该驱动系统的硬件结构如图1所示。

如图1,摄像头为130万像素、20引脚的OV9650,通过FIMC接口接入S3C6410。OV9650与FIMC对应管脚连接如图2所示。其中,CAMYDATA7~CAMYDATA0负责图像数据传输,CAMRSTN为复位信号,CAMSYNC为同步信号。OV9650的配置接口为SCCB接口。由于S3C6410没有专用的SCCB接口,因此使用GPB6、GPB5分别模拟SIO_D、SIO_C,即数据和时钟信号。SCCB协议与I2C协议的区别仅在于设备地址不同,因此,驱动中直接用I2C代替。

2 摄像头驱动系统软件

2.1 Linux驱动模型

在Linux操作系统中,设备驱动为应用程序提供访问接口,屏蔽了底层硬件细节。从Linux2.6内核开始,设备被驱动和内核映射为文件,应用程序可以像访问普通文件一样访问这些挂载在/dev目录下的设备,访问接口被定义在驱动中file_operations结构体对象内。每一个接口函数其实都是一个系统调用,其具体实现由驱动程序完成。Linux内核中驱动模型包括总线(Bus)、设备(Device)和驱动(Driver)三个要素,即设备和驱动作为对象挂载在相同的总线上,由总线对设备和驱动进行一一匹配。

Linux驱动模型将所有外设分为字符设备、块设备和网络设备三种。摄像头属于字符设备,其驱动遵循着字符设备驱动的框架,包括设备号、设备注册和最重要的文件操作函数的实现。特别地,针对摄像头设备,V4L2[8]接口为驱动程序提供了一套完备的文件操作标准接口和缓冲池管理策略,目前大多数的摄像头驱动都遵循V4L2接口标准。

2.2 V4L2驱动

V4L2是Video For Linux 2的简称,是Linux内核中关于视频设备的虚拟驱动,它不涉及硬件,仅仅为应用程序提供一套完备的操作接口,这些接口的具体实现都由遵守V4L2协议的驱动程序来完成,比如常用的ioctl接口。V4L2的存在极大地方便了应用程序的编写,使得同一套应用程序可以应用于多种摄像头。V4L2层次示意图如图3。

基于V4L2的基本图像采集流程如下:

(1)打开视频设备文件(一般为dev/video0),初始化采集格式等参数;

(2)在内核空间申请若干视频采集的帧缓冲区;

(3)地址映射,使得用户空间的应用程序对帧缓冲区有读写权限;

(4)帧缓冲区在视频采集输入队列排队,并启动视频采集;

(5)从缓冲队列取出帧缓冲区,获得数据进行处理;

(6)处理完,将帧缓冲区重新放入视频采集输入队列,循环往复采集连续视频数据。

V4L2为图像采集程序维持一个环形缓冲队列,如图4。该缓冲区队列为ping-pong操作模式,应用程序需要使用的图像数据会通过ioctl接口使对应的缓冲区出队,缓冲队列的其余缓冲区继续接收图像数据,并在一个循环之后覆盖掉原有的缓冲区数据,以保证缓冲区队列中图像数据的实时性。

2.3 关键模块驱动的设计与实现

OV9650通过FIMC接口与S3C6410连接,FIMC为输入图像进行格式转换、剪裁等预处理,最后传输到内核中开辟的图像缓冲区,供应用程序读取。摄像头驱动分为两个部分:OV9650驱动和FIMC驱动。

2.3.1 OV9650驱动

OV9650驱动的主要作用是挂载驱动和配置寄存器,其中.h文件定义了寄存器配置数据,并由.c文件调用。由于FIMC接口的存在,应用程序不需要直接操作OV9650摄像头,因此OV9650驱动不需要为应用程序提供访问接口,不需要定义file_operations结构体。

从Mach-smdk6410.c文件中可以知道,内核在启动时,将OV9650作为一个I2C设备挂载到内核树的I2C总线上。之后内核找到OV9650驱动,执行其入口函数——ov965x_init,将OV9650.c文件中定义的i2c_driver驱动对象也添加到内核树中,最后由总线根据其name将设备和驱动进行匹配。在匹配工作完成之后,内核会调用其探测函数ov965x_probe,将OV9650的配置数据传递给FIMC驱动中的一个全局参数s3c_fimc,用于配置FIMC寄存器,然后,初始化OV9650寄存器。

2.3.2 FIMC驱动

FIMC是s3c6410芯片为摄像头设备提供的一个接口,用来对所采集的图像进行裁剪、放缩等预处理。FIMC为输出图像提供两个DMA通道:preivew通道和codec通道,并为每个通道分配四个ping-pong缓冲区,以提高图像传输速度和内存使用效率。如图5所示。

FIMC接收OV9650图像数据,并向上传递。因此,FIMC驱动最重要的作用就是向应用层提供标准的操作接口,供应用程序使用。

FIMC驱动主要包括三个部分:(1)platform驱动注册;(2)file_operations接口定义;(3)V4L2接口实现。

2.3.2.1 platform驱动注册

platform是Linux 2.6内核所引进的一种新型驱动管理和注册机制。目前Linux内核中大部分的设备驱动都采用platform架构。在platform架构中,设备用platform_device表示,驱动用platform_driver表示。Linux platform driver机制与传统的device driver机制(通过drivce_register函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时通过platform device提供的标准接口进行申请并使用,提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性(这些标准接口是安全的)。

FIMC驱动入口函数是s3c_fimc_core.c中的s3c_fimc_register,该函数将platform_driver类型的s3c_fimc_driver挂载到platform虚拟总线。由mach-smdk6410.c文件可知,内核启动时将所有platform_device(包括s3c_device_fimc0)挂载到platform总线。platform总线的match函数将device和driver匹配之后,会自动调用s3c_fimc_driver中指定的probe函数探测设备、申请内存资源、申请中断等,并将最终形成的platform_device类型数据保存到内核中,供后续使用。最后,调用video_register_device函数将对应的video_device注册到内核。

video_device结构体包括fops、ioctl_ops、release、name、vf_type几个成员变量,其中,最重要的是file_operations类型的fops和v4l2_ioctl_ops类型的ioctl_ops,分别实现文件操作接口和V4L2接口。

2.3.2.2 file_operations接口

FIMC驱动遵循V4L2接口标准,其file_operations接口定义如下:

static const struct v4l2_file_operations s3c_fimc_fops=

{

.owner=THIS_MODULE,

.open=s3c_fimc_open,

.release=s3c_fimc_release,

.unlocked_ioctl=video_ioctl2,

.read=s3c_fimc_read,

.write=s3c_fimc_write,

.mmap=s3c_fimc_mmap,

.poll=s3c_fimc_poll,

};

当应用程序通过系统调用open打开摄像头设备时,内核会最终找到s3c_fimc_open函数来打开摄像头。

应用程序通过read获取图像数据,该函数通过copy_to_user将内核空间所申请缓冲区中图像数据拷贝到用户空间中开辟的图像数据区。该函数没有充分利用FIMC接口提供的ping-pong缓冲区,按字进行拷贝(memcpy),十分耗时。

mmap函数将内核空间中申请到的图像缓冲区映射到应用程序所在的用户空间,这样,应用程序申请到的buffer将指向内核空间的图像缓冲区,应用程序可以(不拷贝)直接对图像进行操作。该函数配合V4L2标准中的环形缓冲区队列,节省了应用程序读取图像数据所消耗的时间。

2.3.2.3 V4L2接口

V4L2接口功能强大,为应用程序提供了完备的操作接口,包括设置格式、帧率、白平衡、曝光模式、申请缓冲区、取数据、剪裁图像等。此处仅列举几个重要接口。

const struct v4l2_ioctl_ops s3c_fimc_v4l2_ops=

{

.vidioc_s_fmt_vid_cap=

s3c_fimc_v4l2_s_fmt_vid_cap,

.vidioc_s_ctrl=s3c_fimc_v4l2_s_ctrl,

.vidioc_streamon=s3c_fimc_v4l2_streamon,

.vidioc_streamoff=s3c_fimc_v4l2_streamoff,

.vidioc_reqbufs=s3c_fimc_v4l2_reqbufs,

.vidioc_querybuf=s3c_fimc_v4l2_querybuf,

.vidioc_qbuf=s3c_fimc_v4l2_qbuf,

.vidioc_dqbuf=s3c_fimc_v4l2_dqbuf,

.vidioc_s_parm=s3c_fimc_v4l2_s_parm,

};

应用程序通过ioctl接口使用这些函数,比如ioctl(fd,VIDIOC_S_FMT,&fmt)用来设置图像格式,此时V4L2会将VIDIOC_S_FMT命令映射为
s3c_fimc_v4l2_s_fmt_vid_cap函数,并将fmt指定的格式告知FIMC接口,FIMC会将OV9650传递过来的原始图像数据经过类型转换传递回应用程序。

s3c_fimc_v4l2_reqbufs用于申请图像缓冲区,该函数为应用程序在内核空间开辟ping-pong缓冲区。s3c_fimc_v4l2_qbuf函数将缓冲区组成环形缓冲队列,当应用程序需要调用图像数据时,使用s3c_fimc_v4l2_dqbuf使指定的缓冲区出队,缓冲区在出队期间,不会被新来的图像数据覆盖,新到的图像数据会被传送到环形队列中指定缓冲区的下一个缓冲区。由于FIMC控制器为P通道和C通道分别开辟了4个缓冲区,在内核初始化时已经申请到,因此FIMC驱动中并不需要再重新申请。

2.4 驱动错误分析与改进设计

在驱动主体框架正确的情况下,开发板(OK6410A)自带的驱动并不能直接使用。同一套V4L2图像采集程序可以应用在USB摄像头上,应用在OV9650上却无法获得图像,并且先后报出两个错误:

(1)tx or ty is lower than zero and this is a invalid target size

(2)VIDIOC_QUERYBUF error

上述错误出现之后,LCD屏幕没有图像,紧接着串口会显示display 0,表示程序无法读出图像数据,导致程序终止。下面分别对两个错误进行分析并改进。

2.4.1 错误1的分析与改进

由于ov9650驱动仅仅将ov9650设备注册进入系统,并没有其他处理,因此ov965x.h和ov965x.c两个文件不用修改和调试。

FIMC采用DMA通道向内核缓冲区传送数据,需要专门的函数打开DMA输出通道。通过函数跟踪得知,原有
s3c_fimc_v4l2_s_fmt_vid_cap函数并没有打开输出DMA通道,因此CMOS摄像头的数据并没有传送到内核开辟的缓冲区中,导致V4L2也无法获取这些图像数据。而且由于没有打开输出DMA通道,out_frame参数也没有设置,导致其对应的尺寸参数也为0,所以报出第一个错误。


s3c_fimc_v4l2_s_fmt_vid_cap中通过调用s3c_fimc_ set_output_frame函数,打开输出DMA通道,解决了该问题。

2.4.2 错误2的分析与改进

该错误由s3c_fimc_v4l2_querybuf函数报出,报错条件是:

if(b->type!=V4L2_BUF_TYPE_VIDEO_OVERLAY&&b

->type!=V4L2_BUF_TYPE_VIDEO_CAPTURE)

该函数用于配置所申请图像缓冲区的长度、偏移量等属性,从而使所申请的环形队列缓冲区生效。由于本系统中摄像头仅用于图像捕获,因此在该函数开头可以将b->type进行强制设定,即:b->type=V4L2_BUF_ TYPE_VIDEO_CAPTURE,从而解决图像缓冲区申请的问题,该错误不再出现。

至此,修正后的FIMC驱动程序可以正常工作,图像数据得以传输到内核中为FIMC驱动开辟的图像缓冲区,应用程序可以通过read系统调用或者ioctl的VIDIOC_DQBUF命令获取图像数据。

图像捕获效果如图6所示。

3 结论

本文深入分析了CMOS摄像头驱动的原理和实现细节。FIMC驱动向内核申请四个ping-pong缓冲区,通过DMA方式传入图像数据,提高了图像数据传输速率。内存共享策略使得应用程序在访问图像缓冲区时免去了内存拷贝的步骤,大大缩减了图像获取的时间。但是目前的FIMC驱动在缓冲区出队和入队时的保护机制仍不够完善,需要在今后的工作当中对这一部分不断进行优化。

参考文献

[1] OminiVision Technologies Inc. OV9650 color CMOS SXGA (1.3MegaPixel)camerachipTM implementation guide[EB/OL].(2004-12-07)[2015-01-31]. http://www.ovt.com.

[2] 胡哲光.基于S3C2440与OV9650的嵌入式监控设计[J].轻工机械,2012,30(2):50-53.

[3] 官志平.基于ARM9的Linux系统移植以及在电梯轿厢内人数检测的应用[D].厦门:厦门大学,2014.

[4] Lu Yinli, Yu Hongli, Zhang Pengpeng. The implementation of embedded image acquisition based on V4L2[C]. Proceedings of the 2011 International Conference on Electronics, Communications and Control (ICECC), 2011:549-552.

[5] Zhang Min, Sun Jinguang, Wang Shi. Research and implementation of the CMOS camera device driver based on S3C2440[C]. Proceedings of the 2010 International Conference on Intelligent Computation Technology and Automation (ICICTA),2010:1039-1042.

[6] Kuang Shunming, He Xiaojian. Included in your digital subscription design and application of CMOS device driver based on S3C2440[C]. Proceedings of the 2011 10th International Conference on Electronic Measurement & Instruments (ICEMI),2011:342-343.

[7] Samsung. S3C6410x RISC microprocessor user′s manual (revision 1.2)[EB/OL].(2009-02-13)[2015-01-31]. http://www.samsung.com.

[8] DIRKS B. Video for Linux two API specification (revision 3.9)[EB/OL].(2012-12-03)[2015-01-31]. http://www.linuxtv.org/downloads/v4l-dvb-apis/v4l2spec.html.

相关推荐

KaOS 2025.05版本发布:全面拥抱Qt6,彻底告别Qt5

KaOSLinux2025.05版本重磅发布:全面拥抱Qt6,开启KDE生态新篇章继2025.03版本发布两个月后,专注于KDE桌面环境、采用XFS文件系统的滚动发行版Li...

基于FIMC接口的CMOS摄像头驱动分析与设计

摘要:目前的嵌入式系统中,USB摄像头使用比较普遍,但其应用会受到传输速度的限制。本文采用一款高速CMOS摄像头,其驱动利用S3C6410内置的FIMC接口技术,采用DMA和ping-pong缓冲...

没错是微软 推出基于Linux的交换机系统

2015-09-2205:59:59作者:郑伟你没看错,为了提升自身Azure云数据中心内网络设备的兼容性及开放性,微软也开始推出基于Linux的网络交换机系统了。这个被称为AzureCloud...

Linus Torvalds 宣布首个 Linux 内核 6.16 候选版本

Linux内核负责人兼创始人LinusTorvalds宣布关闭合并窗口,该窗口用于将主要新功能添加到内核中,并开始发布Linux6.16候选版本,从候选版本1(Linux6.16-r...

Linux内核漏洞将影响Haswell架构服务器

在infoq网站上,GilTene最近报告一个十分重要,但并不为人知Linux内核补丁,特别对采用Haswell架构的Linux系统用户和管理员应该特别关注。报告提醒RedHat发行版的用户(包括...

关于Linux性能调优中网络I/O的一些笔记

写在前面和小伙伴分享一些Linux网络优化的笔记,内容很浅,可以用作入门博文内容结合《Linux性能优化》读书笔记整理涉及内容包括常用的优化工具(mii-tool,ethtool,ifconfig,i...

国产操作系统- Veket Linux(国产操作系统之光银河麒麟阅读理解)

VeketLinux是一个随身的可装在U盘的Linux操作系统。主要面向桌面用户。它的设计重点是提供简单易用且稳定的操作系统,同时保持更新和开发。它具有强大的功能集和广泛的用户基础,可满足...

AlmaLinux 9.6发布:升级工具、初步支持IBM Power虚拟化技术

IT之家5月21日消息,科技媒体linuxiac昨日(5月20日)发布博文,报道称代号为SageMargay的AlmaLinux9.6发行版已上线,距上一版本9.5发...

跟老韩学Linux运维架构师系列,vim与view的基本使用

下面是vim和view的10个实例:用vim打开一个新文件:vimnewfile.txt这个命令将会在vim编辑器中打开一个新文件。在vim中移动光标:使用方向键或h、j、k、l键来移动光标。在v...

malloc底层原理剖析——ptmalloc内存池

malloc底层为什么是内存池malloc大家都用过,其是库函数。我们都知道库函数在不同的操作系统中其实执行的是系统调用,那么malloc在Linux上执行的是哪个系统调用呢?brk()和mmap()...

Zen 6架构首秀Linux,AMD加速下一代处理器布局

IT之家5月15日消息,科技媒体Phoronix昨日(5月14日)发布博文,报道称AMD已经开始为下一代“Zen6”处理器做准备,已为该构架向Linux内核提交了首个补丁,...

为何越来越多企业转向安卓/Linux工业平板电脑?答案在这里

在工业领域,设备的稳定性至关重要,尤其是工业平板电脑,常年运行在高温、粉尘、潮湿等复杂环境下,一旦系统崩溃或者卡顿,可能会影响整个生产流程。那么,为什么越来越多的企业选择安卓/Linux工业平板电脑,...

从3ms到0.8ms:ARM+Linux如何重塑工业控制实时性标杆

在智能制造领域,产线控制系统对实时性的要求越来越高。根据行业调研数据,超过65%的工业现场出现过因系统响应延迟导致的故障停机,平均每次停机造成的直接损失高达2-8万元。传统x86架构搭配Windows...

看Linux如何"挖坑种树"

写在前面,有人看我的Linux文章说技术难度不深,笔者不是不想写深,笔者是觉得Linux难就难在入门,入门之后你就知道如何上网查询你所要要解决的Linux需求。如果你已入门,此文已对你无用,请略过此...

AlmaLinux 9.6 发布,新增功能亮点纷呈!

距离上一版本AlmaLinux9.5发布六个月后,基于5.14内核的AlmaLinux正式宣布其企业级Linux发行版的9.x系列第六个更新——AlmaLinux9.6(Sag...