引言:近年來高性能、低功耗的ARM處理器成為嵌入式應用的主流;開源的嵌入式Linux操作系統由于系統穩定、兼容性和移植性好、網絡功能強等優點也成為首選嵌入式操作系統之一,但目前嵌入式Linux支持的USB攝像頭(如OV511)市場上已淘汰,使用現有USB攝像頭需開發相關驅動程序,由于采用中芯微公司的USB攝像頭在市場中的占有率很高,可高效壓縮后輸出JPEG圖像,所以本文針對這類USB攝像頭設計了基于AT91RM9200處理器的圖像采集處理平臺,實現了JPEG圖像的采集和網絡傳輸。
1.硬件系統設計
?。?) AT91RM9200簡介
AT91RM9200是ATMEL公司生產的基于ARM920T的工業級SOC芯片,不僅有豐富的片上資源和標準接口,而且有低功耗、低成本、高性能、支持多種主要的嵌入式操作系統等特點,其采用5級整數流水線結構性能高達200 MIPS, 具有標準的ARMv4存儲器管理單元(MMU),內部集成有兩個USB 2.0 全速(12 M比特/秒) 主機端口和10/100 Base-T 型以
太網接口,該芯片具有多種工作模式,其低功耗待機模式下電流僅3.1 mA[1]。
?。?) AT91RM9200的USB主機端口(UHP)
AT91RM9200集成有一個USB器件端口(UDP)和一個USB主機端口(UHP),均符合USB V2.0 全速及低速規范。UHP內部集成一個根集線器和2個收發器,可連接127個USB 器件,UHP控制器與OHCI Rev 1.0規范完全兼容,標準分類驅動可以自動檢測并在用戶程序中使用[1]。
?。?) 硬件系統結構
圖像采集平臺的硬件系統結構設計如圖1所示,主要包括AT91RM9200處理器、JTAG接口、網絡模塊、32M SDRAM、16M FLASH、串口、USB主從口等部分。其中網絡模塊通過外接DM9161實現10M/100M自適應網絡連接,通過處理器內置的4個通用同步(異步)收發器(USART) 可實現4路數據傳輸與控制。另外,處理器內置的雙主機收發器可連接USB攝像頭和USB存儲設備,也可經USB集線器連接更多USB設備,提高了系統的擴展性。
圖1.硬件系統結構
2.軟件系統設計
(1) 嵌入式Linux軟件架構
Linux工作模式分為內核模式和用戶模式,其軟件系統架構由硬件控制器、Linux內核、系統調用接口和用戶進程4層組成。一個用戶進程就是一個用戶程序,操作系統支持多進程并發;內核是操作系統的中心組件,有進程管理、內存管理、文件系統管理、設備控制、網絡控制等功能,它通過底層接口層以一致的方式管理硬件,通過高層抽象層為用戶進程提供與硬件無關的API控制硬件資源;系統調用接口負責為應用程序調用內核中特定的過程,從而實現特定服務,一般認為這些調用和服務也是操作系統內核的一部分。
(2) USB驅動程序系統框架
圖2.USB驅動程序系統框架
USB驅動程序的系統框架如圖2所示,包括客戶驅動程序、通用總線驅動程序、主機控制器驅動程序幾部分。其中,客戶驅動程序是特定USB設備的驅動程序,提供了USB設備的功能操作及特定子類協議封裝[6];通用總線驅動程序(USBD)擁有特定操作系統上抽象出的主機控制器驅動程序的共有特性,是整個USB驅動程序的核心,主要實現USB總線管理、URB管理、為客戶驅動程序提供相關接口等功能,它還負責維護設備的加載和卸載、設備配置、客戶端驅動程序的安裝和卸載等工作[2];主機控制器驅動程序是直接與硬件交互的軟件模塊,主要實現主機控制器硬件初始化、負責總線的注冊、為USBD層提供相應的接口函數、完成4種類型的數據傳輸等功能[2]。
Linux通過定義了統一的URB(Universal Request Block)結構,在客戶驅動程序和USBD之間,以及USBD和HCD之間進行消息傳遞,為USB驅動程序的開發帶來了很大方便[3]。我們開發USB驅動程序主要是編寫USB客戶軟件層的程序,即如何將數據封裝成URB和如何從URB中得到數據。
(3) V4L簡介與攝像頭驅動程序開發
Video for Linux(簡V4L)是Linux中關于視頻設備的內核驅動,它為編寫視頻應用程序提供一系列接口函數,內核、驅動程序和應用程序以它為標準進行交流,因此視頻類驅動程序的開發必須遵循此標準,應用V4L API函數進行設計[4]。
設備驅動程序是Linux內核與應用程序之間的接口,通過USB客戶驅動程序提供的USBD接口和應用程序接口,屏蔽了硬件實現的細節。應用程序將外部設備看成是一類特殊文件__設備文件,可以使用像操作普通文件一樣的系統調用接口函數來完成對外部設備的打開、關閉、讀寫和I/O控制操作。陷于篇幅原因只對驅動程序的重要部分進行闡述。
l 驅動程序的注冊、注銷:所有的USB設備類驅動程序都要在USBD中進行注冊和注銷,Linux中的驅動程序通常采用模塊方式編寫,使用函數module_init注冊設備,使用函數module_ exit注銷設備。
module_init(usb_gfkd_init); /*加載模塊入口,調用函數usb_register()注冊設備*/
module_exit(usb_gfkd_exit); /*注銷模塊入口,調用函數usb_deregister()注銷設備*/
l 驅動程序與USBD的接口:USBD為每個設備驅動程序維護一個相關的usb_
driver的數據結構,負責設備的初始化和卸載。當總線上有設備連接操作時,USBD通過該結構來查找相關的驅動程序,并調用初始化函數probe()對設備初始化;當設備斷開時,USBD也通過該結構來查找相關的驅動程序,并調用設備卸載函數disconnect ()對設備卸載。USBD接口的數據結構定義為:
static struct usb_driver gfkd_driver = { "gfkd",gfkd_probe,gfkd_disconnect};
初始化函數static void * gfkd_probe(…)首先讀取設備的Usb dev結構,根據設備的配置描述符判斷該設備是否被驅動程序所支持, 判斷使用接口是否正確,然后為驅動申請一塊內存,再探測使用的攝像頭,完成對攝像頭的初始化,最后創建攝像頭的設備文件結點[5]。
卸載函數static void gfkd_disconnect (struct usb_device *dev, void *ptr)的作用是終止數據傳輸、刪除攝像頭的設備文件結點、釋放接口、將驅動占用的內存釋放。
l 驅動程序與應用程序接口:攝像頭驅動程序在static struct file_operations gfkd_fops中給應用程序提供了統一的外設操作函數接口,當應用程序對攝像頭進行open 、release、read、內存映射mmap以及IO控制等系統調用操作時將通過該結構訪問驅動程序提供的函數。
static struct file_operations gfkd_fops = {
.owner = THIS_MODULE, .open = gfkd_open,
.release = gfkd_close, .read = gfkd_read,
.mmap = gfkd_mmap, .ioctl = gfkd_ioctl,
.llseek = no_llseek, };
打開攝像頭函數static int gfkd_open(struct inode *inode, struct file *file)作用是打開攝像頭的設備文件結點,并為數據傳輸做好必要的準備工作。它先調用函數gfkd _alloc()分配用于視頻解碼的臨時數據緩沖區、幀緩沖區和數據緩沖區;然后初始化攝像頭,用函數gfkd _setMode()設置輸出的視頻格式和分辨率;再用函數gfkd _setFrameDecoder()設置幀緩沖區接收的視頻幀的格式和分辨率;最后調用函數gfkd _init_isoc()初始化等時數據傳輸設置、打開攝像頭和分配提交URB。
關閉攝像頭函數static int gfkd_close(struct inode *inode, struct file *file)作用是關閉攝像頭的設備文件結點。它先調用函數gfkd _stop_isoc()終止等時數據傳輸;再調用函數CameraShutDown()關閉攝像頭;最后使用函數gfkd _dealloc( )釋放分配的各種緩沖區。
內存映射函數static int gfkd_mmap(struct file *file, struct vm_area_struct *vma)實現內核空間與用戶空間的內存映射。先通過函數vmalloc()申請分配足夠大的內核態內存作為圖像幀緩沖區,并能存儲兩個URB采集的圖像;然后用函數remap_page_range()將其映射到用戶空間中。這樣提高了用戶程序獲取內核態圖像幀緩沖區數據的速度。
讀函數static long gfkd_read(struct video_device *dev, char *buf, unsigned long count, int noblock)通過調用函數copy_to_user()將圖像數據從內核態的幀緩沖區拷貝到用戶態的數據緩沖區。
IO控制函數static int gfkd_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)的功能是接收應用程序的各種命令,實現對攝像頭的控制操作,如獲得攝像頭的參數、設置攝像頭的分辨率
、開始采集圖圖像和設置幀同步。
由于Linux中任何USB傳輸都是通過URB實現的,每次URB傳輸都包括URB的建立、發出、回收、數據整理等階段不產生有效數據,因此在具體實現中采用等時傳輸方式,通過建立兩個URB,使用雙URB輪流通信的方法來提高圖像的采集速度。
本驅動程序開發是基于ATMEL最新版Linux-2.4.27-vrs1-Atmel,在驅動程序開發完后需重新配置內核,讓內核支持usb-ohci 和video for linux,再把驅動程序配置成module,然后重新編譯內核生成.o文件。將編譯好的驅動放入文件系統,建立設備文件,然后將文件系統燒入flash,再連接USB攝像頭(如內置中芯微Zc301P DSP),把模塊加載進內核并注冊就可以找到該攝像頭并顯示:
gfkd _core.c: USB gfkd camera found. Type Vimicro Zc301P 0x301b
gfkd _core.c: gfkd driver 00.57.06LE registered
(4) 圖像采集的實現與性能分析
服務端應用程序的實現是基于C/S模式,使用了3個線程,其中一個主線程,一個圖像采集線程負責從驅動程序獲取圖像,可根據變量grabMethod選擇采用read方式或內存映射方式獲取圖像;另有一個圖像發送線程負責圖像發送,程序通過建立帶共享鎖的4幀圖像循環隊列做為圖像采集線程和圖像發送線程進行數據交換的公共緩沖區。服務端還使用了兩個socket,一個用于和服務端口綁定后偵聽是否有服務請求,另外一個用于發送圖像數據,主線程流程如圖3所示。
程序首先設置采集圖像的相關參數(如設備號、圖像大小、初始化圖像幀緩沖區等),然后通過函數 int init_videoIn()獲取攝像頭參數,設置采集圖像寬度、高度、格式、采集方式等參數,并分配4幀采集圖像緩存vd->ptframe[i] =(unsigned char *) realloc (vd->ptframe[i], sizeof(struct frame_t) + (size_t) vd->framesizeIn ),再啟動圖像采集線程 pthread_create (&w1, NULL, (void *) grab, NULL)進行圖像采集;創建服務端socket,與服務端口綁定后偵聽服務請求;如果有新連接進來,函數accept()返回一個新的發送socket,并啟動新的圖像發送線程,pthread_create(&server_th, NULL, (void *)service, &new_sock); 如果采集結束或連接產生錯誤,調用pthread_join (w1, NULL)和close(serv_sock)關閉圖像采集線程和圖像發送線程,釋放有關資源后退出。
圖3.主程序流程
使用奧尼銀色天使S900攝像頭分別對640×480和320×240兩種分辨率用read方式和內存映射方式進行了圖像采集和發送,實驗結果如表1所示,應用程序采用內存映射方式圖像獲取的實時性較高,達到實時視頻的要求。
4結束語
本文針對市場主流USB攝像頭開發了驅動程序,實現了基于AT91RM9200的嵌入式圖像采集和網絡傳輸。克服了其它圖像采集方案采集BMP圖像數據量大和實時性差的問題,并解決了目前嵌入式Linux缺乏USB攝像頭驅動程序的問題,具有集成度和性價比高、實時性 好、支持多種USB攝像頭和充分利用USB帶寬的優點。實驗表明適于高質量實時圖像監控場所和智能圖像監控應用,具有很好的廣泛應用前景。
參考文獻:
[1]ATMEL, AT91RM9200 DATA,[Z]. America, Atmel Corporation , 2003.
[2](美)科比特、魯賓尼、哈特曼主編,LINUX設備驅動程序[M],東南大學出版社,2004
[3]周力功 主編 ,USB編程與驅動程序開發[M],北京航空航天大學,2004
[4]李侃,基于S3C2410平臺與嵌入式Linux圖像采集應用[J],微計算機信息,2006,第3-2期
[5]Don Anderson、Dave Dzatko 著,USB系統體系[M],中國電力出版社,2003
[6]倪繼利著,LINUX 內核分析及編程[M],電子工業出版社,2005