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