文獻(xiàn)標(biāo)識碼: A
DOI:10.16157/j.issn.0258-7998.2017.07.014
中文引用格式: 胡唯唯,王宜懷,張永. 基于K64的USB驅(qū)動(dòng)構(gòu)件化設(shè)計(jì)[J].電子技術(shù)應(yīng)用,2017,43(7):55-58.
英文引用格式: Hu Weiwei,Wang Yihuai,Zhang Yong. The development of USB driver component based on K64[J].Application of Electronic Technique,2017,43(7):55-58.
0 引言
USB現(xiàn)已成為嵌入式設(shè)備的一種主要通信接口,但是由于USB協(xié)議的復(fù)雜性和硬件平臺的多樣性,使得USB驅(qū)動(dòng)程序開發(fā)存在難度大、成本高、可移植差、難以維護(hù)等缺點(diǎn)。為了解決這些問題,本文在深入分析USB協(xié)議的基礎(chǔ)上,對USB設(shè)備的功能進(jìn)行抽象,并采用驅(qū)動(dòng)構(gòu)件化的設(shè)計(jì)思想開發(fā)USB驅(qū)動(dòng)構(gòu)件[1]。同時(shí),在Kinetis Design Studio 3.0集成開發(fā)環(huán)境下,使用恩智浦半導(dǎo)體公司的K64微控制器對該構(gòu)件進(jìn)行測試,將其作為一個(gè)HID設(shè)備與上位機(jī)程序進(jìn)行通信。上位機(jī)軟件在VS 2010環(huán)境下使用C#語言開發(fā),可以動(dòng)態(tài)找到目標(biāo)設(shè)備,實(shí)現(xiàn)快速連接和通信。另外,將該通信系統(tǒng)用于3D打印中,通過對打印產(chǎn)品的紋理進(jìn)行分析,驗(yàn)證本文所開發(fā)的USB構(gòu)件的穩(wěn)定性。
1 USB協(xié)議分析
USB驅(qū)動(dòng)程序主要完成USB設(shè)備的初始化、枚舉和數(shù)據(jù)傳輸。USB主機(jī)與USB設(shè)備之間有4種數(shù)據(jù)傳輸類型,分別是批量傳輸、中斷傳輸、同步傳輸和控制傳輸,每種傳輸方式執(zhí)行一次需要多個(gè)事務(wù)處理[2]。對于控制傳輸,其只能用于USB設(shè)備枚舉,包括3個(gè)階段,分別為設(shè)置階段、數(shù)據(jù)階段和狀態(tài)階段。設(shè)置階段是一次SETUP事務(wù)處理,數(shù)據(jù)階段是多個(gè)IN或者OUT事務(wù)處理,狀態(tài)階段是一次無數(shù)據(jù)傳輸?shù)腎N或者OUT事務(wù)處理。USB設(shè)備枚舉完成后,將使用其他傳輸方式用于實(shí)際數(shù)據(jù)收發(fā),具體使用哪種方式則取決于設(shè)備的類型。USB設(shè)備連接到PC后,PC便開始對其進(jìn)行枚舉[3]。不同操作系統(tǒng)下枚舉過程可能會有所不同,Windows操作系統(tǒng)下的USB設(shè)備枚舉過程為:
(1)用戶將USB設(shè)備插入到PC(USB主機(jī))的USB端口上,USB主機(jī)給USB設(shè)備上電,USB設(shè)備獲取100 mA的電流,并處于上電狀態(tài);
(2)USB主機(jī)檢測USB設(shè)備是全速還是低速設(shè)備,如果D+數(shù)據(jù)線上有高電平則是全速設(shè)備,如果D-數(shù)據(jù)線上有高電平則是低速設(shè)備;
(3)USB主機(jī)復(fù)位USB設(shè)備,復(fù)位時(shí)間至少10 ms。復(fù)位結(jié)束后USB設(shè)備處于默認(rèn)狀態(tài),并使用默認(rèn)的地址0和端點(diǎn)0與USB主機(jī)進(jìn)行通信;
(4)USB主機(jī)獲取USB設(shè)備的18 B的設(shè)備描述符,完成一次控制傳輸。這是USB主機(jī)第一次得到設(shè)備描述符,主機(jī)并不會分析各個(gè)字段的含義,只會得到設(shè)備描述符中端點(diǎn)0所支持的最大數(shù)據(jù)包長度(設(shè)備描述符的第8字節(jié));
(5)USB主機(jī)再一次復(fù)位USB設(shè)備,這一步在USB 2.0協(xié)議中并不要求。另外,對于高速設(shè)備,Windows 8及以上版本會跳過這一步;
(6)USB主機(jī)給USB設(shè)備分配一個(gè)唯一的地址,USB設(shè)備進(jìn)入地址狀態(tài),之后USB設(shè)備將使用這個(gè)新的地址與USB主機(jī)進(jìn)行通信。只要USB設(shè)備不被移除、復(fù)位或者重新啟動(dòng),那么這個(gè)地址將一直存在;
(7)USB主機(jī)獲取配置描述符,及其從屬的接口和端點(diǎn)描述符。如果還有字符串描述符,則繼續(xù)獲取。對于HID類設(shè)備,USB主機(jī)還會獲取報(bào)告描述符;
(8)獲取以上USB設(shè)備的相關(guān)信息后,USB主機(jī)會為USB設(shè)備分配并加載一個(gè)合適的設(shè)備驅(qū)動(dòng)程序;
(9)USB主機(jī)對USB設(shè)備進(jìn)行配置,USB設(shè)備進(jìn)入配置狀態(tài)。對于HID類設(shè)備,則初始化一個(gè)上行傳輸IN端點(diǎn)和一個(gè)下行傳輸OUT端點(diǎn),上行傳輸是USB設(shè)備向PC上傳數(shù)據(jù),下行傳輸是PC向USB設(shè)備發(fā)送數(shù)據(jù)[4]。
枚舉完成后,USB設(shè)備可以和USB主機(jī)進(jìn)行數(shù)據(jù)傳輸。枚舉和枚舉完成后的數(shù)據(jù)傳輸過程中執(zhí)行一次事務(wù)處理就會產(chǎn)生一次中斷。USB中斷包括復(fù)位中斷、令牌中斷、STALL中斷和SOF中斷等,其中令牌中斷包括SETUP令牌中斷、IN令牌中斷和OUT令牌中斷。一次事務(wù)處理由令牌包、數(shù)據(jù)包和握手包組成,令牌包表明此次事務(wù)處理的目的,數(shù)據(jù)包中包含了要傳輸?shù)臄?shù)據(jù),握手包表明此次事務(wù)處理的完成狀態(tài)[5]。圖1是USB設(shè)備枚舉和數(shù)據(jù)收發(fā)的執(zhí)行流程圖。
2 USB驅(qū)動(dòng)構(gòu)件設(shè)計(jì)
驅(qū)動(dòng)構(gòu)件的設(shè)計(jì)應(yīng)滿足可復(fù)用性、可移植性和可維護(hù)性,其中復(fù)用性是設(shè)計(jì)目標(biāo),是軟件成熟的標(biāo)志。軟件的復(fù)用可以降低軟件開發(fā)的難度、減少重復(fù)勞動(dòng)、降低開發(fā)成本、提高開發(fā)效率和軟件質(zhì)量、縮短軟件開發(fā)周期[6]。驅(qū)動(dòng)構(gòu)件由.c源文件和.h頭文件組成。源文件是構(gòu)件的功能函數(shù)實(shí)現(xiàn),函數(shù)分為對外接口函數(shù)和內(nèi)部函數(shù),對外接口函數(shù)供外部調(diào)用,內(nèi)部函數(shù)僅內(nèi)部調(diào)用;頭文件是構(gòu)件的功能描述,其中包含了對外接口函數(shù)聲明、相關(guān)宏定義和類型定義等。在實(shí)際使用時(shí),構(gòu)件應(yīng)滿足兩點(diǎn):無需打開源文件,只要通過讀頭文件就知道如何使用;在不同芯片上使用時(shí),只需要做少量修改或者無需修改。
K64要作為USB設(shè)備使用,必須初始化USB模塊。上電后需要完成設(shè)備枚舉,設(shè)備枚舉過程需要做很多處理,但是所有USB設(shè)備的枚舉過程基本上是一樣的,因此設(shè)備枚舉可以作為一個(gè)函數(shù)進(jìn)行集中處理。USB總線是輪詢式的,所有的通信都是由USB主機(jī)發(fā)起的,因此設(shè)備不能主動(dòng)向USB主機(jī)發(fā)送數(shù)據(jù),只能將要發(fā)給USB主機(jī)的數(shù)據(jù)準(zhǔn)備好等待USB主機(jī)來取。為了方便向USB主機(jī)發(fā)送數(shù)據(jù),在構(gòu)件中封裝一個(gè)發(fā)送數(shù)據(jù)函數(shù)。如果有數(shù)據(jù)要發(fā)送給USB主機(jī),則調(diào)用該函數(shù)將要發(fā)送的數(shù)據(jù)填入指定的緩沖區(qū)內(nèi)即可,在下一次事務(wù)處理中由USB主機(jī)取出,使得USB設(shè)備可以“主動(dòng)”發(fā)送數(shù)據(jù)?;谝陨戏治觯?USB驅(qū)動(dòng)構(gòu)件將封裝4個(gè)函數(shù),分別是初始化函數(shù)usb_init、枚舉處理函數(shù)usb_enumerate、發(fā)送數(shù)據(jù)函數(shù)usb_send和接收數(shù)據(jù)函數(shù)usb_recv。
2.1 usb_init函數(shù)
初始化函數(shù)usb_init完成對USB模塊的初始化,主要包括內(nèi)存分配、時(shí)鐘源使能和使能USB中斷等。每一個(gè)USB設(shè)備都有一個(gè)序列號,同VID和PID一起作為設(shè)備的標(biāo)識符,當(dāng)兩個(gè)VID和PID都一樣的USB設(shè)備插入到PC時(shí),序列號可以起到進(jìn)一步區(qū)分的作用。序列號實(shí)際上是一個(gè)字符串描述符,考慮其用于唯一性標(biāo)識USB設(shè)備的作用,將其作為USB設(shè)備的名稱,并傳入初始化函數(shù)中,這樣開發(fā)者就能夠方便命名自己的USB設(shè)備。
2.2 usb_enumerate函數(shù)
枚舉處理函數(shù)usb_enumerate完成枚舉過程中的控制傳輸,該函數(shù)一般情況下無需改動(dòng)。本驅(qū)動(dòng)構(gòu)件是針對HID設(shè)備的,如果要開發(fā)為其他類型的設(shè)備,在修改描述符后,只需要對該函數(shù)做少量修改即可。以MSD設(shè)備為例,將HID設(shè)備的描述符文件usb_hid_device_descriptor.c替換為MSD設(shè)備的描述符文件usb_msd_device_descriptor.c。比較這兩個(gè)描述符文件,發(fā)現(xiàn)前者比后者多一個(gè)報(bào)告描述符,同時(shí)字符串描述符也不同。為了盡量減少對usb_enumerate函數(shù)代碼的修改,可以將MSD設(shè)備的設(shè)備描述符、配置描述符及其從屬的接口和端點(diǎn)描述符的名稱根據(jù)HID設(shè)備作對應(yīng)修改。這樣,只需要?jiǎng)h除usb_enumerate函數(shù)中對報(bào)告描述符的處理即可。
2.3 usb_send和usb_recv函數(shù)
發(fā)送數(shù)據(jù)函數(shù)usb_send和接收數(shù)據(jù)函數(shù)usb_recv用于數(shù)據(jù)的收發(fā)。為了方便數(shù)據(jù)的收發(fā),這兩個(gè)函數(shù)都有兩個(gè)參數(shù)。usb_send函數(shù)的兩個(gè)參數(shù)為SendBuff和DataLenght,usb_recv函數(shù)的兩個(gè)參數(shù)為RecvBuff和DataLength。進(jìn)行數(shù)據(jù)發(fā)送時(shí),只要將待發(fā)送數(shù)據(jù)的緩沖區(qū)地址和發(fā)送的數(shù)據(jù)長度傳入發(fā)送數(shù)據(jù)函數(shù)usb_send的SendBuff和DataLength即可,下一次事務(wù)處理結(jié)束后,相應(yīng)緩沖區(qū)中的數(shù)據(jù)就會被發(fā)送出去。接收數(shù)據(jù)和發(fā)送數(shù)據(jù)的執(zhí)行是一樣的,只是接收的數(shù)據(jù)已經(jīng)在本次事務(wù)處理中,只要使用usb_recv函數(shù)從相應(yīng)端點(diǎn)的BD中取出即可,RecvBuff用于存放取出的數(shù)據(jù),DataLength是取出的數(shù)據(jù)長度。
3 上位機(jī)軟件設(shè)計(jì)
上位機(jī)軟件使用Windows提供的API函數(shù)對HID設(shè)備進(jìn)行訪問,這些API函數(shù)包含在hid.dll、setupapi.dll、kernel32.dll文件中,分別起到與HID設(shè)備通信、尋找與識別設(shè)備、交換數(shù)據(jù)的作用[7],關(guān)于相關(guān)API函數(shù)的介紹可以查看MSDN。
PC與USB設(shè)備建立連接的第一步就是找到該設(shè)備,上位機(jī)軟件必須時(shí)刻能夠檢測到USB設(shè)備的插入和移除事件,因此必須在相關(guān)窗體句柄創(chuàng)建時(shí)將這些事件通過RegisterDeviceNotification函數(shù)進(jìn)行注冊,該函數(shù)位于user32.dll文件中。
當(dāng)檢測到一個(gè)新的USB設(shè)備插入或者被移除時(shí),Windows將向應(yīng)用程序發(fā)送一個(gè)WM_DEVICECHANGE消息,該消息宏定義為0x0219。然后由默認(rèn)的消息處理函數(shù)WndProc進(jìn)行處理,程序中對WndProc進(jìn)行了重寫,以滿足尋找目標(biāo)設(shè)備的要求[8]。WndProc函數(shù)首先獲取USB總線上指定類型的設(shè)備列表,通過調(diào)用setupapi.dll文件中函數(shù)SetupDiGetClassDevs實(shí)現(xiàn)。SetupDiGetClassDevs函數(shù)的第一個(gè)參數(shù)是HID GUID,GUID是設(shè)備類型的唯一標(biāo)識符,HID類設(shè)備的GUID可以通過hid.dll文件中的HidD_GetHidGuid函數(shù)獲取,為固定值4d1e55b2-f16f-11cf-88cb-001111000030。該函數(shù)的返回值是當(dāng)前USB總線上所有HID類設(shè)備的信息,并保存于InfoSet隊(duì)列中。為了從InfoSet隊(duì)列中找到目標(biāo)設(shè)備,需要調(diào)用setupapi.dll 文件中的SetupDiEnumDeviceInterfaces函數(shù)遍歷InfoSet隊(duì)列,并將每個(gè)設(shè)備的信息保存于DeviceInterfaceData 結(jié)構(gòu)體變量oInterface中。接著,從oInterface中獲取該設(shè)備的VID和PID,然后和目標(biāo)設(shè)備的VID和PID進(jìn)行匹配檢查,以確定該設(shè)備是否為目標(biāo)設(shè)備[9]。如果匹配則找到了目標(biāo)設(shè)備,否則繼續(xù)調(diào)用SetupDiEnumDeviceInterfaces函數(shù),獲取下一個(gè)設(shè)備的信息并繼續(xù)進(jìn)行匹配檢查,直到找到目標(biāo)設(shè)備或者InfoSet隊(duì)列中的設(shè)備都查找完畢為止。
如果沒有找到目標(biāo)設(shè)備,則回收InfoSet占用的內(nèi)存。如果目標(biāo)設(shè)備找到,則使用該設(shè)備路徑作為參數(shù)并調(diào)用CreateFile函數(shù)打開該設(shè)備,之后就可以像讀寫文件一樣操作該設(shè)備。圖2是上位機(jī)軟件尋找USB設(shè)備和執(zhí)行數(shù)據(jù)傳輸?shù)牧鞒虉D。
4 構(gòu)件測試與分析
圖3為數(shù)據(jù)和局部波形圖,圖3(a)是獲取設(shè)備描述符時(shí)使用TravelBus協(xié)議分析儀采集的數(shù)據(jù),圖3(b)是對應(yīng)的局部波形。枚舉過程中USB主機(jī)首先獲取USB設(shè)備的設(shè)備描述符。USB主機(jī)向USB設(shè)備發(fā)送一個(gè)SETUP令牌包,然后是DATA0數(shù)據(jù)包,該數(shù)據(jù)包中包含了8個(gè)字節(jié)的十六進(jìn)制數(shù)據(jù)80 06 00 01 00 00 40 00,該8個(gè)字節(jié)的數(shù)據(jù)是獲取設(shè)備描述符的標(biāo)準(zhǔn)設(shè)備請求。USB設(shè)備接收到該請求后開始處理并向USB主機(jī)返回一個(gè)ACK握手包,表明此次SETUP事務(wù)處理是成功的,從而完成控制傳輸?shù)脑O(shè)置階段。隨后USB主機(jī)發(fā)送IN令牌包開始取設(shè)備描述符,但是USB設(shè)備此時(shí)并沒有將設(shè)備描述符準(zhǔn)備好,因此USB設(shè)備直接向USB主機(jī)發(fā)送一個(gè)NACK不確認(rèn)包。
USB主機(jī)繼續(xù)發(fā)送獲取設(shè)備描述符的IN令牌包,USB設(shè)備返回準(zhǔn)備好的18個(gè)字節(jié)的設(shè)備描述符12 01 00 02 00 00 00 40 A2 15 7F 00 01 01 01 02 00 01,其中PID是0x15A2(第9和第10字節(jié)),VID是0x007F(第11和第12字節(jié))。之后,USB主機(jī)向USB設(shè)備發(fā)送一個(gè)ACK確認(rèn)包,完成控制傳輸?shù)臄?shù)據(jù)階段。最后,USB主機(jī)發(fā)送一個(gè)OUT令牌包,再發(fā)送一個(gè)無數(shù)據(jù)的DATA1數(shù)據(jù)包,USB設(shè)備接收到之后返回一個(gè)ACK握手包,從而完成此次控制傳輸?shù)臓顟B(tài)階段[10]。設(shè)備枚舉成功后,PC將USB設(shè)備掛載到設(shè)備列表中。
5 應(yīng)用
目前使用DLP技術(shù)的3D打印機(jī)需要與PC進(jìn)行通信,通信的實(shí)時(shí)性和穩(wěn)定性至關(guān)重要。將該USB通信系統(tǒng)應(yīng)用于3D打印中,可以提高打印的穩(wěn)定性和實(shí)時(shí)性。打印過程中數(shù)據(jù)丟包率低、實(shí)時(shí)性高,使得所打印的產(chǎn)品紋理的連續(xù)性高(無斷層)、效果逼真。打印成品與局部紋理放大20 000倍效果如圖4所示。
6 結(jié)論
本文在深入分析USB協(xié)議的基礎(chǔ)上,按照構(gòu)件化設(shè)計(jì)思想編寫USB驅(qū)動(dòng)構(gòu)件,同時(shí)以恩智浦半導(dǎo)體公司的K64作為測試對象,并編寫上位機(jī)軟件,實(shí)現(xiàn)與PC之間的USB通信。另外,將該USB通信系統(tǒng)用于實(shí)際項(xiàng)目3D打印中,打印的產(chǎn)品滿足要求。本文所設(shè)計(jì)的USB驅(qū)動(dòng)構(gòu)件封裝簡單合理、設(shè)備枚舉清晰、代碼移植性高、通信穩(wěn)定高效,可以作為驅(qū)動(dòng)程序的開發(fā)模板,同時(shí)對USB驅(qū)動(dòng)程序開發(fā)的規(guī)范性和可移植性具有很高的參考意義。
參考文獻(xiàn)
[1] 龍飛,何欽銘.構(gòu)件化開發(fā)方法在J2EE 項(xiàng)目中的應(yīng)用[J].計(jì)算機(jī)工程與設(shè)計(jì),2007,28(3):591-594.
[2] 黃櫻,劉君,劉卉,等.基于ARM的嵌入式USB主機(jī)系統(tǒng)設(shè)計(jì)[J].微計(jì)算機(jī)信息(嵌入式與SOC),2007,22(2):156-157.
[3] ZHU J,WANG S,ZHANG S Y,et al.Embedded diver system for USB mouse[C].International Conference on Electrical & Control Engineering,2011:180-183.
[4] 侯代文,孫濤,鄧?yán)诿?TMS320VC33與主機(jī)通信的USB接口設(shè)計(jì)[J].電子設(shè)計(jì)工程,2015,23(7):166-170.
[5] 王宜懷,吳璟,蔣銀珍.嵌入式系統(tǒng)原理與實(shí)踐—ARM Cortex-M4 Kinetis微控制器[M].北京:電子工業(yè)出版社,2012.
[6] 呂明琪,薛錦云,胡啟敏.基于軟件體系結(jié)構(gòu)的可復(fù)用構(gòu)件模型[J].計(jì)算機(jī)應(yīng)用研究,2008,25(1):120-122.
[7] 楊晶晶,江春華.USB HID設(shè)備驅(qū)動(dòng)程序設(shè)計(jì)[J].微計(jì)算機(jī)信息(嵌入式與SOC),2006,22(6):140-142.
[8] 郭夏夏.動(dòng)平衡測試系統(tǒng)的關(guān)鍵技術(shù)研究[D].上海:上海交通大學(xué),2014.
[9] WATANABE H,MASAOKA H,OHIGASHI T,et al.Supporting USB devices for the global migration[J].IPSJ International Symposium on Applications & the Internet,2010:153-156.
[10] DONG Z Y,ZHAO H.Data transfer principles and implementation in USB microwave power sensor[C].Seventh International Symposium on Computational Intelligence & Design,2014:76-79.
作者信息:
胡唯唯1,王宜懷1,張 永2
(1.蘇州大學(xué) 計(jì)算機(jī)科學(xué)與技術(shù)學(xué)院,江蘇 蘇州215006;2.蘇州華祥信息科技有限公司,江蘇 蘇州215006)