驅(qū)動(dòng)程序范文
時(shí)間:2023-04-09 11:54:56
導(dǎo)語(yǔ):如何才能寫好一篇驅(qū)動(dòng)程序,這就需要搜集整理更多的資料和文獻(xiàn),歡迎閱讀由公務(wù)員之家整理的十篇范文,供你借鑒。
篇1
2、下拉菜單中選“屬性”,
3、屬性中選“硬件”,
4、硬件里選“設(shè)備管理器”,
5、設(shè)備管理器中點(diǎn)開(kāi)“端口”,
6、端口里右鍵點(diǎn)“打印機(jī)端口”
7、選“屬性”
8、屬性里選“驅(qū)動(dòng)程序”
9、驅(qū)動(dòng)程序里選“驅(qū)動(dòng)程序詳細(xì)信息”
10、此時(shí)在驅(qū)動(dòng)文件窗口里顯示的就是驅(qū)動(dòng)文件所在位置及名稱。
篇2
這歸結(jié)于優(yōu)化的功效,很多玩家都可以有針對(duì)性地對(duì)游戲進(jìn)行優(yōu)化設(shè)置,調(diào)整系統(tǒng)、優(yōu)化資源,同時(shí)對(duì)游戲進(jìn)行設(shè)置上的更改,尋找最佳的平衡點(diǎn)。
現(xiàn)在,我們來(lái)嘗試針對(duì)NVIDIA及ATI兩種主流顯卡品牌的驅(qū)動(dòng)程序進(jìn)行優(yōu)化說(shuō)明,來(lái)看看它們有什么秘密吧!
ATI 提高《Doom》運(yùn)行速度有技巧
如果你想玩《Doom》一類采用OpenGL的游戲,尤其是當(dāng)你的電腦使用ATI顯卡時(shí),你必定會(huì)感到很煩惱。OpenGl性能不強(qiáng)使得ATI的顯卡在以《Doom》為首的OpenGL游戲中一向表現(xiàn)不佳。因此,針對(duì)OpenGL部分的調(diào)節(jié)非常必要,盡可能提高這方面的性能是優(yōu)化ATI顯卡驅(qū)動(dòng)程序的重要方向之一。
切換到ATI催化劑驅(qū)動(dòng)程序的OpenGL選項(xiàng)卡上,選中“主要設(shè)置”的“自定義設(shè)置”,然后進(jìn)行細(xì)節(jié)調(diào)整。將“紋理選項(xiàng)”和“Mipmap”的拉桿向左拉到“性能”上,這樣可以保證在不明顯降低畫面質(zhì)量的情況下使OpenGL游戲速度得到保證,同時(shí)將“等待垂直同步信號(hào)”調(diào)整成“始終為關(guān)”。
要想將ATI顯卡的潛力進(jìn)行最大化的挖掘,還要進(jìn)行自定義設(shè)置。勾選“使用自定義設(shè)置”項(xiàng)后,再點(diǎn)擊“自定義”按鈕就可切換到自定義設(shè)置頁(yè)面。在其中“消除混疊”其實(shí)就是指全屏抗鋸齒,在這里,如果你的顯卡是低于X1000系列的,那么最好選擇2×或者干脆關(guān)閉。但是,最高級(jí)的顯卡也不要妄想開(kāi)啟6×,這時(shí)根本無(wú)法體驗(yàn)游戲樂(lè)趣了。
NVIDIA 讓秘密大白于天下
相比之下,NVIDIA的優(yōu)化設(shè)置更為科學(xué),不僅有預(yù)置的優(yōu)化方案,也有適合高級(jí)玩家的細(xì)節(jié)調(diào)整。
先說(shuō)簡(jiǎn)單的方法,在NVIDIA的ForceWare驅(qū)動(dòng)程序中提供了很多游戲、應(yīng)用程序的優(yōu)化設(shè)置方案,當(dāng)你想運(yùn)行某個(gè)游戲前,只要在驅(qū)動(dòng)控制面板中選中相應(yīng)的優(yōu)化方案就可以了。驅(qū)動(dòng)中的方案會(huì)不定期進(jìn)行調(diào)整、添加,即便很多新的游戲,也會(huì)在短期內(nèi)擁有相應(yīng)的預(yù)置優(yōu)化方案。
當(dāng)然,還是有很多玩家不會(huì)滿足于預(yù)置方案,自主優(yōu)化是另外一種選擇。其中最能影響性能及兼容性的選項(xiàng)莫過(guò)于“Anisotropic filtering”,即各向異性過(guò)濾了。這個(gè)選項(xiàng)在開(kāi)啟時(shí)會(huì)讓畫面更加精細(xì),但是,它還帶來(lái)了性能大幅度下降、兼容性不佳的“副作用”,所以,如果你的顯卡并不是非常新,但又想玩新游戲,不如把這個(gè)選項(xiàng)關(guān)閉,以獲得更好的運(yùn)行速度。
篇3
關(guān)鍵詞:USB協(xié)議;Linux驅(qū)動(dòng);USB Device驅(qū)動(dòng)
中圖分類號(hào):TP316文獻(xiàn)標(biāo)識(shí)碼:A文章編號(hào):1009-3044(2011)22-5418-02
Design of USB Device Drver Program on linux
SUN Yong-gang, JIAO Li-fei
(College of Science, GuiZhou University, Guiyang 550025, China)
Abstract:USB interface, with its efficient, reliable and widely is used in various embedded products. However, existing data on linux operating system, more studies USB Host, USB Device driver rarely is done. Therefore this paperthrough a simpleintroduction of USB protocol, as well as analysis of USB driver architecture about linux, is about an design of USB Device driver of linux systemfor embedded microprocessor S3C2440 .
Key words: USB agreement; linux driver;USB device driver
嵌入式產(chǎn)品通過(guò)的USB接口品可以很方便與PC的USB進(jìn)行通信以完成數(shù)據(jù)的傳輸與交互。ARM嵌入式處理器性以其性能高、功耗低而被廣泛地應(yīng)用于消費(fèi)電子、工業(yè)控制等眾多領(lǐng)域。以ARM內(nèi)核為核心集成了USB功能的處理器使得產(chǎn)品更簡(jiǎn)潔、更靈活、更方便。S3C2440集成了ARM920T內(nèi)核,帶MMU功能,可運(yùn)行l(wèi)inux操作系統(tǒng),同時(shí)帶有兩個(gè)USB Host一個(gè)USB Device控制器,因此在此基礎(chǔ)上完成Linux 下的USB Device驅(qū)動(dòng)程序有著重要的意義。本文是以此處理器為核心的嵌入式智能終端的Linux下的USB Device驅(qū)動(dòng)程序設(shè)計(jì)。
1 USB體系結(jié)構(gòu)及協(xié)議
1.1 USB硬件系統(tǒng)結(jié)構(gòu)
USB接口是由+5V電源線、電源地線、信號(hào)線D+、信號(hào)線D-四根電纜線組成接口。其中+5V電源是向設(shè)備提供電源,信號(hào)線作用是傳輸數(shù)據(jù), 為了提高信號(hào)傳輸?shù)目垢蓴_能力這兩根數(shù)據(jù)線采用差分傳輸。主機(jī)可以通過(guò)D+和D-線的電平高低來(lái)確設(shè)備是高速設(shè)備還是全速設(shè)備。
1.2 USB數(shù)據(jù)傳輸通道
USB主機(jī)與USB設(shè)備由很多端點(diǎn)構(gòu)成,它們之間通過(guò)端點(diǎn)進(jìn)行通訊。通過(guò)設(shè)置與些端點(diǎn)相對(duì)應(yīng)的寄存器,可以為這些端點(diǎn)分配唯一的地址(由端點(diǎn)號(hào)和傳輸方向組成)。USB總線支持四種傳輸類型,他們分別是控制傳輸、同步傳輸、中斷傳輸、批量傳輸。端點(diǎn)O只支持控制傳輸。
1.3 USB總線枚舉
USB總線枚舉就是當(dāng)USB設(shè)備連接到USB主機(jī)時(shí),主機(jī)通過(guò)缺省管道以控制傳輸方式來(lái)獲取USB設(shè)備發(fā)來(lái)的設(shè)備描述符、配置描述符、接口描述符、端點(diǎn)描述符信息,并根據(jù)這些描述相關(guān)內(nèi)容對(duì)USB設(shè)備進(jìn)行相應(yīng)的配置。
2 Linux USB 驅(qū)動(dòng)程序
在Linux系統(tǒng)中,USB驅(qū)動(dòng)程序可以分為USB Host驅(qū)動(dòng)程序和USB Device驅(qū)動(dòng)程序。Linux USB Host驅(qū)動(dòng)程序和USB Device驅(qū)動(dòng)程序總體架構(gòu)如圖1所示。
從圖1可以知,在Linux USB Host中,USB控制器驅(qū)動(dòng)是運(yùn)行在USB 控制器硬件上面的的驅(qū)動(dòng)動(dòng)程序。該驅(qū)動(dòng)實(shí)現(xiàn)了對(duì)USB 控制器硬件進(jìn)行控制,一般稱為USB固件驅(qū)動(dòng)程序。USB設(shè)備驅(qū)動(dòng)處于USB驅(qū)動(dòng)的最頂層,它主要實(shí)現(xiàn)USB設(shè)備如何與主機(jī)進(jìn)行通信。處在USB主機(jī)控制器驅(qū)動(dòng)與USB設(shè)備驅(qū)動(dòng)之間的是USB核心層,起到驅(qū)動(dòng)程序橋梁的作用,該核心層為USB主機(jī)USB主機(jī)控制器驅(qū)動(dòng)提供編程接口。
Linux系統(tǒng)中, USB Device驅(qū)動(dòng)分為UDC驅(qū)動(dòng)、Gadget API、Gadget驅(qū)動(dòng)三個(gè)層次結(jié)構(gòu)。UDC驅(qū)動(dòng)處USB Device控制器硬件之上,該驅(qū)動(dòng)程序控制USB控制器硬件工作,同時(shí)向上層提供操作USB控制器硬件的回調(diào)函數(shù)。處在中間層的是Gadget API層,該API向下層和上層提供統(tǒng)一的編程接函數(shù)的封裝。Gadget驅(qū)動(dòng)程序完成設(shè)備功能的實(shí)現(xiàn)。通過(guò)編寫不同Gadget驅(qū)動(dòng)程序可以使設(shè)備具有不同的功能。
3 S3C2440 USB Device驅(qū)動(dòng)
3.1 S3C2440 USB接口特性
S3C2440嵌入式微處理器集成了一個(gè)設(shè)備控制器。該設(shè)備控制器具有以下特征:
1) 完全兼容USB1.1的協(xié)議。設(shè)備全速運(yùn)行時(shí)可達(dá)到了12Mb/s。
2) 支持控制、中斷和批量傳輸,批量傳輸支持DMA接口。
3) 自帶5個(gè)的端點(diǎn)。端點(diǎn)EP0帶有16byte的FIFO,該端點(diǎn)為雙向的控制端點(diǎn),其余4個(gè)端點(diǎn)都帶有128字節(jié)輸入/輸出的FIFO(異步雙端口RAM)的,支持中斷或DMA批量傳輸。
3.2 S3C2440 USB Device驅(qū)動(dòng)程序設(shè)計(jì)
一個(gè)完整的S3C2440 USB Device驅(qū)動(dòng)程序由S3C2440_UDA驅(qū)動(dòng)和gadget驅(qū)動(dòng)兩部分構(gòu)成。S3C2440_UDA驅(qū)動(dòng)是用來(lái)控制S3C2440的USB Device硬件控制器器,并把對(duì)硬件控制操作抽象為函數(shù)接口供上層調(diào)用。USB gedget驅(qū)動(dòng)程序運(yùn)行在S3C2440_UDA驅(qū)動(dòng)程序之上的,不同的gedget驅(qū)動(dòng)程序使該設(shè)備具有不同的功能。
Linux gadget驅(qū)動(dòng)程序主要涉及到2個(gè)重要的結(jié)構(gòu)體usb_gadget_driver和struct file_operations結(jié)構(gòu)。其中usb_gadget_driver結(jié)構(gòu)體包括bind、setup、disconnect等一些函數(shù)。Linux Gadget提供usb_gadget_register_driver函數(shù)對(duì)Gaget驅(qū)動(dòng)進(jìn)行注冊(cè)。當(dāng)Gadget驅(qū)動(dòng)被注冊(cè)后,Linux內(nèi)核就會(huì)調(diào)用結(jié)構(gòu)體usb_gadget_driver中的bind函數(shù)把Gadget驅(qū)動(dòng)與UDA驅(qū)動(dòng)進(jìn)行綁定,這樣就可以在Gadget驅(qū)動(dòng)中使用UDA提供的統(tǒng)一接口函數(shù)。
bind函數(shù)中需要完成以下工作:
1) 使用usb_ep_autoconfig函數(shù)申請(qǐng)以后用到的傳輸端點(diǎn)。
2) 通過(guò)usb_ep_alloc_request函數(shù)為Gadget驅(qū)動(dòng)分配一個(gè)請(qǐng)求。
3) 通過(guò)調(diào)用register_chrdev_region注冊(cè)設(shè)備驅(qū)動(dòng)程序。
Bind函數(shù)完成這后當(dāng)有USB Host 向USB設(shè)備發(fā)出請(qǐng)求時(shí),Linux系統(tǒng)將調(diào)用setup函數(shù)來(lái)響應(yīng)請(qǐng)求。Setup函數(shù)把設(shè)備的設(shè)備描述符、配置描述符、接口描述符以及以后需要使用的幾個(gè)端點(diǎn)描述符發(fā)送給USB Host,這些配置信息的發(fā)送都是通過(guò)usb_ep_queue函數(shù)來(lái)完的。
struct file_operations結(jié)構(gòu)包含有open、read、write等函數(shù)。通過(guò)該結(jié)構(gòu)體定義的變量被register_chrdev_region函數(shù)注冊(cè)后該設(shè)備就可以像字符設(shè)備那樣使有了。該結(jié)構(gòu)中的一些函數(shù)完成的功能如下:
1) open函數(shù)通過(guò)init_waitqueue_head完成等待隊(duì)列初始化。
2) read函數(shù)通過(guò)alloc_ep_req函數(shù)分配一個(gè)讀請(qǐng)求變量,并為該變量中的complete設(shè)置一個(gè)請(qǐng)求完成函數(shù),調(diào)用usb_ep_queue函數(shù)向端點(diǎn)提交I/O讀請(qǐng)求。當(dāng)內(nèi)核從USB Device讀到數(shù)據(jù)時(shí)就會(huì)調(diào)有剛才的完成函數(shù)。在完成讀數(shù)據(jù)之前可以通過(guò)add_wait_queue和schedule()函數(shù)讓進(jìn)程掛起,在完成函數(shù)中喚醒掛起的進(jìn)程。
3) write函數(shù)通過(guò)alloc_ep_req函數(shù)分配一個(gè)寫請(qǐng)求變量,并為該變量中的complete設(shè)置一個(gè)請(qǐng)求,調(diào)用usb_ep_queue函數(shù)向端點(diǎn)提交I/O寫請(qǐng)求。當(dāng)內(nèi)核向USB Device寫完數(shù)據(jù)時(shí)就會(huì)調(diào)有剛才的完成函數(shù)。在完成寫數(shù)據(jù)之前可以通過(guò)add_wait_queue和chedule()函數(shù)讓進(jìn)程掛起,在完成函數(shù)中喚醒掛起的進(jìn)程。
至此整個(gè)驅(qū)動(dòng)程序就設(shè)計(jì)完成了,圖2為USB Host 與 USB Device 通信測(cè)試效果。
4 結(jié)束語(yǔ)
USB Device 為眾多電子產(chǎn)品提供了一個(gè)與PC信息交互的更好的方案。本文通過(guò)對(duì)USB協(xié)議介紹,以及對(duì)Linux下USB驅(qū)動(dòng)程序進(jìn)行分析,在此基礎(chǔ)上實(shí)現(xiàn)了USB Device驅(qū)動(dòng)程序進(jìn)行設(shè)計(jì)。實(shí)踐表明該設(shè)計(jì)是可行的。
參考文獻(xiàn):
[1] 馮國(guó)進(jìn).嵌入式Linux驅(qū)動(dòng)程序設(shè)計(jì)從入門到精通[M]北京:清華大學(xué)出版社,2008.
[2] 薛園園.USB應(yīng)用開(kāi)發(fā)技術(shù)大全[M].北京:人民郵電出版社,2007.
[3] 劉少峰,韋克平.USB軟件系統(tǒng)的開(kāi)發(fā)[J].計(jì)算機(jī)應(yīng)用研究,2002,19(30).
[4] 于明,范書瑞,曾祥燁.ARM9嵌人式系統(tǒng)設(shè)計(jì)與開(kāi)發(fā)教程[M].北京:電子工業(yè)出版社,2006.
篇4
首先找到我們電腦上的此電腦或者計(jì)算機(jī),鼠標(biāo)右擊彈出菜單選擇管理。
進(jìn)入管理的頁(yè)面之后我們找到設(shè)備管理器。
在設(shè)備管理器下我們找到一個(gè)手機(jī)驅(qū)動(dòng)的標(biāo)識(shí),如果這里安裝過(guò)了我們只需要自動(dòng)更新一下即可,
如果沒(méi)有安裝的話我們使用數(shù)據(jù)線連接電腦查看我們的磁盤有沒(méi)有手機(jī)磁盤。
都沒(méi)有的話我們需要找到手機(jī)品牌官網(wǎng),輸入自己的手機(jī)型號(hào)。
篇5
關(guān)鍵詞:VxWorks;USB設(shè)備驅(qū)動(dòng);管道;回調(diào)
中圖分類號(hào):TP316文獻(xiàn)標(biāo)識(shí)碼:A文章編號(hào):1009-3044(2008)24-1200-04
Design of USB Device Driver Based on Real Time Operation System VxWorks
WANG Hao
(College of Computer, Xidian University, Xi'an 710071, China)
Abstract:The architecture of USB dirver based on VxWorks is given, general method and key technology in developing USB device dirver are analyzed.Then the device driver of LM9833 is implemented, expectant performace of target system is achieved. The general process of developing USB device dirver used in this paper can be refered by others USB device driver developing based on VxWorks.
Key words: VxWorks; USB device driver; pipe; callback
1 VxWorks下USB驅(qū)動(dòng)概述
VxWorks是WindRiver公司開(kāi)發(fā)的具有工業(yè)領(lǐng)導(dǎo)地位的高性能實(shí)時(shí)操作系統(tǒng)(Real Time Operation System, RTOS)內(nèi)核。VxWorks5.5實(shí)現(xiàn)了USB1.1協(xié)議棧。圖1提供一個(gè)VxWorks下USB主驅(qū)動(dòng)棧的簡(jiǎn)單結(jié)構(gòu)。
在棧的最低層是USB主控制器(USB Host Controller, HC),這是主系統(tǒng)中控制每一個(gè)USB設(shè)備的硬件。在主控制器上層是一個(gè)與硬件獨(dú)立的主控制器驅(qū)動(dòng)(USB Host Controller Driver,HCD)。USBD是在HCD之上的與硬件獨(dú)立的模塊,USBD管理每一個(gè)與主機(jī)相連的設(shè)備,向高層提供可與USB設(shè)備通信的路徑,USBD實(shí)現(xiàn)了USB總線枚舉、總線帶寬分配、傳輸控制等操作。在棧的頂層是USB Client 模塊,一般是特定的USB Class Driver,負(fù)責(zé)管理與USB主機(jī)連接的不同類型的設(shè)備。用戶自己的USB設(shè)備驅(qū)動(dòng)程序通常是在USBD這一層上完成。
2 VxWorks下USB設(shè)備驅(qū)動(dòng)詳解
2.1 驅(qū)動(dòng)程序提供的函數(shù)
2.1.1 向應(yīng)用程序提供的接口函數(shù)
設(shè)備驅(qū)動(dòng)程序的主要作用是向上層應(yīng)用程序屏蔽硬件,向上層應(yīng)用程序提供統(tǒng)一的接口函數(shù),驅(qū)動(dòng)程序一般需要實(shí)現(xiàn)的函數(shù)如表1所示。
圖1 USB主驅(qū)動(dòng)棧結(jié)構(gòu)
表1 驅(qū)動(dòng)程序提供的接口
usbMSCDevInit();這是一個(gè)通用的初始化例程,可以在BSP中調(diào)用,也可以由應(yīng)用程序來(lái)調(diào)用,但只能被調(diào)用一次,該例程初始化必須的數(shù)據(jù)結(jié)構(gòu),并向USBD注冊(cè)驅(qū)動(dòng)程序。USB設(shè)備與USB主控制器之間的所有操作都通過(guò)USBD來(lái)完成,因此在調(diào)用usbMSCDevInit()之前必須確定USBD已經(jīng)初始化,在使用USB設(shè)備之前也要確保USB主控制器已經(jīng)掛接到USBD。
usbMSCDevCreate();這是一個(gè)創(chuàng)建設(shè)備例程,當(dāng)有設(shè)備接入時(shí),回調(diào)函數(shù)usbMSCDevAttachCallback()調(diào)用該例程創(chuàng)建一個(gè)邏輯設(shè)備,當(dāng)這個(gè)例程被調(diào)用時(shí)必須在系統(tǒng)中存在一個(gè)實(shí)際的USB物理設(shè)備。
usbMSCDevDestroy();從系統(tǒng)中刪除設(shè)備。首先釋放設(shè)備占有的資源,從設(shè)備鏈表中移除該設(shè)備,同時(shí)調(diào)用usbdPipeDestroy()銷毀該設(shè)備與主機(jī)之間的通信管道,最后釋放設(shè)備結(jié)構(gòu)體。
usbMSCDevShutDown();該例程卸載已注冊(cè)的設(shè)備驅(qū)動(dòng)程序,并回收所有已分配資源,包括刪除設(shè)備、回收信號(hào)量等。
2.1.2 兩個(gè)重要的回調(diào)函數(shù)
在編寫USB設(shè)備驅(qū)動(dòng)程序時(shí),除了上述接口函數(shù)外,還需要編寫另外兩個(gè)重要的回調(diào)函數(shù):usbMSCDevAttachCallback()和usbMSCIrpCallback()。usbMSCDevAttachCallback()用于跟蹤設(shè)備的請(qǐng)求實(shí)現(xiàn)動(dòng)態(tài)接入或刪除;usbMSCIrpCallback()是一個(gè)IRP callback,當(dāng)每一個(gè)IRP執(zhí)行完成之后調(diào)用該回調(diào)函數(shù),實(shí)現(xiàn)IRP之間同步。
2.2 設(shè)備標(biāo)識(shí)
在VxWorks中USB設(shè)備用USBD_NODE_ID來(lái)唯一區(qū)別,它是USBD用來(lái)跟蹤一個(gè)特定設(shè)備的句柄,它與USB設(shè)備的真正USB地址無(wú)關(guān)。這表明Client并不關(guān)心設(shè)備是物理上與哪一個(gè)USB主控制器連接,應(yīng)用為每個(gè)設(shè)備抽象一個(gè)Node ID,使Client可以不用考慮物理設(shè)備的連接細(xì)節(jié)以及USB地址分配。當(dāng)一個(gè)Client通知有一個(gè)設(shè)備連接或斷開(kāi)時(shí),USBD經(jīng)常通過(guò)Node Id來(lái)定位設(shè)備。同樣,當(dāng)USB設(shè)備與USBD通信時(shí),它必須向USBD傳遞該設(shè)備的Node Id。Node ID通常作為設(shè)備結(jié)構(gòu)體的一個(gè)重要成員。
2.3 回調(diào)(Callback)
USB操作是嚴(yán)格遵守時(shí)序的,WindRiver USBD采用回調(diào)機(jī)制來(lái)解決時(shí)序問(wèn)題。例如在USBD識(shí)別一個(gè)動(dòng)態(tài)連接事件之后會(huì)激活一個(gè)動(dòng)態(tài)attach callback操作,這是一個(gè)注冊(cè)到USBD的回調(diào)例程,回調(diào)函數(shù)會(huì)判斷當(dāng)前的操作,如果是接入(USBD_DYNA_ATTACH),就會(huì)調(diào)用usbMSCDevCreate()來(lái)在當(dāng)前的USB句柄上創(chuàng)建一個(gè)邏輯設(shè)備結(jié)構(gòu)體;如果是移出(USBD_DYNA_REMOVE)就會(huì)調(diào)用usbMSCDevDestroy()來(lái)刪除設(shè)備。同樣,當(dāng)USBD處理完成一個(gè)USB IRP之后,Client的一個(gè)IRP callback被激活,該回調(diào)例程是由Irp.userCallback域來(lái)指定。在IRP callback中釋放IRP同步信號(hào)量。
2.4 數(shù)據(jù)傳輸
USB設(shè)備與主機(jī)之間是通過(guò)USB管道進(jìn)行通信的。一旦Client配置完一個(gè)設(shè)備,就可以利用USBD提供的管道(pipe)傳輸功能與設(shè)備進(jìn)行數(shù)據(jù)交換。管道是建立在USBD Client與設(shè)備特定的endpoint之間的單向通道。調(diào)用usbdPipeCreate()函數(shù)創(chuàng)建一個(gè)管道后返回一個(gè)管道句柄USBD_PIPE_HANDLE,以后所有的讀寫操作都分別在各自的管道句柄上進(jìn)行。管道在剛創(chuàng)建完后是處于終止的狀態(tài),為了能有效使用它們,還必須用usbdFeatureClear()來(lái)清除該終止?fàn)顟B(tài)。每一個(gè)管道有以下性質(zhì):USBD_NODE_ID、端點(diǎn)地址,管道類型、數(shù)據(jù)流方向、包的最大有效載荷量、帶寬需求等。對(duì)于塊傳輸沒(méi)有帶寬限制。VxWorks的USB驅(qū)動(dòng)程序的各層驅(qū)動(dòng)程序間通過(guò)IRP機(jī)制進(jìn)行通信,當(dāng)有數(shù)據(jù)傳輸請(qǐng)求發(fā)生時(shí),上層向下傳遞IRP。USBD得到請(qǐng)求后,調(diào)用HCD接口,將IRP轉(zhuǎn)化為usb的傳輸。這就需要提供一個(gè)特殊的回調(diào)函數(shù)usbMSCIrpCallback(),當(dāng)塊傳輸結(jié)束時(shí)調(diào)用該回調(diào)函數(shù)。以下是讀設(shè)備的具體過(guò)程。
1) 創(chuàng)建設(shè)備時(shí)創(chuàng)建out和in管道:
usbdPipeCreate(usbdHandle, NodeId, outEpAddress, configuration, interface, USB_XFRTYPE_BULK, USB_DIR_OUT,maxPacketSize,0,0, outPipeHandle);
usbdPipeCreate(usbdHandle, NodeId, inEpAddress, configuration, interface, USB_XFRTYPE_BULK, USB_DIR_IN,maxPacketSize,0,0, inoutPipeHandle);
2) 定義IRP callback:usbMSCIrpCallback(pVOID p)。
3) 構(gòu)造USB_IRP outIrp請(qǐng)求包。
4) 提交outIrp:usbdTransfer (usbdHandle, outPipeHandle, &outIrp)。讀命令在outIrp.bfrList[0].pBfr域中。
5) 等待outIrp處理結(jié)束,結(jié)束調(diào)用usbMSCIrpCallback()釋放IRP同步信號(hào)量。
6) 構(gòu)造USB_IRP inIrp請(qǐng)求包。
7) 提交inIrp:usbdTransfer (usbdHandle, inPipeHandle, &inIrp)。
8) 等待inIrp處理結(jié)束,讀取的數(shù)據(jù)在inIrp.bfrList[0].pBfr域中。
2.5 多個(gè)設(shè)備管理
驅(qū)動(dòng)程序用LIST_HEAD 來(lái)存儲(chǔ)多個(gè)設(shè)備,每個(gè)接入的USB設(shè)備利用其LINK節(jié)點(diǎn)加入到LIST_HEAD鏈表當(dāng)中;一個(gè)LINK節(jié)點(diǎn)包含三部分:linkFwd指針、linkBack指針和pStruct指針;其中pStruct指向一個(gè)待連接的設(shè)備結(jié)構(gòu)體。當(dāng)有一個(gè)設(shè)備創(chuàng)建完成時(shí)調(diào)用usbListLink()將該設(shè)備加入鏈表,刪除設(shè)備之前調(diào)用usbListUnlink()從鏈表中移出該設(shè)備。由于USB設(shè)備是用Node ID來(lái)唯一標(biāo)識(shí)的,在查找設(shè)備的時(shí)需要利用usbListNext()來(lái)遍歷設(shè)備鏈表,直到某個(gè)設(shè)備的Node ID與特定設(shè)備的Node ID相匹配為止。在多個(gè)設(shè)備管理時(shí),采用USBD_NODE_ID usbd_scan_id[DEVICE_NUM]數(shù)組來(lái)存放多個(gè)設(shè)備的Node ID,在對(duì)設(shè)備進(jìn)行讀寫時(shí)只需提供設(shè)備的索引序號(hào)就可以直接得到設(shè)備Node ID,提高了對(duì)設(shè)備的訪問(wèn)速度。
3 VxWorks下LM9833驅(qū)動(dòng)程序?qū)崿F(xiàn)
3.1 LM9833 USB接口簡(jiǎn)介
LM9833掃描儀控制器在一個(gè)單獨(dú)的IC上可以提供一個(gè)完整的USB圖像掃描控制系統(tǒng)。它的USB接口提供4個(gè)USB端點(diǎn)(Control Endpoint,Interrupt Endpoint,Bulk In Endpoint和Bulk Out Endpoint),內(nèi)部有00~7F個(gè)寄存器,其中00寄存器是存放一個(gè)8bits的圖象數(shù)據(jù),其它分別為控制或狀態(tài)寄存器。通過(guò)向bulk out端點(diǎn)發(fā)送四字節(jié)的讀命令可以從bulk in端點(diǎn)讀取這些寄存器的值,也可以向bulk out端點(diǎn)發(fā)送四字節(jié)的寫命令加待寫數(shù)據(jù)完成寫寄存器。四字節(jié)命令依次表示:讀寫模式(1字節(jié))、起始地址(1字節(jié))、讀寫長(zhǎng)度(2字節(jié))。其中讀寫模式bit0為0表示寫,1表示讀;bit1為0表示非增模式,為1表示增模式,即讀寫寄存器完成之后寄存器地址加1。LM9833的工作過(guò)程就是通過(guò)讀寫這些寄存器來(lái)完成的。
3.2 設(shè)備描述符結(jié)構(gòu)
typedef struct _usbScanDev
{
USBD_NODE_ID scanDevId; /* USBD node ID of the device */
UINT16 configuration; /* Configuration value */
UINT16 interface; /* Interface number */
UINT16 altSetting; /* Alternate setting of interface */
UINT16 outEpAddress; /* Bulk out EP address */
UINT16 inEpAddress; /* Bulk in EP address */
USBD_PIPE_HANDLE outPipe; /* Pipe handle for Bulk out EP */
USBD_PIPE_HANDLE inPipe; /* Pipe handle for Bulk in EP */
USB_IRP inIrp; /* IRP used for bulk-in data */
USB_IRP outIrp; /* IRP used for bulk-out data */
USB_IRP statusIrp; /* IRP used for reading status */
UINT8 * scanInData; /* Pointer for bulk-in data */
UINT8 * scanOutData; /* Pointer for bulk-out data */
BOOL connected; /* TRUE if USB_SCAN device connected*/
LINK scanDevLink; /* Link to other SCAN devices */
UINT8 CommandByte[4]; /* Which read/write command the device */
UINT16 actBytes; /* actual bytes will be transfered */
UINT8 direction; /* data transfer direction */
} USB_SCAN_DEV, *pUSB_SCAN_DEV;
設(shè)備描述符結(jié)構(gòu)中包含了設(shè)備的一些重要信息和訪問(wèn)該設(shè)備時(shí)的必須資源,如Node ID、IN/OUT管道、IN/OUT IRP等等。
3.3 注冊(cè)設(shè)備(LM9833)驅(qū)動(dòng)程序
注冊(cè)驅(qū)動(dòng)程序一般包含兩大步:與USBD建立連接和注冊(cè)attach callback。以下是注冊(cè)LM9833驅(qū)動(dòng)程序的源代碼。
#define USB_SCAN_CLASS 0xff
#define USB_SCAN_SUB_CLASS0x00
#define USB_SCAN_DRIVE_PROTOCOL0xff
STATUS usbScanDevInit()
{……
if(usbdClientRegister ("SCAN_CLASS", &usbdHandle)!=OK||
usbdDynamicAttachRegister (usbdHandle, USB_SCAN_CLASS, USB_SCAN_SUB_CLASS,
USB_SCAN_DRIVE_PROTOCOL, usbScanDevAttachCallback) != OK)
……
}
usbScanDevInit()調(diào)用usbdClientRegister()向USBD注冊(cè)一個(gè)名為SCAN_CLASS的Client,同時(shí)調(diào)用usbdDynamicAttachRegister()向USBD注冊(cè)一個(gè)回調(diào)例程usbScanDevAttachCallback (),跟蹤該Client請(qǐng)求,當(dāng)設(shè)備動(dòng)態(tài)地接入或移出系統(tǒng)時(shí)會(huì)自動(dòng)地調(diào)用該回調(diào)函數(shù)。一個(gè)Client在利用usbdDynamicAttachRegister()進(jìn)行注冊(cè)時(shí)只對(duì)特定的class、subclass、protocol感興趣。在成功注冊(cè)Client后,USBD返回一個(gè)Client句柄(USBD_CLIENT_HANDLE),以后對(duì)USBD的調(diào)用都會(huì)用到這個(gè)句柄。Attach callback 如下。
LOCAL VOID usbScanDevAttachCallback
(
USBD_NODE_ID nodeId,
UINT16 attachAction,
UINT16 configuration,
UINT16 interface,
UINT16 deviceClass,
UINT16 deviceSubClass,
UINT16 deviceProtocol
)
該回調(diào)函數(shù)主要響應(yīng)外部設(shè)備的動(dòng)作,實(shí)現(xiàn)USB設(shè)備的動(dòng)態(tài)接入和移除。
3.4 創(chuàng)建設(shè)備
創(chuàng)建設(shè)備函數(shù)如下:
LOCAL STATUS usbScanPhysDevCreate(USBD_NODE_ID nodeId, UINT16 configuration, UINT16 interface)
當(dāng)接入設(shè)備時(shí)激活usbScanDevAttachCallback()操作,回調(diào)函數(shù)會(huì)根據(jù)接入(USBD_DYNA_ATTACH)動(dòng)作調(diào)用usbScanPhysDevCreate()創(chuàng)建一個(gè)邏輯設(shè)備,在創(chuàng)建設(shè)備的同時(shí),創(chuàng)建設(shè)備與USB主機(jī)之間通信的管道(pipe)。管道是建立在USB設(shè)備端點(diǎn)(endpoint)之上,是主機(jī)與設(shè)備之間數(shù)據(jù)傳輸?shù)膯蜗蛲ǖ馈TO(shè)備與主機(jī)之間數(shù)據(jù)傳輸管道是建立在批量端點(diǎn)(bulk endpoint)之上,有BULK_IN和BULK_OUT兩個(gè)端點(diǎn),從而建立雙向的數(shù)據(jù)通路。最后將該設(shè)備加入設(shè)備鏈表,進(jìn)行多個(gè)設(shè)備的管理。創(chuàng)建設(shè)備流程如圖2所示。
圖2 創(chuàng)建設(shè)備流程
3.5 讀寫設(shè)備
對(duì)設(shè)備進(jìn)行讀寫時(shí),首先需要將讀寫函數(shù)轉(zhuǎn)換成設(shè)備能夠識(shí)別的命令,對(duì)于LM9833來(lái)說(shuō),需將讀寫函數(shù)轉(zhuǎn)換成LM9833所能識(shí)別的四字節(jié)讀寫命令,讀寫時(shí)將這四字節(jié)的命令置于IRP包數(shù)據(jù)域的最前端,這樣到數(shù)據(jù)到達(dá)設(shè)備時(shí)首先接收到的是四個(gè)字節(jié)的命令,LM9833會(huì)根據(jù)這四個(gè)字節(jié)的命令完成相應(yīng)的功能。讀寫函數(shù)原型為:
STATUS usbScanRead/usbScanWrite
(
UINT dev, /* sequence number of the device */
UINT Addr, /* start address in register */
UINT8 *pBuffer, /* pBuffer to receive/send data from/to device*/
UINT Len, /* lenth of pBuffer */
BOOL bIncrement /* incremece of address in register or not */
)
LM9833的一個(gè)讀寫控制流程如圖3所示,查找設(shè)備流程如圖4所示。
圖3 LM9833讀寫控制流程
圖4 查找設(shè)備流程
設(shè)備命令組幀:
const unsigned int MODE_INC_READ = 0x03;
const unsigned int MODE_NOINC_READ = 0x01;
const unsigned int MODE_INC_WRITE = 0x02;
const unsigned int MODE_NOINC_WRITE = 0x00;
switch (Cmd)
{
case USB_SCAN_WRITE:/* bulk out */
pScanDev->CommandByte[0] = (bIncrement>0)? MODE_INC_WRITE : MODE_NOINC_WRITE;
pScanDev->CommandByte[1] = Addr+((bIncrement>0)? i : 0);
pScanDev->CommandByte[2] = (Len >> 8); /* length of the data to be written */
pScanDev->CommandByte[3] = (Len & 0xff);
memcpy(pScanDev->scanOutData, pScanDev->CommandByte, 4); /* 4 bytes Lm9833 command followed by write data */
memcpy(pScanDev->scanOutData + 4, pBuffer + i, Len);
pScanDev->actBytes = Len+4; /* actual length to be transfered*/
pScanDev->direction = USB_SCAN_DIR_OUT;
break;
case USB_SCAN_READ: /* bulk in */
篇6
引言
近年來(lái)電力行業(yè)為了快速部署變電站,采用了建造整體變電所的方法:在生產(chǎn)基地將變電站的內(nèi)部設(shè)備安裝、調(diào)試完成,只留下與外界的接口,整體運(yùn)到變電站所在地后進(jìn)行安裝和簡(jiǎn)單調(diào)試即可投入運(yùn)行。其內(nèi)部設(shè)備通過(guò)CAN總線進(jìn)行通信,系統(tǒng)原有的監(jiān)控軟件基于DOS系統(tǒng),維護(hù)調(diào)試比較困難,因此想要尋求更方便、友好的系統(tǒng)支持。經(jīng)過(guò)比較,嵌入式操作系統(tǒng)市場(chǎng)上風(fēng)頭正勁的Windows CE .NET成為最終選擇。微軟的最新產(chǎn)品Windows CE.NET提供了端對(duì)端的開(kāi)發(fā)、調(diào)試手段,可以不拆卸設(shè)備的情況下通過(guò)Telnet登錄到WindowsCE上進(jìn)行調(diào)試和維護(hù),其系統(tǒng)本身為嵌入式市場(chǎng)進(jìn)行重新設(shè)計(jì),包括創(chuàng)建一個(gè)基于WindowsCE的定制設(shè)備所需的一切。這樣就需要將原來(lái)DOS下的程序移植到WindowsCE.NET下,但是各個(gè)硬件廠商目前還沒(méi)有提供CAN通信卡在Windows CE.NET下的驅(qū)動(dòng),所以開(kāi)發(fā)Windows CE.NET下的CAN卡驅(qū)動(dòng)成為項(xiàng)目推行中的關(guān)鍵一環(huán)。
本文主要針對(duì)研華的雙口CAN卡PCM3680進(jìn)行分析,介紹在WindowsCE.ENT系統(tǒng)下進(jìn)行底層設(shè)備驅(qū)動(dòng)開(kāi)發(fā)的方法并提供CAN通信的實(shí)例。
1 CAN總線通信協(xié)議及CAN通信卡介紹
CAN總線是德國(guó)Bosch公司20世紀(jì)80年代初為解決現(xiàn)代汽車中眾多的控制與測(cè)試儀器之間的數(shù)據(jù)交換而開(kāi)的一種串行數(shù)據(jù)通信協(xié)議。它是一種多主總線,廢除了傳統(tǒng)的站地址編碼,而代之以對(duì)通信數(shù)據(jù)塊進(jìn)行編碼。這種方法使網(wǎng)絡(luò)內(nèi)節(jié)點(diǎn)個(gè)數(shù)在理論上不受限制,擴(kuò)展格式中的29位的標(biāo)識(shí)碼便可以定義2 29個(gè)不同的數(shù)據(jù)塊。
在本項(xiàng)目中使用的是研華的PCM3680,這是一塊嵌入式PC104的雙口CAN總線通信卡;CAN控制器采用Philips的獨(dú)立CAN控制器SJA1000芯片;CAN收發(fā)器采用Philips的P82C250,可以同時(shí)操作兩個(gè)CAN網(wǎng)絡(luò),提供高達(dá)1Mb/s的傳輸速度。PCM3680支持很寬的中斷范圍:中斷3、4、5、6、7、9、10、11、12、15,同時(shí)1000V的光電隔離提供系統(tǒng)高可靠性。在CAN卡通信中,要用到CAN控制器中的很多寄存器,各個(gè)寄存器的含義和作用可以參考控制芯片的說(shuō)明書。圖1列出驅(qū)動(dòng)程序設(shè)計(jì)中用到最主要的寄存器結(jié)構(gòu)。
2 CAN卡驅(qū)動(dòng)底層函數(shù)設(shè)計(jì)
本方案設(shè)計(jì)CAN驅(qū)動(dòng)是放在Windows CE操作系統(tǒng)的內(nèi)核下層,位于OEM adaptation layer(OAL)層的一個(gè)真正的驅(qū)動(dòng),而不是在主程序中的串口操作。在Windows CE的設(shè)備管理器可以看到CAN1和CAN2兩個(gè)端口,并且可以查看其工作的正常與否和對(duì)其進(jìn)行配置。如:中斷號(hào)和I/O地址。
2.1 CAN卡寄存器讀寫函數(shù)
CAN卡的通信是通過(guò)操作CAN卡上的CAN控制器進(jìn)行的。在CAN控制器中有很多寄存器,如控制寄存器、命令寄存器、狀態(tài)寄存器、中斷寄存器等,通過(guò)讀寫這些寄存器中的命令狀態(tài)字可以檢測(cè)和控制CAN卡的行為。在Windows CE.NET下,通過(guò)調(diào)用DOK中的API函數(shù)HalTranslateBusAddress,將CAN卡分配的物理地址映射為邏輯地址。這樣各個(gè)寄存器對(duì)應(yīng)的就是CAN卡基地址的偏移地址,因此,對(duì)寄存器的讀寫就轉(zhuǎn)化為對(duì)內(nèi)存地址的讀寫。下面是CAN卡寄存器的讀寫函數(shù):
*在偏移量為off的地址讀取一個(gè)字節(jié)的數(shù)據(jù)inline BYTE CANR(LPCAN_HW_OPEN_INFO hCan,DWORD off)
{
return hCan->lpCanHWInfo->lpCanObj->lpMappedBaseAddr[off];
*將一個(gè)字節(jié)數(shù)據(jù)寫到偏移量為off的地址中inline VOID CANW(LPCAN_HW_OPEN_INFO hCan,DWORD off,BYTE val)
{
hCan->lpCanHWInfo->lpCanObj->lpMappedBaseAddr[off]=val;
}
參數(shù)LPCAN_HW_OPEN_INFO定義的是CAN卡的數(shù)據(jù)結(jié)構(gòu),其中成員lpMappeBaseAddr[0]表示的是映射后基地址,lpMappedBaseAddr[1]就是基地址+1的地址,對(duì)應(yīng)CAN卡的寄存器是命令寄存器。通過(guò)上述兩個(gè)函數(shù)可操作CAN卡上的所有寄存器。
2.2 CAN卡初始化
CAN卡的控制器比較復(fù)雜,在通信前必須確認(rèn)硬件信息正確性、初始化各寄存器。初始化函數(shù)的基本流程如圖3所示。
第一步,檢查端口號(hào)和硬件信息的正確性,主要是CAN卡中斷號(hào)是否有效。
第二卡,設(shè)置CAN卡默認(rèn)參數(shù):
CanCardConfigInfo CAN_DEFAULT_SETTING=
{0X00,0XFF,0X03,0X1C};/*設(shè)置默認(rèn)波特率為125Kbps*/
DWORD dwThreadID =0;
PHYSICAL_ADDRESS phyAddr={hwInfo->dwIOBaseAddr *16,0 };
第三卡,用WinCE API函數(shù)LocalAlloc為CAN卡驅(qū)動(dòng)中用到的數(shù)據(jù)結(jié)構(gòu)分配緩沖區(qū);通過(guò)HalTranslateBusAddress和MmMapIoSpace函數(shù)映射I/O地址,提供直接訪問(wèn)設(shè)備的虛擬地址:
if(!HalTranslateBusAddress(Isa,0,phyAddr,0,&phyAddr))
goto _ExitInit;
hCan->lpCanHWInfo->lpCanObj->lpMappedBaseAddr=
(LPBYTE)MmMapIoSpace(phyAddr,CANCARDADDRLEN,FALSE);
if(!hCan->lpCanHWInfo->lpCanObj->lpMappedBaseAddr)
goto _ExitInit;
如果分配內(nèi)存或映射邏輯地址失敗,則退出初始化程序,CAN卡初始化失敗。
第四步,初始化讀寫屬性、共享模式、讀超時(shí)時(shí)間和第二個(gè)CAN口的基地址。
第五步,創(chuàng)建CAN卡事件和數(shù)據(jù)接收事件:hCan->lpCanHWInfo->hCanEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
hCan->lpCanHWInfo->hRecvMsgEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
第六步,初始化中斷,如果CAN卡有復(fù)位請(qǐng)求就退出初始化程序。設(shè)置好中斷后啟動(dòng)數(shù)據(jù)接收線程,設(shè)置線程優(yōu)先級(jí)繼續(xù)線程處理;最后配置CAN卡參數(shù),進(jìn)入正常運(yùn)行狀態(tài)。
2.3 CAN卡信息發(fā)送
CAN卡的信息發(fā)送分為兩個(gè)步驟。在對(duì)CAN卡基本信息進(jìn)行檢查后,首先設(shè)置發(fā)送緩沖的ID號(hào)。CAN標(biāo)準(zhǔn)模式的ID號(hào)為11位,偏移地址10中存放的是ID號(hào)的高8位,偏移地址11的高3位存放的是ID號(hào)的低3位,剩下5位分別是RTR位(遠(yuǎn)程傳送請(qǐng)求位)和數(shù)據(jù)長(zhǎng)度。通過(guò)CANW函數(shù)將處理后的數(shù)據(jù)寫入到相應(yīng)的偏移地址,設(shè)置完相應(yīng)的地址數(shù)據(jù)后,通過(guò)循環(huán)將偏移地址12~19的數(shù)據(jù)采集回來(lái)存到數(shù)組中。然后,設(shè)置CAN卡的傳輸請(qǐng)求為允許并不斷偵測(cè)狀態(tài)寄存器的變化,當(dāng)傳輸緩沖滿標(biāo)志或傳輸結(jié)束標(biāo)志為1時(shí)通出程序,完成一次數(shù)據(jù)采集。傳輸緩沖區(qū)的寄存器如表1所列。
表1
ID號(hào)10ID.10ID.9ID.8ID.7ID.6ID.5ID.4ID.3RTR,數(shù)據(jù)長(zhǎng)度碼11ID.2ID.1ID.0RTRDLC.3DLC.2DLC.1DLC.0數(shù)據(jù)1~812~19數(shù)據(jù)數(shù)據(jù)數(shù)據(jù)數(shù)據(jù)數(shù)據(jù)數(shù)據(jù)數(shù)據(jù)數(shù)據(jù)表2
ID號(hào)20ID.10ID.9ID.8ID.7ID.6ID.5ID.4ID.3RTR,數(shù)據(jù)長(zhǎng)度碼21ID.2ID.1ID.0RTRDLC.3DLC.2DLC.1DLC.0數(shù)據(jù)1~822~29數(shù)據(jù)數(shù)據(jù)數(shù)據(jù)數(shù)據(jù)數(shù)據(jù)數(shù)據(jù)數(shù)據(jù)數(shù)據(jù)CAN消息發(fā)送函數(shù)的實(shí)現(xiàn)如下:
BOOL CAN_SendMessage(LPCAN_HW_OPEN_INFO hCan,LPCanCardMessageBuflpMsg)
{
BOOL bSuc=FALSE;
ASSERT(hCan && lpMsg && lpMsg->dwMessageLen <=8); /*防錯(cuò)處理*/
if(0= =(hCan->dwAccessCode & GENERIC_WRITE))
return FALSE;
:: EnterCriticalSection(&hCan->lpCanHWInfo->
TransmitCritSec); /*進(jìn)入臨界區(qū)*/
BYTE byV=static_cast<BYTE>(1pMsg->dwMsgID>>3);
CANW(hCan,10,byV); /*設(shè)置ID值高8位*/
byV=static_cast<BYTE>=((lpMsg->dwMsgID & 7)<<5);
if(lpMsg->bRTR) byV|=0x10;
byV+=static_cast<BYTE>(lpMsg->dwMessageLen);
CANW(hCan,11,byV);/*設(shè)置ID值低3位、RTR及數(shù)據(jù)長(zhǎng)度*/
for(UINT i=0;<lpMsg->dwMessageLen;++i)
{
CANW(hCan,12+i,lpMsg->byMsg[i]);
} /*采集數(shù)據(jù)*/
CANW(hCan,1,1);/*重置傳輸請(qǐng)求*/
while(TRUE)
{byV=CANR(hCan,2);
if(byV & 0X40) /*傳輸緩沖區(qū)滿,退出*/
{break;}
if(byV & 0X8){ /*傳輸結(jié)束,正確返回退出*/
bSuc = TRUE;
break;}
}
::LeaveCriticalSection(&hCan->lpCanHWInfo->TransmitCritSec); /*離開(kāi)臨界區(qū)*/
return bSuc;
}
2.4 CAN卡信息接收
CAN卡的信息接收是發(fā)送的逆過(guò)程,當(dāng)接收緩沖區(qū)標(biāo)志為1時(shí),表示緩沖區(qū)已滿可以接收數(shù)據(jù),將數(shù)據(jù)接收到數(shù)組后釋放接收緩沖區(qū),然后對(duì)接收到的數(shù)據(jù)進(jìn)行分解并存儲(chǔ)到CAN卡信息緩沖區(qū)的結(jié)構(gòu)體。接收緩沖區(qū)的寄存器結(jié)構(gòu)如表2所列。
CAN消息接收函數(shù)的實(shí)現(xiàn)如下:
BOOL CAN_RecvRecvMessage(LPCAN_HW_OPEN_INFO
HCan,OUT LPCanCardMessageBuflpMsg)
{……
if(CANR(hCan,2)&1){ /*判斷接收緩沖區(qū)是否已滿*/
for(UINT i=0;i<10;++i)
recvBuf[i]=CANR(hCan,20+i);/*將數(shù)據(jù)暫存到臨時(shí)緩沖區(qū)*/
CANW(hCan,1,4); /*釋放接收緩沖區(qū)*/
LpMsg->dwMsgID=recvBuf[0]<<3; /*取出ID的高8位*/
BYTE byV =recvBuf[1];
LpMsg->dwMsgID+=byV >>5;/*取出ID低3位,然后和高8位合并*/
LpMsg->bRTR =byV &0x10?TRUE:/*返回RTR狀態(tài)*/
LpMsg->dwMessageLen = byV &0XF; /*返回?cái)?shù)據(jù)長(zhǎng)度*/
……
}
else
{++hCan->lpCanHWInfo->dwErrorMsgCount;}/*沒(méi)有收到數(shù)據(jù),錯(cuò)誤計(jì)數(shù)加1*/
::LeaveCriticalSection(&hCan->lpCanHWInfo->
ReceiveCritSec); /*離開(kāi)臨界區(qū)*/
Return bSuc;
}
2.5 CAN卡事件處理
CAN卡事件處理函數(shù)是CAN卡驅(qū)動(dòng)程序中很重要的部分。驅(qū)動(dòng)設(shè)計(jì)要求具有消息通知的功能,當(dāng)事件發(fā)生時(shí)及時(shí)捕獲事件并進(jìn)行消息處理。
下面是事件處理函數(shù)的實(shí)現(xiàn):
staric DWORD WINAPI CAN_EventHanle(LPVOID lpParam)
{
ASSERT(lpParam);
LPCAN_HW_OPEN_INFO hCan=(LPCAN_HW_OPEN_INFO)lpParam;
CanCardMessageBuf bufMsg;
while(TEUE)
{ /*循環(huán)等待CAN卡消息產(chǎn)生,然后進(jìn)行處理*/
::WaitForSingleObject(hCan->lpCanHWInfo->hCanEvent,0XFFFFFFFF);
if(hCan->lpCanHWInfo->bKillCanThread) break; /*若CAN線程已關(guān)閉則中斷*/
if(CAN_RecvMessage(hCan,&hufMsg)){ /*正確接收數(shù)據(jù)后*/
CAN_RecvBufPush(hCan,&bufMsg);} /*將數(shù)據(jù)壓入緩沖*/
BYTE byV=CANR(hCan,3); /*將3號(hào)寄存器讀出然后立即寫入*/
CANW(hCan,3,byV);/*能夠獲取每次中斷*/
InterruptDone(hCan->lpCanHWInfo->lpCanObj->dwSysIrqt);
} /*本次中斷結(jié)束,等待下次中斷*/
return 0;
}
2.6 其它函數(shù)
為了提供更多的功能和更方便地使用CAN卡進(jìn)行通信,在CAN卡驅(qū)動(dòng)程序中還設(shè)計(jì)了一些函數(shù)如CAN_Config用CAN卡信息配置、CAN_RecvBufPop用于處理接收緩沖區(qū)、CAN_Reset用于復(fù)位CAN卡、CheckHWInfo用于硬件信息檢查等。這些函數(shù)提供了對(duì)CAN通信卡的設(shè)置、檢查等功能,在這里不再詳述了。
3 CAN卡驅(qū)動(dòng)封裝設(shè)計(jì)
CAN卡底層驅(qū)動(dòng)函數(shù)雖然功能完整,但是對(duì)于用戶使用比較復(fù)雜并且一般用戶不需要了解底層實(shí)現(xiàn)的機(jī)制。為了便于使用,最后對(duì)CAN卡的驅(qū)動(dòng)進(jìn)行了封裝,提供CanOpenFile、CanSendMsg等五個(gè)函數(shù)用于CAN總線的通信,以動(dòng)態(tài)連接庫(kù)(DLL)的形式提供給用戶調(diào)用。封裝函數(shù)及功能如下:
*CanOpenFile;初始化并打開(kāi)CAN卡的一個(gè)端口。
*CanCloseFile;關(guān)閉由CanOpenFile打開(kāi)的CAN卡端口。
*CanRecvMsg;接收CAN卡數(shù)據(jù),打開(kāi)CAN卡時(shí)必須具有GENERIC_READ權(quán)限。
*CanSendMsg;通過(guò)CAN卡發(fā)送數(shù)據(jù)。打開(kāi)CAN卡時(shí)必須具有GENERIC_WRITE權(quán)限。
*CanIOControl;設(shè)置或獲取CAN卡I/O參數(shù)支持的I/O控制包括:IOCTL_CAN_CONFIG,IOCTL_CAN_RESET,IOCTL_CAN_TIMEOUT,IOCTL_CAN_SENDREADY,IOCTL_CAN_RECVREADY。
下面是CanSendMsg函數(shù)實(shí)現(xiàn)的代碼:
BOOL CanSendMSg(
HANDLE hCan,
LPCanCardMessageBuflpMsg)
{
if(!hCan||INVALID_HANDLE_VALUE= =hCan||
!lpMsg||lpMsg->dwMessageLen>8)return FALSE;
return CAN_SendMessage(LPCAN_HW_OPEN_INFO)
hCan,lpMsg);
該函數(shù)就是通過(guò)封裝CAN卡的底層驅(qū)動(dòng)函數(shù)SendMessage來(lái)實(shí)現(xiàn)的,這樣將功能集中的五個(gè)函數(shù)更方便了用戶使用。
結(jié)語(yǔ)
篇7
通信卡的硬件設(shè)計(jì)
1 通信卡的主要特點(diǎn)
通信卡的硬件設(shè)計(jì)目的是智能化通信:在卡上具有4個(gè)DMA通道及相應(yīng)的數(shù)據(jù)緩沖存儲(chǔ)區(qū)。在數(shù)據(jù)的接收過(guò)程中,通信卡會(huì)自動(dòng)接收幀數(shù)據(jù),判別幀長(zhǎng)度,在幀尾將接收到的幀數(shù)據(jù)提交系統(tǒng)。在發(fā)送數(shù)據(jù)過(guò)程中,系統(tǒng)只須把發(fā)送的數(shù)據(jù)提交給本卡,具體的發(fā)送過(guò)程由該卡自行完成,不因多路通信而使主機(jī)增加開(kāi)銷。該卡有內(nèi)/外時(shí)鐘兩種工作方式,近距離可省去調(diào)制解調(diào)器,波特率為600b/s-64Kb/s,提供2路符合RS-232/CCITT V.24和RS-422A標(biāo)準(zhǔn)的接口信號(hào)。
2 通信卡的電路設(shè)計(jì)
通信卡組成框圖如圖1所示。其主要由DMA控制器、總線競(jìng)爭(zhēng)仲裁器、串行通信控制器、數(shù)據(jù)緩沖存儲(chǔ)器SRAM、存儲(chǔ)器I/O映像和物理地址產(chǎn)生器、總線接口及防護(hù)、波特率產(chǎn)生器、接口電路等單元電路組成。
DMA控制器作為通信卡的主設(shè)備,控制卡上的數(shù)據(jù)接收和發(fā)送(來(lái)自通信控制器和CPU),并負(fù)責(zé)仲裁優(yōu)先權(quán)。由于在進(jìn)行數(shù)據(jù)通信時(shí),允許主機(jī)CPU訪問(wèn)本卡SRAM,且主機(jī)對(duì)其中某一路發(fā)控制命令時(shí),不影響其余三路通信,這樣將會(huì)使主機(jī)CPU與本卡DMA競(jìng)爭(zhēng)本卡總線控制權(quán)。而總線上沒(méi)有給出主機(jī)CPU指令排隊(duì)狀態(tài)序列,故不能直接設(shè)計(jì)多主競(jìng)爭(zhēng),因而在本卡總線上用門陣列設(shè)計(jì)了一個(gè)狀態(tài)機(jī),不斷地監(jiān)視總線爭(zhēng)用情況,完成本卡總線競(jìng)爭(zhēng)、仲裁功能。完成HDLC規(guī)程的串行通信的器件是INTEL 8274多規(guī)程串行控制器,它能完成2個(gè)獨(dú)立的串行接收/發(fā)送全雙工通信。為了使4個(gè)信道在一幀數(shù)據(jù)的發(fā)送或接收過(guò)程中,主機(jī)不干預(yù),故將發(fā)送的一幀數(shù)據(jù)在發(fā)送開(kāi)始前由CPU用批命令放入卡上的SRAM中,同樣,在接收過(guò)程中,卡上DMA將接收到的數(shù)據(jù)放入SRAM中,等一幀接收完成后,再用批命令取出,放入系統(tǒng)存儲(chǔ)器中??ㄉ蟂RAM不占用主機(jī)內(nèi)存地址,采用I/O映像,該適配器插入主機(jī)之后,主機(jī)通過(guò)系統(tǒng)I/O地址對(duì)其訪問(wèn),這由地址產(chǎn)生器完成??偩€接口包括數(shù)據(jù)收發(fā)器,數(shù)據(jù)開(kāi)關(guān)電路,讀、寫及中斷等控制電路,地址譯碼等。波特率產(chǎn)生器提供了一個(gè)可編程的時(shí)鐘信號(hào)發(fā)生器,用戶可自行設(shè)置通信速率,供工作于內(nèi)時(shí)鐘方式時(shí)使用。接口電路提供符合RS-232/C CITT V.24和RS-422A標(biāo)準(zhǔn)的接口信號(hào)。
3 通信流程設(shè)計(jì)
如果要發(fā)送一幀數(shù)據(jù),CPU用批命令將數(shù)據(jù)放到本卡SRAM中,然后設(shè)置相應(yīng)的發(fā)送DMA通道。DMA通道是非自動(dòng)重裝方式,啟動(dòng)8274控制器發(fā)送,然后CPU就不需要管理,由卡上硬件自動(dòng)發(fā)送。
如果需要接收數(shù)據(jù),啟動(dòng)接收通道。接收通路自動(dòng)搜索輸入信號(hào),搜索到數(shù)據(jù)幀時(shí),由卡上DMA控制器來(lái)管理,將接收的數(shù)據(jù)放人本卡SRAM中,并向CPU請(qǐng)求中斷,讀出數(shù)據(jù)。
4 總線競(jìng)爭(zhēng)設(shè)計(jì)
由于同時(shí)可進(jìn)行四路通信,且CPU可隨時(shí)對(duì)某一路發(fā)控制命令或訪問(wèn)卡上SRAM存儲(chǔ)器,就將出現(xiàn)CPU與本卡主設(shè)備DMA爭(zhēng)用本卡總線的狀況。由于ISA總線沒(méi)能給出CPU指令排隊(duì)狀態(tài)序列,這給總線仲裁帶來(lái)了困難,而本卡的總線競(jìng)爭(zhēng)、仲裁是由可編程邏輯器件設(shè)計(jì)的狀態(tài)機(jī)來(lái)完成的。狀態(tài)機(jī)使用一個(gè)4MHz信號(hào)作為時(shí)鐘,共設(shè)3種狀態(tài):
①CPU控制狀態(tài)。
②DMA控制狀態(tài)。
③空閑狀態(tài),CPU及DMA均未能得到控制權(quán)。
當(dāng)狀態(tài)機(jī)檢測(cè)到無(wú)CPU及DMA申請(qǐng)總線使用權(quán)時(shí),就進(jìn)入空閑狀態(tài),而一旦CPU或DMA請(qǐng)求總線,狀態(tài)機(jī)立即將總線使用權(quán)交給CPU或DMA。如果正在CPU控制狀態(tài)期間,DMA申請(qǐng)總線使用權(quán),狀態(tài)機(jī)仍然判定CPU控制總線,讓DMA處于等待,直到檢測(cè)CPU指令完成,狀態(tài)機(jī)才轉(zhuǎn)為DMA控制狀態(tài)。如果在DMA控制狀態(tài)時(shí),CPU申請(qǐng)總線使用權(quán),狀態(tài)機(jī)仍然判定DMA控制總線,讓CPU處于等待,直至DMA字節(jié)傳送完,狀態(tài)機(jī)轉(zhuǎn)為CPU控制狀態(tài)。CPU及DMA對(duì)總線的使用權(quán)是單個(gè)指令或字節(jié)傳送,不設(shè)置總線封鎖,因而CPU、DMA可頻繁交換使用權(quán),不會(huì)出現(xiàn)等待時(shí)間過(guò)長(zhǎng)的現(xiàn)象。
通信卡的驅(qū)動(dòng)程序設(shè)計(jì)
1 設(shè)備驅(qū)動(dòng)程序的I/O模型
通信卡的驅(qū)動(dòng)程序是利用Windows XP的DDK軟件開(kāi)發(fā)的標(biāo)準(zhǔn)的核心態(tài)設(shè)備驅(qū)動(dòng)程序。它使用統(tǒng)一的“文件”形式,用戶可以通過(guò)代表通信卡設(shè)備的文件名,在WIN32子系統(tǒng)中用文件操作函數(shù)來(lái)訪問(wèn)。該設(shè)備驅(qū)動(dòng)和程序可以根據(jù)用戶的需要,設(shè)置為自動(dòng)加載,或手動(dòng)加載,也可以動(dòng)態(tài)的加載該驅(qū)動(dòng)程序。通信卡的驅(qū)動(dòng)程序設(shè)計(jì)成為支持同步I/O模型,也可以為異步I/O模型。
2 發(fā)送數(shù)據(jù)I/O例程設(shè)計(jì)
在用戶態(tài)提交發(fā)送任務(wù)到核心態(tài),由I/O管理程序負(fù)責(zé)調(diào)用驅(qū)動(dòng)程序,驅(qū)動(dòng)程序發(fā)送IRP交給硬件,假若當(dāng)前發(fā)送器不忙,則具體的發(fā)送操作全部由發(fā)送器自行完成,而驅(qū)動(dòng)程序則返回已經(jīng)一個(gè)本幀可以發(fā)送的標(biāo)志。具體的發(fā)送完成結(jié)果則可以在發(fā)送任務(wù)完成后查詢得到??墒牵绻绦蛳虍?dāng)前設(shè)備發(fā)送器提交發(fā)送任務(wù)時(shí),該發(fā)送器正在處理上一幀,那么則有兩種方法處理。
①立即方式:立即返回一個(gè)錯(cuò)誤,當(dāng)前設(shè)備忙,不能發(fā)送。
②阻塞方式:I/O管理器程序會(huì)調(diào)度相應(yīng)的異步處理例程,將當(dāng)前的發(fā)送任務(wù)放進(jìn)任務(wù)隊(duì)列中,返回一個(gè)標(biāo)志:I/O掛起,當(dāng)前設(shè)備忙,發(fā)送任務(wù)提交任務(wù)隊(duì)列。驅(qū)動(dòng)程序會(huì)在發(fā)送器空閑時(shí),提交任務(wù)隊(duì)列中的發(fā)送任務(wù)給發(fā)送器。完成發(fā)送任務(wù)后,將相關(guān)文件句柄設(shè)為有信號(hào)狀態(tài),通知本次發(fā)送任務(wù)完成。
3 接收數(shù)據(jù)I/O例程
接收數(shù)據(jù)例程采用客戶/服務(wù)器的模式設(shè)計(jì)。由于用戶的接收請(qǐng)求和硬件的接收并不是同步的,所以在設(shè)計(jì)中,為避免丟失數(shù)據(jù),考慮創(chuàng)造一個(gè)專門的接收線程。這樣,同步用戶請(qǐng)求、接收線程、硬件層之間的通信就必須仔細(xì)地設(shè)計(jì)。圖2示意了用戶態(tài)接收請(qǐng)求、接收線程以及硬件層之間的通信同步。
①硬件層與接收線程之間通信
硬件層與接收線程之間通信的同步是通過(guò)同步事件對(duì)象來(lái)實(shí)現(xiàn)的。同步事件對(duì)象通常處于無(wú)信號(hào)狀態(tài),只有當(dāng)成功地接收到一幀時(shí),才將該事件置為有信號(hào)狀態(tài)。接收線長(zhǎng)久等待同步事件對(duì)象,在沒(méi)有收到數(shù)據(jù)時(shí),因同步事件對(duì)象處于無(wú)信號(hào)狀態(tài)而阻塞。當(dāng)硬件檢測(cè)到數(shù)據(jù)時(shí),實(shí)時(shí)中斷服務(wù)程序負(fù)責(zé)將同步事件對(duì)象置為有信號(hào)狀態(tài),接收線程就會(huì)釋放阻塞。將通信卡SRAM上的接收數(shù)據(jù)讀進(jìn)接收線程緩沖區(qū)隊(duì)列,然后將同步事件對(duì)象置為無(wú)信號(hào)狀態(tài),接收線程再次阻塞,等待接收下一幀數(shù)據(jù)。
②用戶層和接收線程之間的通信
當(dāng)用戶提交接收任務(wù)時(shí),由驅(qū)動(dòng)程序的調(diào)度程序讀取接收線程的緩沖區(qū)隊(duì)列,并將標(biāo)志置為“空”,同時(shí)將緩沖隊(duì)列事件置為無(wú)信號(hào)狀態(tài),并返回,如果緩沖區(qū)是空,則
?立即方式:立即返回?zé)o數(shù)據(jù)。
?阻塞方式:阻塞直到有數(shù)據(jù)隊(duì)列進(jìn)入。接收隊(duì)列的每個(gè)緩沖區(qū)都對(duì)應(yīng)一個(gè)通知事件,如果接收線程將SRAM中的數(shù)據(jù)讀進(jìn)隊(duì)列,就將相應(yīng)的通知事件置為有信號(hào)狀態(tài),用戶請(qǐng)求就等待該通知事件,只要有數(shù)據(jù)在隊(duì)列中,就讀取返回,否則被阻塞。
4 硬中斷服務(wù)程序設(shè)計(jì)
通信卡在全雙工的通信中具有實(shí)時(shí)性,而且在較高的波特率,硬中斷非常多。為了防止高優(yōu)先級(jí)中斷過(guò)多地?fù)屨糃PU時(shí)間,設(shè)計(jì)中采用了實(shí)時(shí)中斷服務(wù)程序和延遲過(guò)程調(diào)用的方法。
在Windows XP系統(tǒng)中,每個(gè)內(nèi)核函數(shù)和過(guò)程都運(yùn)行于特定的優(yōu)先級(jí)。較高優(yōu)先級(jí)的函數(shù)或事件可以搶占較低優(yōu)先級(jí),反之則不然。實(shí)時(shí)中斷服務(wù)程序ISR運(yùn)行于DIRQL級(jí),具有較高的優(yōu)先級(jí),它只能被更高級(jí)的硬中斷所搶占,不會(huì)對(duì)同級(jí)或較低優(yōu)先級(jí)的硬中斷的響應(yīng)。所以,在實(shí)時(shí)中斷服務(wù)程序中,只做盡量少的必須工作,即讀出中斷向量,而將大量的數(shù)據(jù)傳送及處理等工作交給延遲過(guò)程DPC去完成。因?yàn)镈PC過(guò)程運(yùn)行于DISPATCH_LEVEL級(jí),是相對(duì)較低的優(yōu)先級(jí),它可以被任何級(jí)的硬中斷所搶占。這樣,既保證了硬中斷的及時(shí)響應(yīng),又提高了程序的性能。
5 驅(qū)動(dòng)程序人口例程
Windows XP為每個(gè)核心態(tài)的驅(qū)動(dòng)程序提供了一個(gè)默認(rèn)的標(biāo)準(zhǔn)入口點(diǎn)DriverEntry()。設(shè)計(jì)中,考慮到該例程運(yùn)行于PASSIVE_LEVE級(jí),系統(tǒng)只運(yùn)行該例程一次就拋棄了,所以在驅(qū)動(dòng)程序中用到的重要數(shù)據(jù)、對(duì)象等都不能保存在DriverEntry()例程中,必須在初始化過(guò)程中分配一塊NoPaged內(nèi)存來(lái)保存。通信卡的驅(qū)動(dòng)程序的DriverEntry()例程主要完成如下功能。
讀取Registry的硬件配置信息,聲明I/O地址、中斷等資源;創(chuàng)建代表通信卡的設(shè)備名MPSC,該名字對(duì)WIN32子系統(tǒng)是可見(jiàn)的;設(shè)置調(diào)度例程入口點(diǎn);分配一塊NoPaged的內(nèi)存,存儲(chǔ)重要信息;連接硬中斷,設(shè)置中斷服務(wù)程序,初始化線程、事件、信號(hào)燈、DPC等內(nèi)核對(duì)象,返回狀態(tài)STATUS_SUCCESS。
如果在以上過(guò)程中遇到錯(cuò)誤,則需要做以下工作:斷開(kāi)硬中斷;釋放硬件資源;將相關(guān)的錯(cuò)誤信息打包,并記錄;返回錯(cuò)誤信息。
6 設(shè)計(jì)驅(qū)動(dòng)程序中需要注意的問(wèn)題
由于通信卡在應(yīng)用中具有一定的實(shí)時(shí)性,為了克服中斷的不確定性。提高系統(tǒng)的性能,開(kāi)發(fā)了Windows XP系統(tǒng)所有驅(qū)動(dòng)程序中最難的異步驅(qū)動(dòng)程序。在系統(tǒng)的內(nèi)核開(kāi)發(fā),異步模型I/O操作中,內(nèi)核對(duì)象的同步十分復(fù)雜,細(xì)微的差錯(cuò)都會(huì)導(dǎo)致系統(tǒng)徹底崩潰。在設(shè)計(jì)驅(qū)動(dòng)程序中需要注意以下問(wèn)題。
?在程序中用到的內(nèi)核對(duì)象:事件、信號(hào)燈、線程以及連鎖等,都必須將其存儲(chǔ)在Nopaged內(nèi)存中,否則,會(huì)造成系統(tǒng)崩潰。
篇8
關(guān)鍵詞:嵌入式系統(tǒng);linux;驅(qū)動(dòng)程序
引言
Linux是一個(gè)遵循POSIX標(biāo)準(zhǔn)的免費(fèi)操作系統(tǒng)。具有BSD和SYSV的擴(kuò)展特性。與其他操作系統(tǒng)相比,嵌入式Linux系統(tǒng)以其可應(yīng)用于多種硬件平臺(tái)、內(nèi)核高效穩(wěn)定、源碼開(kāi)放、軟件豐富、網(wǎng)絡(luò)通信和文件管理機(jī)制完善等優(yōu)良特性而正被作為研究熱點(diǎn),越來(lái)越多的研究人員采用Linux平臺(tái)來(lái)開(kāi)發(fā)自己的產(chǎn)品。Linux設(shè)備驅(qū)動(dòng)程序在Linux內(nèi)核源代碼中占有很大比例,從2.0、2.2到2.4版本的內(nèi)核,源代碼的長(zhǎng)度日益增加,其實(shí)主要是設(shè)備驅(qū)動(dòng)程序在增加。
設(shè)備驅(qū)動(dòng)程序的編寫
設(shè)備驅(qū)動(dòng)程序是linux內(nèi)核的一部分,是操作系統(tǒng)內(nèi)核和機(jī)器硬件之間的接口,它由一組函數(shù)和一些私有數(shù)據(jù)組成,是連接應(yīng)用程序與具體硬件的橋梁。Linux的一個(gè)基本特點(diǎn)是它對(duì)硬件設(shè)備的管理抽象化,系統(tǒng)中的每一個(gè)設(shè)備都用一個(gè)特殊的文件來(lái)表示。所有的硬件設(shè)備都像普通的文件一樣看待,使用與操作系統(tǒng)相同的標(biāo)準(zhǔn)系統(tǒng)來(lái)進(jìn)行打開(kāi)、讀寫和關(guān)閉。
在Linux操作系統(tǒng)下有3類主要的設(shè)備文件類型:塊設(shè)備、字符設(shè)備、網(wǎng)絡(luò)設(shè)備。字符設(shè)備是指存取時(shí)沒(méi)有緩存的設(shè)備??上裎募粯釉L問(wèn)字符設(shè)備,字符設(shè)備驅(qū)動(dòng)程序負(fù)責(zé)實(shí)現(xiàn)這些行為。系統(tǒng)的控制臺(tái)和并口就是字符設(shè)備的例子,它們可以很好地用“流”來(lái)描述。塊設(shè)備是文件系統(tǒng)的宿主,如磁盤。Linux允許像字符設(shè)備那樣讀取塊設(shè)備――允許一次傳輸任意數(shù)目的字節(jié)。結(jié)果是,字符設(shè)備和塊設(shè)備讀取數(shù)方式一致。而網(wǎng)絡(luò)設(shè)備不同于字符設(shè)備和塊設(shè)備,它面向的上一層不是文件系統(tǒng)而是網(wǎng)絡(luò)協(xié)議層,是通過(guò)BSD套接口訪問(wèn)數(shù)據(jù)。與設(shè)備相對(duì)應(yīng)的是三類設(shè)備驅(qū)動(dòng)程序,字符設(shè)備驅(qū)動(dòng)程序、塊設(shè)備驅(qū)動(dòng)程序、網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序。
字符設(shè)備驅(qū)動(dòng)程序、塊設(shè)備驅(qū)動(dòng)程序與網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序的結(jié)構(gòu)體是不同的。
在linux源代碼linux/include/linux/fs.h中定義了字符設(shè)備和塊設(shè)備驅(qū)動(dòng)程序中必須使用的file_operations結(jié)構(gòu),每個(gè)設(shè)備驅(qū)動(dòng)都實(shí)現(xiàn)這個(gè)接口所定義的部分或全部函數(shù)。隨著內(nèi)核的不斷升級(jí),file_operations結(jié)構(gòu)也越來(lái)越大,不同的版本的內(nèi)核會(huì)稍有不同。
應(yīng)用程序只有通過(guò)對(duì)設(shè)備文件的open、release、read、write、ioctl等才能訪問(wèn)字符設(shè)備和塊設(shè)備。用戶自己定義好file_operations結(jié)構(gòu)后,編寫出設(shè)備實(shí)際所需要的各操作函數(shù),對(duì)于不需要的操作函數(shù)用NULL初始化,這些操作函數(shù)將被注冊(cè)到內(nèi)核,當(dāng)應(yīng)用程序?qū)υO(shè)備相應(yīng)的設(shè)備文件進(jìn)行文件操作時(shí),內(nèi)核會(huì)找到相應(yīng)的操作函數(shù),并進(jìn)行調(diào)用。如果操作函數(shù)使用NULL,操作函數(shù)就進(jìn)行默認(rèn)處理。
對(duì)于字符設(shè)備而言,llseek(),read(),write(),ioctl(),open(),release()這些函數(shù)是不可缺的;對(duì)十塊設(shè)備,open(),release(),ioctl(),check_media_change(),revalidate()是不可缺少的。
網(wǎng)絡(luò)設(shè)備結(jié)構(gòu)體net_device定義在include\linuxhletdevice.h里,如下所示:
定義好net_device結(jié)構(gòu)體后,根據(jù)實(shí)際情況編寫操作函數(shù),其中hard_start_xmit()函數(shù)是用來(lái)發(fā)送數(shù)據(jù)的,set mac address()是進(jìn)行網(wǎng)絡(luò)參數(shù)設(shè)置的。
當(dāng)linux初始化時(shí)將調(diào)用初始化函數(shù)intdevice_init(),該函數(shù)包括以下內(nèi)容:
注冊(cè)所用設(shè)備。linux用設(shè)備號(hào)來(lái)標(biāo)識(shí)字符設(shè)備和塊設(shè)備。設(shè)備號(hào)分為主設(shè)備號(hào)和從設(shè)備號(hào),最終形成設(shè)備接點(diǎn)。設(shè)備節(jié)點(diǎn)在訪問(wèn)字符設(shè)備和塊設(shè)備的設(shè)備驅(qū)動(dòng)程序時(shí)將使用。通常主設(shè)備號(hào)標(biāo)識(shí)設(shè)備對(duì)應(yīng)的驅(qū)動(dòng)程序,大多數(shù)設(shè)備是“一個(gè)主設(shè)備號(hào)對(duì)應(yīng)一個(gè)驅(qū)動(dòng)程序”,如:虛擬控制臺(tái)和串口終端由驅(qū)動(dòng)程序4管理。次設(shè)備號(hào)由內(nèi)核使用,用于確定設(shè)備文件所指的設(shè)備。字符設(shè)備和塊設(shè)備注冊(cè)時(shí)必須先定義好設(shè)備號(hào)。
字符設(shè)備注冊(cè)函數(shù)如下:
int register_chrdev(unsigned int major,constchar*name,struct file_oprations*fops);其中major是主設(shè)備號(hào)。
由于對(duì)網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序的訪問(wèn)不需要設(shè)備節(jié)點(diǎn),它的注冊(cè)函數(shù)如下:
int register_netdev(struct net_device*dev)
注冊(cè)設(shè)備所用的中斷。中斷在現(xiàn)代計(jì)算機(jī)結(jié)構(gòu)中有重要的地位,操作系統(tǒng)必須提供程序響應(yīng)中斷的能力。一般是把一個(gè)中斷處理程序注冊(cè)到系統(tǒng)中去。操作系統(tǒng)在硬件中斷發(fā)生后調(diào)用驅(qū)動(dòng)程序的處理程序。
注冊(cè)中斷所用的函數(shù)如下:其中,irq是中斷向量;handler是中斷處理函數(shù);flags是中斷處理中的掩碼;devices是設(shè)備名;dev_id是在中斷共享使用的id。
當(dāng)linux不使用該設(shè)備時(shí),就要調(diào)用清除函
編寫服務(wù)子程序
服務(wù)于I/O請(qǐng)求的子程序,又稱為驅(qū)動(dòng)程序的上半部分。調(diào)用這部分是由于系統(tǒng)調(diào)用的結(jié)果。這部分程序在執(zhí)行的時(shí)候,系統(tǒng)仍認(rèn)為是和進(jìn)行調(diào)用的進(jìn)程屬于同一個(gè)進(jìn)程,只是用戶態(tài)變成了核心態(tài),具有進(jìn)行此系統(tǒng)調(diào)用的用戶程序的運(yùn)行環(huán)境,因此可以在其中調(diào)用sleep等與進(jìn)程運(yùn)行環(huán)境有關(guān)的函數(shù)。
中斷服務(wù)子程序,又稱為驅(qū)動(dòng)程序的下半部分。在Linux系統(tǒng)中,并不是直接從中斷向量表中調(diào)用設(shè)備驅(qū)動(dòng)程序的中斷服務(wù)子程序,而是由Linux系統(tǒng)來(lái)接收硬件中斷,再由系統(tǒng)調(diào)用中斷服務(wù)子程序。中斷可以產(chǎn)生在任何一個(gè)進(jìn)程運(yùn)行的時(shí)候,因此在中斷服務(wù)程序被調(diào)用的時(shí)候,不能依賴于任何進(jìn)程的狀態(tài),也就不能調(diào)用任何與進(jìn)程運(yùn)行環(huán)境相關(guān)的函數(shù)。因?yàn)樵O(shè)備驅(qū)動(dòng)程序一般支持同一類型的若干設(shè)備,所以一般在系統(tǒng)調(diào)用中斷服務(wù)程序的時(shí)候,都帶有一個(gè)或多個(gè)參數(shù),以唯一標(biāo)識(shí)請(qǐng)求服務(wù)的設(shè)備。
設(shè)備驅(qū)動(dòng)程序的使用
直接將驅(qū)動(dòng)程序編譯進(jìn)linux內(nèi)核
將設(shè)備驅(qū)動(dòng)程序復(fù)制到linux/drivers相關(guān)的子目錄下,比如字符設(shè)備驅(qū)動(dòng)程序就放在linux/drivers/char下。
修改linux/drivers相關(guān)的子目錄的Makefile,
如obj-$(config_dev_driver)+=dev_driver.o,這樣在編譯內(nèi)核時(shí)將會(huì)編譯dev_driver.c,生成dev_driver.o.
對(duì)內(nèi)核進(jìn)行重新編譯時(shí),進(jìn)行相關(guān)的配置,比如要使用AT91RM9200的UART,就要如下配置:
Character devices->Serial drivers.>AT91RM9200 serial port suppot
將驅(qū)動(dòng)程序編譯成驅(qū)動(dòng)模塊
在設(shè)備驅(qū)動(dòng)程序中要有兩個(gè)重要函數(shù):
module_init(dev-init),module_exit(dev_exit)
利用相應(yīng)的交叉編譯器以及編譯命令將驅(qū)動(dòng)程序dev_driver.c編譯成dev_driver.o這樣的動(dòng)態(tài)驅(qū)動(dòng)模塊。利用insmod命令給系統(tǒng)安裝驅(qū)動(dòng)模塊,如果在/dev目錄下沒(méi)有相應(yīng)的設(shè)備文件,就可以使用mknod創(chuàng)建一個(gè)設(shè)備文件。利用rmmod命令卸載驅(qū)動(dòng)模塊,設(shè)備文件的刪除可以用rm命令。
篇9
A/D轉(zhuǎn)換是單片機(jī)數(shù)據(jù)采集系統(tǒng)的重要組成部分,實(shí)時(shí)內(nèi)核下A/D驅(qū)動(dòng)程序的實(shí)現(xiàn)過(guò)程主取決于A/D轉(zhuǎn)換器的轉(zhuǎn)換時(shí)間。本文首先比較和分析μC/OS-II下A/D采樣數(shù)據(jù)的三種方法;其次介紹C8051F015單片機(jī)A/D模數(shù)轉(zhuǎn)換器配置及特點(diǎn);最后,在μC/OS-II內(nèi)核移植到8位單片機(jī)C8051F015的基礎(chǔ)上,介紹編寫A/D驅(qū)動(dòng)程序的一般思想和方法。
1 μC/OS-II實(shí)時(shí)內(nèi)核下的A/D讀方法
實(shí)時(shí)內(nèi)核下,驅(qū)動(dòng)程序采用什么方法讀取A/D采樣數(shù)據(jù)是首先考慮的問(wèn)題。許多因素將影響讀取A/D,如A/D的轉(zhuǎn)換時(shí)間、模擬值的轉(zhuǎn)換頻率、輸入通道數(shù)等,但最主要的取決于A/D的轉(zhuǎn)換時(shí)間。典型的A/D轉(zhuǎn)換典型的A/D轉(zhuǎn)換電路由模擬多路復(fù)用器(MUX)、放大器和模數(shù)轉(zhuǎn)換器(ADC)三部分組成。下面描述讀取A/D的三種方法。
圖1所示的是第1種讀取方法。假設(shè)A/D轉(zhuǎn)換器的轉(zhuǎn)換時(shí)間較慢(5ms以上)。應(yīng)用程序調(diào)用圖1所示的驅(qū)動(dòng)程序,并傳遞要讀取的通道。驅(qū)動(dòng)程序通過(guò)MUX選擇要讀取的模擬通道(①)開(kāi)始讀。有,延時(shí)幾μs以便使信號(hào)通過(guò)MUX傳遞,并之穩(wěn)定下來(lái)。接著,ADC被觸發(fā)開(kāi)始轉(zhuǎn)換(②)。然后驅(qū)動(dòng)程序延時(shí)一段時(shí)間以完成轉(zhuǎn)換(③_。延時(shí)時(shí)間必須比ADC轉(zhuǎn)換時(shí)間長(zhǎng)。最后驅(qū)動(dòng)程序讀取ADC轉(zhuǎn)換結(jié)果(④)。并將轉(zhuǎn)換結(jié)果返回到應(yīng)用程序(⑤)。
圖2所示的是第2種讀取方法。當(dāng)模擬轉(zhuǎn)換完成后,ADC產(chǎn)生的個(gè)中斷信號(hào)。若ADC轉(zhuǎn)換完成,ISR給信號(hào)量發(fā)一個(gè)信號(hào)(⑤),通知驅(qū)動(dòng)程序,ADC已經(jīng)完成轉(zhuǎn)換。如果ADC在規(guī)定的時(shí)限內(nèi)沒(méi)有完成轉(zhuǎn)換。信號(hào)量超過(guò)(③),則驅(qū)動(dòng)程序不再等待下去。驅(qū)動(dòng)程序和中斷服務(wù)子程序(ISR)的偽代碼如下:
ADRd(ChannelNumber)
{
選擇要讀取的模擬輸入通道;
等待AMUX輸出穩(wěn)定;
啟動(dòng)ADC轉(zhuǎn)換;
等待來(lái)自ADC轉(zhuǎn)換結(jié)束中斷產(chǎn)生的信號(hào)量;
if(超時(shí)){
*eer=信號(hào)錯(cuò)誤;
return;
}else{
讀取ADC轉(zhuǎn)換結(jié)果并將其返回到應(yīng)用程序;
}
}
ADCoversion Complete ISR {
保存全部CPU寄存器; /*將CPU的PSW、ACC、B、DPL、DPH及Rn入棧*/
通知內(nèi)核進(jìn)入ISR(調(diào)用OSIntEnter()或OSIntNesting直接加1);
發(fā)送ADC轉(zhuǎn)換完成信號(hào); /*利用μC/OS-II內(nèi)核的OSSemPost()*/
通知內(nèi)核退出ISR(調(diào)用OSIntExit());
恢復(fù)所有CPU寄存器;/*將CPU的PSW、ACC、B、DPL、DPH及Rn出棧*/
執(zhí)行中斷返回指令(即RETI);
}
在這種方法里,要求ISR執(zhí)行時(shí)間與調(diào)用等待信號(hào)的時(shí)間之和為A/D轉(zhuǎn)換時(shí)間。
如果A/D轉(zhuǎn)換時(shí)間小于處理中斷時(shí)間與等待信號(hào)所需的時(shí)間之和,則可以用第三種方法。如圖3所示,前兩步(①②同以上兩種方法)結(jié)束后,驅(qū)動(dòng)程序接著在一個(gè)軟件循環(huán)中等待(③)ADC直到完成轉(zhuǎn)換。在循環(huán)等待時(shí),驅(qū)動(dòng)程序檢測(cè)ADC的狀態(tài)(BUSY)信號(hào)。如果等待時(shí)間超過(guò)設(shè)定的定時(shí)值(軟件定時(shí)),則結(jié)束等待循環(huán)(循環(huán)等超時(shí))。如果在循環(huán)等待中,檢測(cè)到ADC發(fā)出轉(zhuǎn)換結(jié)束的信號(hào)(BUSY)時(shí),驅(qū)動(dòng)程序讀取ADC轉(zhuǎn)換結(jié)果(④)并將結(jié)果返回到應(yīng)用程序(⑤)。驅(qū)動(dòng)程序偽代碼如下:
ADRd(ChannelNumber){
選擇要讀取的模擬輸入通道;
等待AMUX輸出穩(wěn)定;
啟動(dòng)ADC轉(zhuǎn)換;
啟動(dòng)超時(shí)定時(shí)器;
while(ADC Busy & Counter 0);/*循環(huán)檢測(cè)*/
if(Counter==0){
*err=信號(hào)錯(cuò)誤;
return;
}else{
讀取ADC轉(zhuǎn)換結(jié)果并將其返回到應(yīng)用程序;
}
}
A、D轉(zhuǎn)換速度快,這種驅(qū)動(dòng)程序的實(shí)現(xiàn)是最好的。
2 C8051F015單片機(jī)的A/D轉(zhuǎn)換器
2.1 C8051C015單片機(jī)
C8051C015的美國(guó)Cygnal公司新推出的高速SOC型C8051Fxxx系列單片機(jī)。它的內(nèi)核CIP-51與MCS-51的指令集完全兼容,CIP-51的系統(tǒng)時(shí)鐘頻率在0~25MHz。C8051Fxxx系列單片機(jī)采用流水線結(jié)構(gòu),與標(biāo)準(zhǔn)的8051相比,指令執(zhí)行速度有很大的提高。CIP-51內(nèi)核的指令執(zhí)行時(shí)間是以系統(tǒng)時(shí)鐘為單位,70%的指令執(zhí)行時(shí)間為1個(gè)或2個(gè)系統(tǒng)時(shí)鐘周期。C8051F015具有32KB的內(nèi)存、2304B的RAM(片內(nèi)256B、片外2048B)。CIP-51內(nèi)核具有標(biāo)準(zhǔn)8052的所有外設(shè)部件,片上還集成有9通道10位A/D轉(zhuǎn)換接口電路、SMBus/I2C、SPI串行接口。
2.2 C8051F015的A/D轉(zhuǎn)換電路
C8051F015的A/D轉(zhuǎn)換電路包括1個(gè)9通道可配置模擬多路開(kāi)關(guān)AMUX(8路用于外部模擬輸入、1路用于芯片環(huán)境溫度的測(cè)量)、1個(gè)可編程增益放大器PGA和1個(gè)100ksps 10位分辨率的逐次逼近型ADC。A/D中還集成了跟蹤保持電路和可編程窗口檢測(cè)器。
ADC有4種啟動(dòng)方式:軟件命令、定時(shí)器2溢出、定時(shí)器3溢出及外部信號(hào)輸入。寄存器ADC0CN是配置啟動(dòng)和跟蹤方式的控制寄存器。每次轉(zhuǎn)換結(jié)束時(shí),ADC0CH的ADBUSY(忙標(biāo)志)的下降沿觸發(fā)中斷,也可用軟件查詢這個(gè)狀態(tài)位。
2.3 ADC轉(zhuǎn)換速度
C8051Fxxx系列單片機(jī)中ADC的速率都是可編程設(shè)置的。表1給出了所需最小分頻系數(shù)與SYSCLK(系統(tǒng)時(shí)鐘)的關(guān)系(ADC0CF為ADC配置寄存器)。
表1 ADC時(shí)鐘分頻系數(shù)與SYSCLK頻率的關(guān)系
SYSCLK頻率/MHzADC時(shí)鐘分頻系數(shù)ADC0CF的ADCSC2~1時(shí)鐘頻率<2.510002.5~520015~10401010~208(復(fù)位值)011時(shí)鐘頻率>20161xx在C8051F015單片機(jī)中,ADC的轉(zhuǎn)換時(shí)鐘周期至少在400ns,轉(zhuǎn)換時(shí)鐘應(yīng)不大于2MHz。一般在啟動(dòng)ADC之前都要處于跟蹤方式,而ADC一次轉(zhuǎn)換完成要用16個(gè)系統(tǒng)時(shí)鐘。另外,在轉(zhuǎn)換之前還要加上3個(gè)系統(tǒng)時(shí)鐘的跟蹤/保持捕獲時(shí)間,所以完成一次轉(zhuǎn)換需19個(gè)ADC轉(zhuǎn)換時(shí)鐘(9.5μs)。
圖1中的方法簡(jiǎn)單,轉(zhuǎn)換時(shí)間在ms級(jí)以上,一般用于變化慢的模擬輸入信號(hào),不適用于C8051F015。圖2中的方法,為了減少μC/OS-II內(nèi)核調(diào)用ISR所用時(shí)間,ISR一般都用于匯編語(yǔ)言編寫。從程序1中ISR偽代碼可以看出,盡管ISR用匯編語(yǔ)言編寫。代碼效率高,但μC/OS-II調(diào)用ISR的時(shí)間與調(diào)用等待信號(hào)時(shí)間之和大于A/D的轉(zhuǎn)換時(shí)間,所以CPU用于ISR和循環(huán)檢測(cè)的開(kāi)銷大。
圖3所示的方法顯然適合于C8051F015單片機(jī),其優(yōu)點(diǎn)是:可以獲得快速的轉(zhuǎn)換時(shí)間;不需要增加一個(gè)復(fù)雜的ISR;轉(zhuǎn)換時(shí)信號(hào)改變時(shí)間更短;CPU的開(kāi)銷??;循環(huán)檢測(cè)程序可被中斷,為中斷信號(hào)服務(wù)。
圖4 A/D驅(qū)動(dòng)程序模塊流程圖
3 A/D驅(qū)動(dòng)程序的編寫
外設(shè)驅(qū)動(dòng)程序是實(shí)時(shí)內(nèi)核和硬件之間的接口,是連接底層硬件和內(nèi)核的紐帶。編寫驅(qū)動(dòng)程序模塊應(yīng)滿足以下主要功能:①對(duì)設(shè)備初始化;②把數(shù)據(jù)從內(nèi)核傳送到硬件從硬件讀取數(shù)據(jù);③讀取應(yīng)用程序傳送給設(shè)備的數(shù)據(jù)和回送應(yīng)用程序請(qǐng)求的數(shù)據(jù);④監(jiān)測(cè)和處理設(shè)備出現(xiàn)的異常。
A/D轉(zhuǎn)換電路作為一個(gè)模擬輸入模塊,μC/OS-II內(nèi)核應(yīng)把它作為一個(gè)獨(dú)立的任務(wù)(以下稱為ADTask())來(lái)調(diào)用。A/D驅(qū)動(dòng)程序模塊流程如圖4所示。ADInit()初始化所有的模擬輸入通道、硬件ADC以及應(yīng)用程序調(diào)用A/D模塊的參量,并且ADInit()創(chuàng)建任務(wù)ADTask()。ADTb1[]是一個(gè)模擬輸入通道信息、ADC硬件狀態(tài)等參數(shù)配置以及轉(zhuǎn)換結(jié)果存儲(chǔ)表。ADUpdate()負(fù)責(zé)讀取所有模擬輸入通道,訪問(wèn)ADRd()并傳遞給它一個(gè)通道數(shù)。ADRd()負(fù)責(zé)通過(guò)多路復(fù)用器選擇合適的模擬輸入,啟動(dòng)并等待ADC轉(zhuǎn)換,以及返回ADC轉(zhuǎn)換結(jié)果到ADUpdate()。
在μC/OS-II這時(shí)內(nèi)核下各原型函數(shù)、數(shù)據(jù)結(jié)構(gòu)和常量的定義如下:
INT16S ADRd(INT8U ch);
/*定義如何讀取A/D,A/D必須通過(guò)AIRd()來(lái)驅(qū)動(dòng)*/
void ADUpdate(void);
/*一定時(shí)間內(nèi)更新輸入通道*/
void ADInit(void);
/*A/D模塊初始化代碼,包括初始化所有內(nèi)部變量(通過(guò)ADInit()初始化ADTb[]),初始化硬件A/D(通過(guò)ADInitI())及創(chuàng)建任務(wù)ADTask()*/
void ADTask (void data);
/*由ADInit()創(chuàng)建,負(fù)責(zé)更新輸入通道(調(diào)用ADUpdate ())*/
void ADInitI (void);
/*初始化硬件A/D*/
AD_TaskPrio:設(shè)置任務(wù)ADTask()的優(yōu)先級(jí)。
AD_TaskStkSize:設(shè)置分配給任務(wù)ADTask()的堆棧大小。
AD_MaxNummber:AMUX的輸入通道數(shù)。
AD_TaskDly:設(shè)定更新通道的間隔時(shí)間。
AD ADTbl[AD_MaxNummber]:AD類型的數(shù)組(AD是定義的數(shù)據(jù)結(jié)構(gòu))。
4 結(jié)論
對(duì)于A/D轉(zhuǎn)換器接口電路驅(qū)動(dòng)程序的編寫歸納出以下幾點(diǎn):
①在決定采用具體的驅(qū)動(dòng)方案之前,分析接口電路的特點(diǎn),尤其是了解A/D的轉(zhuǎn)換速度;
②對(duì)于轉(zhuǎn)換速度快的A/D轉(zhuǎn)換器,可能出現(xiàn)CPU的處理速度與A/D轉(zhuǎn)換速度不匹配,一般的A/D中不帶有FIFO緩沖區(qū),須有內(nèi)存中開(kāi)辟緩沖區(qū);
篇10
第一節(jié)windows nt網(wǎng)絡(luò)結(jié)構(gòu)
§1.1.1 windows nt網(wǎng)絡(luò)體系結(jié)構(gòu)
windows nt的網(wǎng)絡(luò)體系結(jié)構(gòu)是基于國(guó)際標(biāo)準(zhǔn)化(iso)制定的標(biāo)準(zhǔn)模型──開(kāi)放式系統(tǒng)互連(open system interconnection:osi)參考模型分層建立的,這種方式有利于隨時(shí)擴(kuò)展其它功能和服務(wù)。
windows nt網(wǎng)絡(luò)模型開(kāi)始于mac子層,網(wǎng)卡驅(qū)動(dòng)程序就駐留在其中。它通過(guò)相關(guān)的網(wǎng)卡把windows nt與網(wǎng)絡(luò)連接起來(lái),圖中的多個(gè)網(wǎng)卡表明在一臺(tái)運(yùn)行windows nt的計(jì)算機(jī)上能使用多種網(wǎng)卡。
這一網(wǎng)絡(luò)體系結(jié)構(gòu)包括兩個(gè)重要接口──ndis接口與傳輸驅(qū)動(dòng)
程序接口(tdi)。這兩個(gè)接口把兩個(gè)層隔離開(kāi)來(lái),辦法是相鄰的部件只允許按單一的標(biāo)準(zhǔn)來(lái)寫,不允許多重標(biāo)準(zhǔn)。例如一個(gè)網(wǎng)卡驅(qū)動(dòng)程序(在ndis接口的下面)就不需要特地按每個(gè)傳輸協(xié)議來(lái)寫它的代碼塊,恰恰相反,該驅(qū)動(dòng)程序是寫給ndis接口的,它通過(guò)符合ndis的相應(yīng)傳輸協(xié)議來(lái)請(qǐng)求服務(wù)。這些接口包含在windows nt的網(wǎng)絡(luò)體系結(jié)構(gòu)中,以容納可移植、可互換的模塊。
在兩個(gè)接口之間,是傳輸協(xié)議。它在網(wǎng)絡(luò)中起著組織者的作用。一個(gè)傳輸協(xié)議規(guī)定了數(shù)據(jù)以何種方式呈遞給下一個(gè)接收層,以及如何對(duì)數(shù)據(jù)相應(yīng)地進(jìn)行打包。它通過(guò)ndis把數(shù)據(jù)傳給網(wǎng)卡驅(qū)動(dòng)程序,并通過(guò)tdi把數(shù)據(jù)傳給轉(zhuǎn)發(fā)程序(redirector)
tdi之上是轉(zhuǎn)發(fā)程序,它把本地的網(wǎng)絡(luò)資源申請(qǐng)轉(zhuǎn)送給網(wǎng)絡(luò)。
為了能和其他廠商的網(wǎng)絡(luò)互連,windows nt允許有多個(gè)轉(zhuǎn)發(fā)程序。對(duì)于每一個(gè)轉(zhuǎn)發(fā)程序windows nt計(jì)算機(jī)必須也有一個(gè)相應(yīng)的供應(yīng)者(provider)(由網(wǎng)絡(luò)廠商提供)。多供應(yīng)者路由選擇程序決定適當(dāng)?shù)墓?yīng)者,然后借助于供應(yīng)者,對(duì)應(yīng)用請(qǐng)求到相應(yīng)的轉(zhuǎn)發(fā)程序做出選擇。windows nt支持兩種類型的網(wǎng)絡(luò)驅(qū)動(dòng)程序
傳輸驅(qū)動(dòng)程序
實(shí)現(xiàn)數(shù)據(jù)鏈路層中的邏輯鏈路控制子層協(xié)議和傳輸層協(xié)議。向 下與ndis接口,向上與tdi接口。
網(wǎng)卡驅(qū)動(dòng)程序
實(shí)現(xiàn)對(duì)物理層的管理和數(shù)據(jù)鏈路層中介質(zhì)訪問(wèn)控制子層協(xié)議,通過(guò)ndis向下管理物理網(wǎng)卡,向上與傳輸驅(qū)動(dòng)程序通信。
§1.1.3 windows nt網(wǎng)卡驅(qū)動(dòng)程序
windows nt環(huán)境下的網(wǎng)卡驅(qū)動(dòng)程序也分為兩種:
miniport網(wǎng)卡驅(qū)動(dòng)程序:miniport驅(qū)動(dòng)程序只須實(shí)現(xiàn)與網(wǎng)絡(luò)硬件相關(guān)的操作(包括發(fā)送和接收)。而所有底層網(wǎng)卡驅(qū)動(dòng)程序的通用操作(如同步),一般由ndis接口程序來(lái)實(shí)現(xiàn)。
full網(wǎng)卡驅(qū)動(dòng)程序:full網(wǎng)卡驅(qū)動(dòng)程序必須實(shí)現(xiàn)所有硬件相關(guān)和同步、排隊(duì)等操作。例如full網(wǎng)卡驅(qū)動(dòng)程序?yàn)榱隧憫?yīng)數(shù)據(jù)接收,需要保持本身的捆綁信息,而miniport就可以由ndis接口庫(kù)來(lái)實(shí)現(xiàn)。
在windows nt的早期版本中,full網(wǎng)卡驅(qū)動(dòng)程序要求開(kāi)發(fā)者實(shí)現(xiàn)許多底層操作,來(lái)處理多處理器的核心問(wèn)題以及處理器、線程的同步,這樣不同的開(kāi)發(fā)者在大量重復(fù)著許多相同的工作。
而miniport網(wǎng)卡驅(qū)動(dòng)程序允許開(kāi)發(fā)者僅僅寫一些與網(wǎng)絡(luò)硬件相關(guān)的代碼即可,而那些通用的函數(shù)由ndis接口庫(kù)來(lái)實(shí)現(xiàn),這樣開(kāi)發(fā)出來(lái)的驅(qū)動(dòng)程序減少了不必要的工作。
第二節(jié)miniport驅(qū)動(dòng)程序的結(jié)構(gòu)
ndis接口規(guī)范了網(wǎng)卡驅(qū)動(dòng)程序的實(shí)現(xiàn),同時(shí)也對(duì)tdi驅(qū)動(dòng)程序的實(shí)現(xiàn)提出了一定的要求,在nt中,ndis約束下的網(wǎng)卡驅(qū)動(dòng)程序、tdi驅(qū)動(dòng)程序和系統(tǒng)的關(guān)系如下圖所示:
圖2.0 ndis約束下的網(wǎng)卡驅(qū)動(dòng)程序、tdi驅(qū)動(dòng)程序和系統(tǒng)的關(guān)系
miniport驅(qū)動(dòng)程序包括驅(qū)動(dòng)程序?qū)ο蟆Ⅱ?qū)動(dòng)程序源代碼和ndis接口庫(kù)代碼。windows nt ddk提供ndis.h作為miniport驅(qū)動(dòng)程序的主要頭文件,定義了miniport驅(qū)動(dòng)程序的入口點(diǎn)、ndis接口庫(kù)函數(shù)和通用數(shù)據(jù)結(jié)構(gòu)。
上邊緣函數(shù)的作用是網(wǎng)卡驅(qū)動(dòng)與ndis接口庫(kù)進(jìn)行通信,而下邊緣函數(shù)是tdi協(xié)議驅(qū)動(dòng)程序與ndis通信的手段。ndis用一個(gè)叫做邏輯網(wǎng)卡的軟件對(duì)象來(lái)描述系統(tǒng)中的每塊網(wǎng)卡,而邏輯網(wǎng)卡與windows nt設(shè)備對(duì)象的通信由i/o子系統(tǒng)來(lái)管理,描述網(wǎng)卡的設(shè)備對(duì)象包括相關(guān)的網(wǎng)絡(luò)信息如名字、網(wǎng)絡(luò)地址和網(wǎng)卡內(nèi)存基地址等,它還包含與硬件相關(guān)的驅(qū)動(dòng)程序狀態(tài)數(shù)據(jù)(捆綁數(shù)目,捆綁句柄,包過(guò)濾數(shù)據(jù)庫(kù)等)。ndis分配一個(gè)句柄到miniportinitialize這個(gè)上邊緣函數(shù)的一個(gè)結(jié)構(gòu)中,然后miniport網(wǎng)卡驅(qū)動(dòng)程序?qū)⒃谝院筇峁┻@個(gè)句柄來(lái)給ndis調(diào)用,這個(gè)結(jié)構(gòu)一直被ndis保持,并且對(duì)miniport驅(qū)動(dòng)程序不透明。 當(dāng)miniport網(wǎng)卡驅(qū)動(dòng)程序初始化一塊網(wǎng)卡時(shí),它創(chuàng)立自己的內(nèi)部數(shù)據(jù)結(jié)構(gòu)來(lái)描述網(wǎng)卡,記錄需要它管理的與設(shè)備相關(guān)的狀態(tài)信息。當(dāng)miniport網(wǎng)卡驅(qū)動(dòng)程序調(diào)用ndismsetatttibutes或ndismsetattributesex兩ndis庫(kù)函數(shù)時(shí),它傳遞一個(gè)句柄給這數(shù)據(jù)結(jié)構(gòu)。這樣,當(dāng)調(diào)用miniport驅(qū)動(dòng)程序入口點(diǎn)時(shí),它就傳遞這個(gè)句柄來(lái)驗(yàn)證驅(qū)動(dòng)程序所對(duì)應(yīng)的網(wǎng)卡的正確性。這個(gè)數(shù)據(jù)結(jié)構(gòu)為miniport網(wǎng)卡驅(qū)動(dòng)程序所擁有并維護(hù)。miniport nic驅(qū)動(dòng)程序還需要維護(hù)一組對(duì)象,這些對(duì)象是系統(tǒng)定義的對(duì)象標(biāo)識(shí)符(object idetifier:oid)來(lái)標(biāo)識(shí),以描述驅(qū)動(dòng)程序的性能和當(dāng)前狀態(tài)信息。為查詢這些信息,上層驅(qū)動(dòng)程序調(diào)用ndisrequest向ndis接口庫(kù)指示oid。oid表示了調(diào)用所需的信息類型,如miniport驅(qū)動(dòng)程序所支持的lookahead緩沖區(qū)大小等。ndis接到上層驅(qū)動(dòng)程序的查詢請(qǐng)求,將oid傳遞給上邊緣函數(shù)miniportqueryinformation實(shí)現(xiàn)對(duì)oid的查詢,如果上層驅(qū)動(dòng)程序請(qǐng)求改變狀態(tài)信息則調(diào)用miniportsetinformation實(shí)現(xiàn)對(duì)oid的設(shè)置。典型的miniport nic驅(qū)動(dòng)程序必須有一些函數(shù)來(lái)通過(guò)ndis接口實(shí)現(xiàn)上層驅(qū)動(dòng)程序與硬件的通信。這些函數(shù)稱為上邊緣服務(wù)函數(shù)。
這些上邊緣服務(wù)函數(shù)由驅(qū)動(dòng)程序的開(kāi)發(fā)者根據(jù)驅(qū)動(dòng)程序面向的特定低層網(wǎng)絡(luò)類型和硬件以及相應(yīng)環(huán)境,可以有選擇地實(shí)現(xiàn),但必須保證驅(qū)動(dòng)程序最基本的功能,這些基本功能包括初始化、發(fā)送、中斷處理、重置、參數(shù)查詢與設(shè)置和報(bào)文接收。
miniportinitialize:操作系統(tǒng)根據(jù)系統(tǒng)配置信息,檢測(cè)出網(wǎng)卡已安裝時(shí),由ndis接口在初始化時(shí)調(diào)用,主要完成低層網(wǎng)絡(luò)類型確定,對(duì)應(yīng)于物理網(wǎng)卡的邏輯網(wǎng)卡初始化,中斷信息注冊(cè),網(wǎng)卡與主機(jī)通訊方式的確認(rèn)。i/o端口的申請(qǐng)與注冊(cè),內(nèi)存映像,mib的初始化,物理網(wǎng)卡的驗(yàn)證與初始化等。
miniportreconfigure:支持網(wǎng)卡參數(shù)動(dòng)態(tài)變化,和miniportinitilize一樣由ndis接口以初始化級(jí)別調(diào)度執(zhí)行(不能屏蔽中斷,必須由驅(qū)動(dòng)程序承認(rèn)并清除在此期間產(chǎn)生的中斷),支持即插即用和軟配置的網(wǎng)卡在動(dòng)態(tài)改變參數(shù)時(shí),必須提供此函數(shù)。
miniportqueryinformation:查詢網(wǎng)卡的狀態(tài)以及網(wǎng)卡驅(qū)動(dòng)程序的操作或統(tǒng)計(jì)參數(shù),如是否支持組通訊、網(wǎng)卡的物理速率是否支持回環(huán)、是否支持直接拷貝等,這些參數(shù)以oid方式統(tǒng)一管理。
miniportsetinformation:ndis接口或協(xié)議驅(qū)動(dòng)程序通過(guò)調(diào)用此接口改變驅(qū)動(dòng)程序維護(hù)的oid庫(kù),一些操作參數(shù)的改變也將同時(shí)改變驅(qū)動(dòng)程序狀態(tài),例如組地址的設(shè)置。
miniportreset:包括網(wǎng)卡硬件重置和驅(qū)動(dòng)程序軟件重置,軟件重置包括驅(qū)動(dòng)程序狀態(tài)重置,以及一些相關(guān)的參數(shù)重置,還需考慮有些參數(shù)的恢復(fù),重置時(shí)不必完成所有正在活躍的外部請(qǐng)求,但必須釋放已占用的外部資源。
miniporthalt:掛起網(wǎng)卡并釋放該網(wǎng)卡驅(qū)動(dòng)程序占用的所有資源,在此期間不屏蔽中斷。
miniportisr:高優(yōu)先級(jí)的中斷處理程序,進(jìn)行的工作包括初始中斷處理類型,決定是否進(jìn)行中斷轉(zhuǎn)交,對(duì)卡上中斷進(jìn)行處理 等,該服務(wù)類型只在以下情況被調(diào)用:
ndis接口調(diào)用miniportinitialize和miniporthalt兩函數(shù)時(shí)。
.中斷處理類型設(shè)為每此中斷處理過(guò)程都調(diào)用時(shí)。
為使系統(tǒng)能及時(shí)響應(yīng)所有硬件中斷,高優(yōu)先級(jí)的硬件中斷處理程序應(yīng)盡可能的減少運(yùn)行時(shí)間,防止長(zhǎng)時(shí)間的屏蔽低優(yōu)先級(jí)中斷,避免造程中斷丟失。
miniporthandleinterrupt:由中斷延時(shí)處理程序在中斷延時(shí)處理時(shí)進(jìn)行調(diào)用。ndis排隊(duì)所有的延時(shí)處理,該服務(wù)主要處理發(fā)送完成、報(bào)文接收、描述符用盡、溢出、網(wǎng)卡異常等中斷。
miniportsend:ndis收到上層發(fā)送請(qǐng)求時(shí)經(jīng)過(guò)若干協(xié)議處理再向下調(diào)用此服務(wù)過(guò)程,發(fā)送的packet已含有l(wèi)lc和mac頭,該服務(wù)過(guò)程進(jìn)行邊界對(duì)齊、packet約束重整、描述符映射和報(bào)文發(fā)送、以及發(fā)送資源和packet緩沖隊(duì)列管理。
miniporttransferdata:多個(gè)已和網(wǎng)卡捆綁的協(xié)議驅(qū)動(dòng)程序在接收到報(bào)文到達(dá)指示后,向網(wǎng)卡驅(qū)動(dòng)程序發(fā)出傳送請(qǐng)求以拷貝各自所需的報(bào)文數(shù)據(jù)部分,網(wǎng)卡驅(qū)動(dòng)程序根據(jù)各協(xié)議驅(qū)動(dòng)程序?qū)蝹€(gè)packet是否進(jìn)行多次拷貝,以決定是否暫存只允許單次拷貝的packet等。
miniportcheckhandle:ndis每秒調(diào)用此服務(wù)函數(shù)一次,驅(qū)動(dòng)程序發(fā)現(xiàn)網(wǎng)卡異常時(shí)報(bào)告給ndis由ndis調(diào)用miniportreset進(jìn)行硬件重恢復(fù)。
miniportenableintrrupt:中斷使能。
miniportdisableinterrupt:中斷屏蔽。
另外,每個(gè)網(wǎng)卡驅(qū)動(dòng)程序必須有一個(gè)初始化入口點(diǎn),由driver entry函數(shù)實(shí)現(xiàn),它和系統(tǒng)相關(guān),由操作系統(tǒng)在裝入驅(qū)動(dòng)程序時(shí)調(diào)用,主要完成初始化ndis wrapper,再由wrapper初始生成驅(qū)動(dòng)程序管理塊并完成相應(yīng)各種初始化工作,登錄網(wǎng)卡驅(qū)動(dòng)程序所有上邊緣服務(wù)入口點(diǎn),同時(shí)寫入ndis版本信息。ndis接口庫(kù)包括在ndis.sys中,它是一個(gè)核態(tài)函數(shù)庫(kù),有一套抽象的函數(shù),無(wú)論協(xié)議驅(qū)動(dòng)程序還是nic驅(qū)動(dòng)程序都連接到這個(gè)庫(kù)中,以實(shí)現(xiàn)上下層之間的操作。
第二章fddi網(wǎng)卡驅(qū)動(dòng)程序的加載和運(yùn)行
第一節(jié) 網(wǎng)卡驅(qū)動(dòng)程序的安裝
windows nt網(wǎng)卡驅(qū)動(dòng)程序安裝的目的是實(shí)現(xiàn)網(wǎng)卡相應(yīng)硬件信息和驅(qū)動(dòng)程序在windows nt注冊(cè)庫(kù)中的注冊(cè),使windows nt能夠正確識(shí)別網(wǎng)卡,了解所必需的軟硬件信息并能在windows nt啟動(dòng)時(shí)加載相應(yīng)驅(qū)動(dòng)程序。
網(wǎng)卡驅(qū)動(dòng)程序安裝時(shí),首先在主群組的控制面板中選擇“網(wǎng)絡(luò)”,然后添加網(wǎng)卡,指定相應(yīng)信息文件──oemsetup.inf的路徑,以完成以下兩個(gè)必要的操作:
復(fù)制驅(qū)動(dòng)程序到相應(yīng)的系統(tǒng)目錄(windows nt根目錄system32drivers)中;
在windows nt注冊(cè)庫(kù)中存入相應(yīng)軟硬件信息。
下面主要以fddi網(wǎng)卡為例介紹安裝驅(qū)動(dòng)程序所必需的工作:
§2.1.1網(wǎng)卡一般硬件參數(shù)
對(duì)于fddi網(wǎng)卡,必須在編寫其oemsetup.inf文件時(shí)確定以下硬件參數(shù):
總線類型:pci(5)……括號(hào)中的數(shù)字5表示pci總線在ndis中的總線類型代碼;
廠商代號(hào):0x5588……系統(tǒng)加載時(shí)確定網(wǎng)卡的標(biāo)記,也是編程時(shí)確定pci槽號(hào)的標(biāo)識(shí);
cfid: 0x01;
介質(zhì)類型:光纖(3) ……括號(hào)中的數(shù)字表示光纖在ndis中的介質(zhì)類型代碼;
是否支持全雙工:支持。
對(duì)于其它的硬件信息在此inf配置信息文件中可有可無(wú),如若配置,則可在驅(qū)動(dòng)程序的編寫時(shí)利用這些信息,方便編程,同時(shí)有利于其它應(yīng)用對(duì)其參數(shù)的確定和使用。網(wǎng)卡驅(qū)動(dòng)程序的安裝通常將創(chuàng)建登錄表中的四個(gè)不同子鍵:
software registrion鍵,對(duì)應(yīng)于驅(qū)動(dòng)程序,存在于hkey_local_machinesoftwarecompany productnameversion中。我們的fddi網(wǎng)卡驅(qū)動(dòng)程序所對(duì)應(yīng)的是hkey_local_machinesoftwarenet612yhfddiyhfddi1.0;
網(wǎng)卡的軟件登錄鍵,存在于hkey_local_machinesoftwaremicrosoft windows ntnt3.51networkcardsyhfddi1;
驅(qū)動(dòng)程序的服務(wù)登錄鍵,存在于hkey_local_machinesystemcurrentcontrolsetservices
網(wǎng)卡的服務(wù)登錄鍵,存在于hkey_local_machinesystemcurrentcontrolsetservices
對(duì)于每一個(gè)網(wǎng)絡(luò)部件,一個(gè)名為netrules的特殊子鍵在鄰近的驅(qū)動(dòng)程序或網(wǎng)卡登錄子鍵里創(chuàng)建,netrules標(biāo)識(shí)網(wǎng)絡(luò)部件為網(wǎng)絡(luò)整體的一部分。
fddi網(wǎng)卡驅(qū)動(dòng)程序?qū)?yīng)的標(biāo)準(zhǔn)軟件登錄表項(xiàng)將出現(xiàn)在以下路徑:
hkey_local_machinesoftwarenet612yhfddiyhfddi1.0;
驅(qū)動(dòng)程序?qū)?yīng)的標(biāo)準(zhǔn)項(xiàng)的值為:
description =yhfddi/pci adapter controller
install date =……
……
refcount =0x01
servicename =yhfddi
softwaretype =driver
title =yhfddi/pci adapter controller
而且在yhfddi驅(qū)動(dòng)程序相關(guān)的netrules子鍵下,這些值項(xiàng)為:
bindable =yhfddi driver yhfddi adapter non exclusiver
bindform =“yhfddisys”yes no container
class = reg_multi_sz “yhfddi driver basic”
infname =oemnad1.inf
type =yhfddisys ndisdriver yhfddidriver
use =driver
yhfddi網(wǎng)卡在如下路徑的networkcards子鍵里介紹:
hkey_local_machinesoftwaremicrosoft
windows ntnt3.51networkcardsyhfddi1;
網(wǎng)卡的標(biāo)準(zhǔn)項(xiàng)包括以下這些值:
description =yhfddi/pci adapter controller
install date =……
manufacturer =net612
productname =yhfddi
servicename =yhfddi01
title =[01]yhfddi/pci adapter controller
§2.1.3編寫inf信息配置文件
gui inf描述語(yǔ)言被windows nt用以書寫系統(tǒng)所有部件的配置文件,當(dāng)然也可以用以書寫網(wǎng)絡(luò)系統(tǒng)各部件的配置文件,該配置文件描述了網(wǎng)絡(luò)部件安裝、配置、刪除的執(zhí)行過(guò)程。當(dāng)網(wǎng)絡(luò)部件進(jìn)行初始安裝或二次安裝(通常通過(guò)ncpa進(jìn)行)時(shí),安裝程序讀取部件對(duì)應(yīng)的配置文件,進(jìn)行解釋執(zhí)行。gui inf描述語(yǔ)言由節(jié)、命令、邏輯操作、變量規(guī)范、流程控制以及一套調(diào)用dll或外部程序的機(jī)制組成,其中,節(jié)是配置文件的主體,節(jié)可分為install節(jié)(類似于函數(shù)),shell節(jié)(也類似于函數(shù),但可調(diào)用insall和shell節(jié)),detect節(jié)(不包含命令),一個(gè)配置文件一般由若干不同類型的節(jié)組成。驅(qū)動(dòng)程序的開(kāi)發(fā)者根據(jù)需要可以在配置文件中編寫相應(yīng)代碼,使得用戶和系統(tǒng)之間能進(jìn)行交互,并且由用戶決定一些配置參數(shù)。
nt網(wǎng)卡配置文件有其一套規(guī)范,驅(qū)動(dòng)程序開(kāi)發(fā)者必須按規(guī)范編寫配置文件,一般來(lái)說(shuō),一個(gè)配置文件至少應(yīng)該提供下面三個(gè)節(jié):
安裝入口點(diǎn):[identify]shell節(jié)。該節(jié)主要功能是給出安裝部件的類型名,系統(tǒng)通過(guò)它識(shí)別該部件屬于哪一大類(display,mouse,scsi,network等)中的哪一類(網(wǎng)絡(luò)adapter,driver,transport,service,network和netprovidor),同時(shí),還需要給出映像文件和配置文件所在的源介質(zhì)及標(biāo)識(shí)。
[returnoption]shell節(jié)。系統(tǒng)執(zhí)行安裝identify節(jié)后,執(zhí)行該節(jié)。它主要功能是檢查所需安裝的部件是否支持的硬件平臺(tái)和語(yǔ)言,并給出網(wǎng)卡名(有些配置文件支持多類網(wǎng)卡,此時(shí)必須讓用戶進(jìn)行選擇,并獲得選擇結(jié)果)。
[installoption]shell節(jié)。該節(jié)是配置文件得主體,也是上次安裝完后再次進(jìn)行配置、刪除、更新的入口點(diǎn)。主要功能是拷貝映像文件和配置文件,生成配置的各種選項(xiàng),創(chuàng)建該部件在注冊(cè)庫(kù)中對(duì)應(yīng)的各種登錄子樹(shù)并更新重寫。
第二節(jié) 驅(qū)動(dòng)程序的加載過(guò)程
§2.2.1 windows nt的啟動(dòng)過(guò)程
熱門標(biāo)簽
驅(qū)動(dòng)教學(xué)論文 驅(qū)動(dòng)電源設(shè)計(jì) 驅(qū)動(dòng)器 驅(qū)動(dòng)程序設(shè)計(jì) 驅(qū)動(dòng) 心理培訓(xùn) 人文科學(xué)概論
相關(guān)文章
1數(shù)字經(jīng)濟(jì)驅(qū)動(dòng)制造業(yè)增效研究
2科技創(chuàng)新是驅(qū)動(dòng)效率提升的動(dòng)力
4大數(shù)據(jù)驅(qū)動(dòng)智慧農(nóng)業(yè)發(fā)展對(duì)策