摘 要: 目前的嵌入式系統(tǒng)中,USB攝像頭使用比較普遍,但其應用會受到傳輸速度的限制。本文采用一款高速CMOS攝像頭,其驅動利用S3C6410內置的FIMC接口技術,采用DMA和ping-pong緩沖池機制,結合內存共享策略,有效提高了傳輸速率并充分利用了有限的內存資源。深入分析了該驅動的原理和實現細節(jié),并提出了改進設計,最終應用在嵌入式圖像采集系統(tǒng)中,能夠為應用程序提供高清、高速圖像。
關鍵詞: FIMC接口;OV9650;;內存共享;DMA
0 引言
嵌入式系統(tǒng)具有體積小、功耗低和成本低等天然性的優(yōu)勢,因而得到廣泛應用,甚至在許多場合得以取代傳統(tǒng)工控機,比如視頻監(jiān)控系統(tǒng)和安防系統(tǒng)。目前嵌入式系統(tǒng)中最常用、嵌入式Linux內核支持最廣泛的是USB攝像頭。然而受到嵌入式處理器性能的限制,USB攝像頭接口的傳輸速率限制為12 Mb/s,對于常用的640×480分辨率的YUV圖像,其最高幀率為3.75幀/s,無法滿足實時性要求。因此,基于CMOS圖像傳感器的高速攝像頭正在被推廣應用。CMOS高速攝像頭可以為嵌入式系統(tǒng)實時地提供高分辨率圖像[1],很適合進行識別、跟蹤等實時圖像處理作業(yè)。采集640×480的VGA圖像,CMOS攝像頭最高幀率可以達到30幀/s。基于OV系列的CMOS攝像頭應用很多,比如監(jiān)控[2]和人數檢測[3]都使用了OmniVision公司的OV9650攝像頭。參考文獻[4]介紹了基于S3C6410和OV9650的V4L2圖像采集系統(tǒng)的設計,參考文獻[5]和[6]介紹了基于S3C2440中相機接口(Camera Interface,CAMIF)的OV9650攝像頭驅動設計。
本文涉及的S3C6410[7]的完全交互式移動相機(Fully Integrated Mobile Camera,FIMC)接口是由S3C2440的CAMIF發(fā)展而來,但是目前關于FIMC驅動的原理分析和設計實現的文獻仍然很少。本文對OV9650及FIMC接口驅動的原理和實現細節(jié)作了深入分析,并對原有驅動進行了改進,使之適用于視線檢測系統(tǒng)。
本文首先分析攝像頭驅動所依賴的硬件接口,然后重點分析其驅動軟件設計原理和實現細節(jié),并給出改進設計,最后對下一步的改進工作提出展望。
1 攝像頭驅動系統(tǒng)的硬件接口
本文所涉及的驅動系統(tǒng)基于OK6410嵌入式開發(fā)板,采用S3C6410作為中央處理器。S3C6410內置的FIMC接口為開發(fā)板與CMOS攝像頭的連接提供了可靠便利的接口。該驅動系統(tǒng)的硬件結構如圖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協議的區(qū)別僅在于設備地址不同,因此,驅動中直接用I2C代替。
2 攝像頭驅動系統(tǒng)軟件
2.1 Linux驅動模型
在Linux操作系統(tǒng)中,設備驅動為應用程序提供訪問接口,屏蔽了底層硬件細節(jié)。從Linux2.6內核開始,設備被驅動和內核映射為文件,應用程序可以像訪問普通文件一樣訪問這些掛載在/dev目錄下的設備,訪問接口被定義在驅動中file_operations結構體對象內。每一個接口函數其實都是一個系統(tǒng)調用,其具體實現由驅動程序完成。Linux內核中驅動模型包括總線(Bus)、設備(Device)和驅動(Driver)三個要素,即設備和驅動作為對象掛載在相同的總線上,由總線對設備和驅動進行一一匹配。
Linux驅動模型將所有外設分為字符設備、塊設備和網絡設備三種。攝像頭屬于字符設備,其驅動遵循著字符設備驅動的框架,包括設備號、設備注冊和最重要的文件操作函數的實現。特別地,針對攝像頭設備,V4L2[8]接口為驅動程序提供了一套完備的文件操作標準接口和緩沖池管理策略,目前大多數的攝像頭驅動都遵循V4L2接口標準。
2.2 V4L2驅動
V4L2是Video For Linux 2的簡稱,是Linux內核中關于視頻設備的虛擬驅動,它不涉及硬件,僅僅為應用程序提供一套完備的操作接口,這些接口的具體實現都由遵守V4L2協議的驅動程序來完成,比如常用的ioctl接口。V4L2的存在極大地方便了應用程序的編寫,使得同一套應用程序可以應用于多種攝像頭。V4L2層次示意圖如圖3。
基于V4L2的基本圖像采集流程如下:
?。?)打開視頻設備文件(一般為dev/video0),初始化采集格式等參數;
(2)在內核空間申請若干視頻采集的幀緩沖區(qū);
?。?)地址映射,使得用戶空間的應用程序對幀緩沖區(qū)有讀寫權限;
?。?)幀緩沖區(qū)在視頻采集輸入隊列排隊,并啟動視頻采集;
?。?)從緩沖隊列取出幀緩沖區(qū),獲得數據進行處理;
?。?)處理完,將幀緩沖區(qū)重新放入視頻采集輸入隊列,循環(huán)往復采集連續(xù)視頻數據。
V4L2為圖像采集程序維持一個環(huán)形緩沖隊列,如圖4。該緩沖區(qū)隊列為ping-pong操作模式,應用程序需要使用的圖像數據會通過ioctl接口使對應的緩沖區(qū)出隊,緩沖隊列的其余緩沖區(qū)繼續(xù)接收圖像數據,并在一個循環(huán)之后覆蓋掉原有的緩沖區(qū)數據,以保證緩沖區(qū)隊列中圖像數據的實時性。
2.3 關鍵模塊驅動的設計與實現
OV9650通過FIMC接口與S3C6410連接,FIMC為輸入圖像進行格式轉換、剪裁等預處理,最后傳輸到內核中開辟的圖像緩沖區(qū),供應用程序讀取。攝像頭驅動分為兩個部分:OV9650驅動和FIMC驅動。
2.3.1 OV9650驅動
OV9650驅動的主要作用是掛載驅動和配置寄存器,其中.h文件定義了寄存器配置數據,并由.c文件調用。由于FIMC接口的存在,應用程序不需要直接操作OV9650攝像頭,因此OV9650驅動不需要為應用程序提供訪問接口,不需要定義file_operations結構體。
從Mach-smdk6410.c文件中可以知道,內核在啟動時,將OV9650作為一個I2C設備掛載到內核樹的I2C總線上。之后內核找到OV9650驅動,執(zhí)行其入口函數——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緩沖區(qū),以提高圖像傳輸速度和內存使用效率。如圖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機制與傳統(tǒng)的device driver機制(通過drivce_register函數進行注冊)相比,一個十分明顯的優(yōu)勢在于platform機制將設備本身的資源注冊進內核,由內核統(tǒng)一管理,在驅動程序使用這些資源時通過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類型數據保存到內核中,供后續(xù)使用。最后,調用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,
};
當應用程序通過系統(tǒng)調用open()打開攝像頭設備時,內核會最終找到s3c_fimc_open()函數來打開攝像頭。
應用程序通過read()獲取圖像數據,該函數通過copy_to_user()將內核空間所申請緩沖區(qū)中圖像數據拷貝到用戶空間中開辟的圖像數據區(qū)。該函數沒有充分利用FIMC接口提供的ping-pong緩沖區(qū),按字進行拷貝(memcpy),十分耗時。
mmap函數將內核空間中申請到的圖像緩沖區(qū)映射到應用程序所在的用戶空間,這樣,應用程序申請到的buffer將指向內核空間的圖像緩沖區(qū),應用程序可以(不拷貝)直接對圖像進行操作。該函數配合V4L2標準中的環(huán)形緩沖區(qū)隊列,節(jié)省了應用程序讀取圖像數據所消耗的時間。
2.3.2.3 V4L2接口
V4L2接口功能強大,為應用程序提供了完備的操作接口,包括設置格式、幀率、白平衡、曝光模式、申請緩沖區(qū)、取數據、剪裁圖像等。此處僅列舉幾個重要接口。
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()用于申請圖像緩沖區(qū),該函數為應用程序在內核空間開辟ping-pong緩沖區(qū)。s3c_fimc_v4l2_qbuf()函數將緩沖區(qū)組成環(huán)形緩沖隊列,當應用程序需要調用圖像數據時,使用s3c_fimc_v4l2_dqbuf()使指定的緩沖區(qū)出隊,緩沖區(qū)在出隊期間,不會被新來的圖像數據覆蓋,新到的圖像數據會被傳送到環(huán)形隊列中指定緩沖區(qū)的下一個緩沖區(qū)。由于FIMC控制器為P通道和C通道分別開辟了4個緩沖區(qū),在內核初始化時已經申請到,因此FIMC驅動中并不需要再重新申請。
2.4 驅動錯誤分析與改進設計
在驅動主體框架正確的情況下,開發(fā)板(OK6410A)自帶的驅動并不能直接使用。同一套V4L2圖像采集程序可以應用在USB攝像頭上,應用在OV9650上卻無法獲得圖像,并且先后報出兩個錯誤:
?。?)tx or ty is lower than zero and this is a invalid target size
?。?)VIDIOC_QUERYBUF error
上述錯誤出現之后,LCD屏幕沒有圖像,緊接著串口會顯示display 0,表示程序無法讀出圖像數據,導致程序終止。下面分別對兩個錯誤進行分析并改進。
2.4.1 錯誤1的分析與改進
由于ov9650驅動僅僅將ov9650設備注冊進入系統(tǒng),并沒有其他處理,因此ov965x.h和ov965x.c兩個文件不用修改和調試。
FIMC采用DMA通道向內核緩沖區(qū)傳送數據,需要專門的函數打開DMA輸出通道。通過函數跟蹤得知,原有s3c_fimc_v4l2_s_fmt_vid_cap()函數并沒有打開輸出DMA通道,因此CMOS攝像頭的數據并沒有傳送到內核開辟的緩沖區(qū)中,導致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)
該函數用于配置所申請圖像緩沖區(qū)的長度、偏移量等屬性,從而使所申請的環(huán)形隊列緩沖區(qū)生效。由于本系統(tǒng)中攝像頭僅用于圖像捕獲,因此在該函數開頭可以將b->type進行強制設定,即:b->type=V4L2_BUF_ TYPE_VIDEO_CAPTURE,從而解決圖像緩沖區(qū)申請的問題,該錯誤不再出現。
至此,修正后的FIMC驅動程序可以正常工作,圖像數據得以傳輸到內核中為FIMC驅動開辟的圖像緩沖區(qū),應用程序可以通過read()系統(tǒng)調用或者ioctl()的VIDIOC_DQBUF命令獲取圖像數據。
圖像捕獲效果如圖6所示。
3 結論
本文深入分析了CMOS攝像頭驅動的原理和實現細節(jié)。FIMC驅動向內核申請四個ping-pong緩沖區(qū),通過DMA方式傳入圖像數據,提高了圖像數據傳輸速率。內存共享策略使得應用程序在訪問圖像緩沖區(qū)時免去了內存拷貝的步驟,大大縮減了圖像獲取的時間。但是目前的FIMC驅動在緩沖區(qū)出隊和入隊時的保護機制仍不夠完善,需要在今后的工作當中對這一部分不斷進行優(yōu)化。
參考文獻
[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的嵌入式監(jiān)控設計[J].輕工機械,2012,30(2):50-53.
[3] 官志平.基于ARM9的Linux系統(tǒng)移植以及在電梯轎廂內人數檢測的應用[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.