劉 澤,陳 洋,陳 林
(重慶郵電大學,重慶400065)
嵌入式視頻采集系統軟件平臺大多用的是Linux 系統,V4L2 是Linux 內核采用的視頻采集基本框架,相比于V4L,V4L2 更靈活,擴展性也更好一些,由于V4L2 對V4L 的改動較大,兩者并不兼容[1]。V4L2 支持多種設備,并且支持視頻采集接口、視頻輸出接口等。視頻采集采用的流水線方式使得V4L2 的操作極其簡單方便。V4L2 中還提供大量API 來對攝像頭進行基本操作,這些操作主要通過調用ioctl 來實現設備打開、關閉,以及圖像格式、曝光、增益等操作[2],一般V4L2 驅動是讓CPU 通過I2C 總線對圖像傳感器進行讀寫操作,I2C 總線包括數據總線和時鐘總線,主要由三部分構成:I2C 總線驅動、I2C 設備驅動和I2C 核心[3]。
本文采用MT9M001 圖像傳感器,開發出支持曝光、增益以及圖片尺寸大小等參數設置的驅動程序,CPU 采用TI 公司的AM3354,由于該芯片并沒有標準的攝像頭接口,硬件方案采用CPLD+SRAM 方式對CMOS 傳感器采集數據進行預處理和緩存,并參照Linux 自帶虛擬攝像頭ViVi,設計出具有兩個線程的驅動程序,詳細給出了非標準攝像頭接口的V4L2驅動程序的設計過程,最后調用動態庫API 來實現抓圖。
Linux 提供了虛擬攝像頭驅動程序ViVi,其框架也是基于V4L2 標準,V4L2 主要包括V4L2 驅動核心、字符設備驅動核心、平臺V4L2 驅動以及具體傳感器驅動,見圖1。

圖1 V4L2 驅動框架
V4L2 作為兩層驅動系統,在videodev 頂層模塊,初始化時會被注冊為主設備號為81 的字符設備,底層則是以videodevice 為核心的V4L2 驅動,該結構主要包含了功能函數以及視頻設備的屬性。此外videodev 模塊還提供video_device_register 函數API 來注冊V4L2 驅動,生成視頻設備節點,比如/dev/video1 等。V4L2 驅動源碼位于內核目錄/driver/media/video 下面,主要核心代碼文件有[4]:
1)v4l2-dev.c 視頻捕捉接口,主要數據結構為:struct video-device,主要用于射頻設備的注冊。
2)v4l2-device.c v4l2 提供設備描述,主要用于注冊v4l2設備。
3)v4l2-ioctl.c 為ioctl 提供了通用框架,主要是為了實現結構體v4l2_ioctl_ops 視頻操作相關聯下的API。
4)v4l2-mem2mem.c 內存之間的操作函數,是內核和視頻設備緩沖之間的框架。
5)v4l2-common.c 通用視頻的設備接口,此文件替換videodevice.c 文件配備常規內核分配。
在V4L2 核心中,提供了一套標準API 來用于視頻處理緩沖器,這些API 允許驅動程序用自己的方式實現read()、mmap()等[5]。
目前設備上的視頻緩沖器,支持線性存取DMA 的方式。Videobuf 層實現了用戶空間與驅動程序之間數據傳輸的粘合性,主要用于幀緩沖管理和分配。Videobuf 層的更多相關信息,在內核目錄的Documentation/video4linux/videobuf 中有詳細說明。
圖像傳感器采用的是micron 公司的MT9M001 CMOS 圖像傳感器,該傳感器幀率最大為30 f/s(幀/秒),即1 s 最大可以采集30 幀圖像數據,最大支持130 萬像素。性能優異,由于圖像傳感器采集的數據是10 位的灰度值,實際采集使用時舍去兩位,保存為8 位位圖格式來降低存儲空間。
主控芯片采用的是cortex A8 系列的ARM 處理器AM3354,工作時鐘最大為800 MHz,具有抗高溫、抗干擾等特點,完全可以滿足高清工業相機對CPU 的要求。
在主線程中,會注冊I2C 設備,通過檢查設備的名字,若有相同名字的設備probe 函數便會被調用,在驅動探測函數中需要使能圖像傳感器。通過傳感器的ID 號0x8431 來完成初始化。
reg_write(cmos_mt9m001_client,MT9M001_CHIP_ENABLE,1);
mid=reg_read(cmos_mt9m001_client,MT9M001_CHIP_VERSION);
if(mid!=0x8431)return ENXIO;
若能識別傳感器ID 號,說明傳感器被正確連接。應用程序調用open 打開注冊的視頻設備節點時,驅動中的open 函數會被執行,在open 函數中會創建一個負責讀取傳感器數據的子線程,在子線程被創建后會一直休眠。
kthread_run(cmos_buffer,dev,dev->v4l2_dev.name);
set_current_state(TASK_INTERRUPTIBLE);
wait_for_completion(&thread_completion);
此處采用CPLD 來預處理一幀的圖像數據,當CPLD 完成一幀數據的預處理后,通過CPLD 產生邊沿觸發信號來對視頻數據進行讀取,同時在open 函數中會申請cpld_int 內核中斷函數:
ret =request_irq(CPLD_IRQ,cpld_int,IRQF_TRIGGER_FALLING,"frame_interrupt",NULL);
CPLD 完成預處理后會向CPU 發出中斷,中斷函數會被調用,在中斷處理函數中會調用完成量喚醒創建的子線程。
子線程會對緩沖區隊列數據進行處理,其運行結束后會喚醒應用程序采集,一幀圖像數據處理完成,總結主次線程流程見圖2。

圖2 主次線程執行流程
在內核下的video_buf.c 中提供了V4L2 視頻緩沖區內存管理的函數,視頻的數據隊列通過videobuf_queue 結構體來管理,在此結構體中通過videobuf_queue_ops 結構體來進行相關視頻緩沖區操作,數據結構里主要提供API 來進行緩存區的具體操作。buffer_prepare 用來準備好隊列中的幀緩沖,buffer_queue 用來將buffer 加入對列,buffer_cleanup 則用來清除對列[6]。
視頻采集主要基于ioctl 操作集來實現,Linux 內核專門提供v4l2_ioctl_ops 結構體來管理緩沖區數據。常用函數說明如表1。

表1 v4l2_ioctl_ops 操作集
表中每一個ioctl 對應一個API 接口,這些API 需要手動實現[7]。視頻采集一幀數據流程如圖3 所示。

圖3 視頻采集基本流程
在對圖像數據進行采集時,在沒有DMA 情況下,直接read 和write 方式從內核空間拷貝大量數據到用戶空間的方式,不僅耗費大量內存,效率也十分低下[8]。本驅動采用內存映射的方式,向內和空間申請2 ~4 個緩沖區,當應用程序調用mmap 函數時,驅動程序不再是直接拷貝數據到用戶空間,而是利用mmap 來建立緩沖區和用戶空間的映射,即直接對用戶空間進行操作,效率大大提高。驅動程序建立映射只需對數據結構v4l2_file_operations 成員函數mmap 填充即可。


本驅動中還支持設置采集圖片尺寸大小以及曝光、增益等的設置。其中v4l2_ctrl_ops 結構體中的成員函數s_ctrl 用來設置曝光增益值,而g_volatile_ctrl 用來獲取曝光值。

硬件環境搭載好后,通過makefile 將驅動程序生成.ko 文件,將.ko 文件用insmod 命令安裝驅動。會在文件目錄/dev下自動生成video1 文件,此文件就是V4L2 驅動掛載點,對驅動程序進行測試時,將測試程序封裝為動態庫,動態庫中各個API 說明如表2。

表2 動態庫API 功能說明
上述API 中,CameraImageProcess 比較關鍵,因為其預留出圖像數據數組指針,實際需要對圖像進行分析處理時,只需獲得該API 參數指針進行操作即可,圖4 和圖5 為調用封裝動態庫采集圖片。
本文針對cortexA8 硬件平臺以及cmos 圖像傳感器mt9m001,實現一款針對非標準攝像頭接口的V4L2 驅動,詳細闡述了程序的設計思路以及實現應用程序動態庫的封裝,并在驅動程序中實現了對曝光、增益值得設置。最后通過調用應用程序封裝的動態庫采集圖片來驗證驅動的正確性,對沒有標準攝像頭接口的平臺開發具有一定借鑒意義。

圖4 未設置曝光增益采集的圖片

圖5 加入曝光增益設置采集的圖片
[1]王飛,孔聰. 基于V4L2 的Linux 攝像頭驅動的實現[J]. 電子科技,2012,25(2):86-87.
[2]黃俊偉,巴義. 基于V4L2 移動視頻監控系統的研究與設計[J]. 電視技術,2012,36(17):159-162.
[3]宋寶華.Linux 設備驅動開發詳解[M].北京:人民郵電出版社,2008.
[4]蔡小偉.基于ARM cortex A8 的嵌入式視頻監控系統設計[D].南昌:南昌大學,2013.
[5]廖夢云,趙利,莫金旺.基于CMOS 圖像傳感器的嵌入式視頻采集系統設計[J].計算機系統應用,2009(5):194-197.
[6]余臣.高分辨率高速CMOS 相機的硬件設計[D].成都:電子科技大學,2011.
[7]劉登誠,沈蘇彬,李莉. 基于V4L2 的視頻驅動程序設計與實現[J].微計算機信息,2011,27(10):56-58.
[8]叢培超.高幀高清CMOS 工業攝像機設計與實現[D].大連:大連理工大學,2013.