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