原始資料來自網絡 整理日: 2015年2月12日
1. Welcome
算是給所有正在學習USB,還徘徊着不得其門而入的朋友一個入門的契機吧,我也深知入門的痛苦,有些人入門就是抱着那什么USB協議,包定義,幀格式......啃來啃去的,結果啃不出個所以然來。
依我的經驗來看,協議方面的東東,隨便找本書,過一遍就行了;然后,你的終點應該放在你如何來寫第一個成功的USB固件;而要寫USB固件,那么了解Cypress固件架構是必要的,也是重中之重;再然后,等你積累了一些端點,控制,bulk,中斷傳輸,SlaveFIFO,GPIF等等的經驗后,再回過頭去看協議方面的內容,就會有更加深刻的體會了;然后,你就可以試着更改FW。c文件了——這個時候你就是高手了。
2. 工具
-  
68013的USB開發板
淘寶隨便買塊好了,還送不少資料。 -  
要准備開發工具
去Cypress官網下一個Cy3684的開發包;
全稱: cy3684_ez_usb_fx2lp_development_kit_15.exe
網址: www.cypress.com/?rID=14321 -  
安裝開發包
工具就是Cypress USB Console了。怎么用不用我說了吧. 
3. 固件架構
以一個3684開發包自帶的例子講解。
-  
進入目錄(個人找自己的)
D:\Program Files\Cypress\USB\Examples\FX2LP\Bulkloop, -  
3個頭文件拷到bulkloop文件夾
文件夾:D:\Program Files\Cypress\USB\Target
Cypress頭文件: Fx2.h, fx2regs.h, syncdly.h -  
keil設置
output里關掉Run User Program #1(前面的勾去掉) -  
檢查keil C51文件路徑是否正確
如果你的keil是直接裝載C:\Keil....下,那不會有錯誤,否則,自行設置正確的路徑。 
4. 編譯bulkloop工程
在工程下,有以下幾個文件:其中,USBJmp.OBJ, EZUSB.LIB基本上是每個工程都要添加的,是一些中斷向量表,EZUSB的函數庫等等,不用管它們。 現在重點看前面三個文件:
-  
fw.c
這個文件是整個USB的固件根本(FirmWare的縮寫),USB協議方面的通信都是在這里完成的,包括上電枚舉,重枚舉,喚醒以及調用用戶自己的程序和控制命令等等。基本上,如非必要,盡量不要動這個文件的內容,也不要在里面書寫你自己的任何代碼。 -  
bulkllop.c
這個就是用戶自己的代碼書寫文件(原始名稱:periph.c)。
我們所有的代碼都在這個文件里書寫。Cypress已經給我們搭好了架構。
void TD_Init(void)
這個函數只會在USB啟動后調用一次。在這個函數里添加你自己的初始化代碼,也就是傳輸數據前要處理的,例如IO口配置,時鍾,端點,FIFO的選擇等等。 我們看bulkloop的初始化,它在USB的in,out傳輸啟動前進行了哪些初始化: CPU時鍾頻率,USB工作模式選擇,端點選擇,端點傳輸方向,FIFO大小的配置等等。
void TD_Poll(void)
Poll中文意思調度,這個函數就是用戶調度程序,USB會在空閑的時候反復調用該函數,所以我們把自己需要反復執行的代碼放在這里。例如在bulkloop里,它就實現了反復從端點2接收上位機數據然后傳給端點6,再從端點6傳給上位機(4,8端點一樣)。 BOOL DR_VendorCmnd(void):這個函數就是自定義命令代碼的書寫處。我們的Vendor命令都會寫在這里,fw.c固件會自動調用我們的代碼。
void ISR_Ep0in(void)
interrupt 0~void ISR_Ep8inout(void) interrupt 0:這幾個函數是當使用端點中斷傳輸時,中斷代碼的書寫處,很少用。 以上,是經常會用到的幾個函數;其他,基本不常用。 -  
dscr.51
這個文件是USB描述符文件,包括了設備描述符,接口描述符,端點描述符,字符串等等。里面的英文都注釋得很詳細了,我就不多做介紹了,剛開始入門的時候,這個文件也不必改動。 
5. 幾個包含文件
-  
fx2.h
預定義,宏及函數聲明 -  
fx2regs.h
68013的寄存器地址定義。 -  
syncdly.h
同步延時。在其他文件里經常調用的一個函數SYNCDELAY就是這里定義的。 -  
intrins.h
C51一些數據類型及函數定義。 
好了,就寫到這里,搞懂每個文件的作用非常非常重要,這樣,你就可以知道自己的代碼書寫在什么地方,遇到不明的函數,定義可以到指定的位置查詢,或者想修改某個設置(例如想把端點2設成IN,端點6設成out),知道到哪個文件里去修改。
6. 入門第一個例子例子(bulkloop)
USB入門的第一個例子,肯定是bulkloop了,裝好驅動,開發包后,在開發包下....
 Cypress\USB\Examples\FX2LP\Bulkloop就是bulkloop例子。
我之所以從bulkllop開始說,也是深有體會的。想當初剛開始學USB的時候,抱着協議悶頭看楞是看不出個所以然,后來從例子開始學,才慢慢搞懂了USB,說實話,我到現在對USB協議類的DD還是一知半解,不過並不妨礙我進行USB的開發不是,所以說,Cypress的固件架構是個好東東,我們可以偷懶了。
開發包里例子是基於Cypress的一塊開發板的,我想很少有人能弄到吧,而bulkloop就簡單了,什么外圍都不需要,一塊68013,加個cyconsole interface就可以看到效果了.可以說,看懂了bulkloop,USB也就算正式入門了,剩下的要做的就是觸類旁通,然后再不停的回過頭去看USB協議,就會恍然大悟“然來是這樣”。
6.1 readme.txt
首先,看bulkloop文件夾下的readme.txt文件,告訴我們這個固件主要實現的功能。
 數據從EP2OUT->EP6IN ,從端點2的out緩沖區到端點6的in緩沖區。數據流向為:
- PC端(console軟件)設定要傳輸的數據
 - PC端發起端點2out傳輸,數據到達68013端點2的out緩沖區
 - bulkloop固件查詢到端點2的out緩沖區有數據,於是將數據發往端點6的IN緩沖區
 - PC端發起端點6 IN傳輸,於是68013的端點6 IN緩沖區中的數據被讀到PC機顯示
這就是整個bulkloop過程,EP4->EP8同理。 
6.2 TD_Init()
然后看固件,看一個固件總是從TD_Init()開始的:
CPUCS = ((CPUCS & ~bmCLKSPD) | bmCLKSPD1) ;
IFCONFIG |= 0x40;
 
        這兩句設定CPU工作狀態以及端口的工作模式。
 bmCLKSPD,bmCLKSPD1是一個預定義,在FX2.H文件中有定義,keil應該都會用的,直接go to definition...跳到定義處查看;至於語法,使用了與或操作,用來進行位操作,置1或清0,編程語言中常用的技巧,C語言不要太差哦
 然后寄存器每位的意義,我們在TRM中可以查到,這兩句告訴我們:
 設定68013 CPU時鍾為48M,端口工作模式為普通的IO口。
EP1OUTCFG = 0xA0;
EP1INCFG  = 0xA0;
SYNCDELAY; // see TRM section 15.14
EP2CFG    = 0xA2;
SYNCDELAY;
EP4CFG    = 0xA0;
SYNCDELAY;
EP6CFG    = 0xE2;
SYNCDELAY;
EP8CFG    = 0xE0;
 
        這幾行代碼進行端點的配置EP2,EP4為out端點,512×2緩沖;EP6,EP8為in端點,512×2緩沖。
 例如
 EP2CFG=0XA2=1010 0010,看TRM對該寄存器的解釋,
| key | comment | 
|---|---|
| b7 = 1 | 該端點有效; | 
| b6 = 0 | 該端點為out傳輸; | 
| b5b4 = 10 | 該端點進行bulk傳輸; | 
| b3 = 0 | 端點為512緩沖; | 
| b2 = 0 | 只讀; | 
| b1b0 = 10 | 該端點緩沖區倍率為雙重,即512×2.其他同理。 | 
SYNCDELAY;
EP2BCL = 0x80; // arm EP2OUT by writing byte count w/skip.
SYNCDELAY;
EP2BCL = 0x80;
SYNCDELAY;
EP4BCL = 0x80; // arm EP4OUT by writing byte count w/skip.
SYNCDELAY;
EP4BCL = 0x80;
 
        這段代碼對端點計數器進行初始化
 注意這里兩個端點都寫了兩次,那是因為我們設置的端點緩沖為512×2,假如緩沖倍率為4,即512×4的話,那么這里初始化要寫4次。
// enable dual autopointer feature
AUTOPTRSETUP |= 0x01;
 
        這端代碼告訴我們可以使用自動指針,也就是AUTOPTRHx兩個自動指針,這兩個自動指針使用方便,可以自動指向端點緩沖區。
6.3 TD_POLL()
然后是TD_POLL();在這里處理相關數據傳送,USB在空閑的時候會自動調用這里面的代碼。
WORD i;
WORD count;
if(!(EP2468STAT & bmEP2EMPTY)) {
    // check EP2 EMPTY(busy) bit in EP2468STAT (SFR), 
    // core set's this bit when FIFO is empty
    if(!(EP2468STAT & bmEP6FULL)) {
        // check EP6 FULL(busy) bit in EP2468STAT (SFR), 
        // core set's this bit when FIFO is full
        // 首先,查詢端點2的EMPTY標志,如果不為1,說明有數據,
        // 然后查詢端點6的FULL標志,如果不為1,說明端點6 FIFO為空,
        // 可以接收數據。至於bmEP2EMPTY,bmEP6FULL,自行go to definition... */
        // 當檢查到端點2 out fifo有數據且端點6 in fifo為空時,就可以將ep2的數據"copy"到ep6.
        // 使用自動指針直接更換2者的指針,實現數據傳送: 
        APTR1H    = MSB( &EP2FIFOBUF ); // 取端點2 FIFO指針
        APTR1L    = LSB( &EP2FIFOBUF );
        
        AUTOPTRH2 = MSB( &EP6FIFOBUF ); // 取端點6 fifo指針
        AUTOPTRL2 = LSB( &EP6FIFOBUF );
        
        count     = (EP2BCH << 8) + EP2BCL; // 計數器
        
        // loop EP2OUT buffer data to EP6IN
        // 傳送count字節
        for( i = 0x0000; i < count; i++ ) {
            // setup to transfer EP2OUT buffer to EP6IN buffer using AUTOPOINTER(s)
            EXTAUTODAT2 = EXTAUTODAT1; // APTR1指針賦給APTR2,實現數據傳送
        }
    
        // 完畢后,重置計數器,以進行下次傳輸:
        EP6BCH = EP2BCH;
        SYNCDELAY;
        
        EP6BCL = EP2BCL;    // arm EP6IN SYNCDELAY;
        EP2BCL = 0x80;      // re(arm) EP2OUT
    }
}
 
        以上即時bulkloop的整個工作過程,可以在interface中方便的看到結果,板子不在身邊,就懶的貼圖了。
- FW.C文件
 
FW.C文件,是比較難看懂的了,這個要逐字逐句研讀,
 我當初整整看了一個星期,邊理解,邊一行一行的注釋,可以說,看懂了,USB協議部分也就差不多了。
7.1 main()函數
從main()函數開始看:
DWORD i;
WORD offset;
DWORD DevDescrLen;
WORD IntDescrAddr;
WORD ExtDescrAddr;
Sleep   = FALSE;        // 初始化用戶變量 休眠使能--禁止
Rwuen   = FALSE;        // 遠程喚醒--禁止
Selfpwr = FALSE;        // 
GotSUD  = FALSE;        // SetUp令牌包到來標志
 
        定義了一些變量,具體用途在后面;第二段同時對變量進行初始化,從名字可以看出其用途。
7.2 TD_Init();
緊接着調用TD_Init()函數,是一些我們自己的初始化配置。
// 定向USB描述符
pDeviceDscr          = (WORD)&DeviceDscr;
pDeviceQualDscr      = (WORD)&DeviceQualDscr;
pHighSpeedConfigDscr = (WORD)&HighSpeedConfigDscr;
pFullSpeedConfigDscr = (WORD)&FullSpeedConfigDscr;
pStringDscr          = (WORD)&StringDscr;
 
        這段代碼用來獲取USB的各個描述符在68013內存中的地址,准確說是在RAM中的地址,在dscrpt.a51文件中有定義,所有的描述符組成了整個的描述符表,后面會用到。
if ((WORD)&DeviceDscr & 0xC000)
 
        這段代碼及以后的,在固件中解釋是:
Is the descriptor table in external RAM (> 16Kbytes)? If yes, then relocate.
Note that this code only checks if the descriptors START in external RAM. It will not work if the descriptor table spans internal and external RAM.
意思是說,這段代碼用來判斷描述符表首址,也就是前面的DeviceDscr、DeviceQualDscr等是否位於68013的外部RAM區,如果是,則移除,然后將描述符表移到內部RAM區,為什么要移到內部RAM區,因為當描述符表位於外部RAM時,USB是不工作的。那么如何判斷描述符地址是否超出內部RAM的地址呢?首先,&DeviceDscr取得整個描述符表的首地址(它也是DeviceDscr設備描述的首址),然后和0XC000相與,為什么要和0XC000相與?這就牽涉到68013 FX2LP(注意是LP)的內部結構圖:
圖片暫咯
上圖針對的是128pin的FX2LP,如果是56或100pin的,那么沒有外部RAM,只有內部RAM。
 可以看到,FX2LP內部RAM從0000-FFFF,其他為外部RAM。而內部RAM中,只有從0000-3FFF和從E000-FFFF的區域可用,其他為系統保留。從0000-3FFF這16K bytes的內部RAM空間,叫做主RAM,對56,100,128pin來說,都可以同時作為程序或數據存儲器(對128pin來說,EA=0)。
 再看&DeviceDscr & 0xC000的結果,要為“真”的話,顯然,必須&DeviceDscr>=0X4000,也就是說判斷的是描述表首址&DeviceDsc是否大於3FFF,剛好是主RAM區的大小,這就是為什么要用&DeviceDscr 和 0xC000相與就來判斷實現了描述符表首地址的原因了(外部 or 內部 ram?)。
// 重定向描述符S
IntDescrAddr = INTERNAL_DSCR_ADDR;
ExtDescrAddr = (WORD)&DeviceDscr;
DevDescrLen  = (WORD)&UserDscr - (WORD)&DeviceDscr + 2;
for (i = 0; i < DevDescrLen; i++)
     *((BYTE xdata *)IntDescrAddr+i) = *((BYTE xdata *)ExtDescrAddr+i);
 
        判斷發現描述符表首址位於外部RAM的后,緊接着就將外部RAM的描述符移到內部RAM。這里就用到了前面定義的變量,
-  
IntDescrAddr
保存內部RAM首址0X80, -  
ExtDescrAddr
保存我們獲得的當前描述符表外部RAM的首址 , -  
DevDescrLen
是整個描述表的長度,從DeviceDscr段到UserDscr段,在dscrptr中有定義。然后for循環,將從ExtDescrAddr地址開始的外部RAM中的數據逐個copy到從IntDescrAddr地址開始的內部RAM區。 
// 更新描述符指針
pDeviceDscr = IntDescrAddr;
offset      = (WORD)&DeviceDscr - INTERNAL_DSCR_ADDR;
pDeviceQualDscr         -= offset;
pConfigDscr             -= offset;
pOtherConfigDscr        -= offset;
pHighSpeedConfigDscr    -= offset;
pFullSpeedConfigDscr    -= offset;
pStringDscr             -= offset;
 
        完畢后更新描述符指針,指向內部RAM區,通過原指針減去一個偏移量得到。
然后是USB的一些初始狀態設置:
EZUSB_IRQ_ENABLE();                 // EZUSB中斷使能
EZUSB_ENABLE_RSMIRQ();              // 使能遠程喚醒中斷
INTSETUP |= (bmAV2EN | bmAV4EN);    // 使能INT2,4自動向量跳轉
USBIE |= bmSUDAV | bmSUTOK | bmSUSP | bmURES | bmHSGRANT; // 使能所選擇中斷
EA = 1;                             // 開8051中斷
 
        -  
EZUSB_IRQ_ENABLE();
預定義是EZUSB=1,查TRM得知,ezusb是EIE寄存器的第0位,EIE.0=1,使能USB中斷; -  
EZUSB_ENABLE_RSMIRQ();
EICON |= 0x20,EICON.5=1,使能遠程喚醒中斷; -  
INTSETUP |= (bmAV2EN | bmAV4EN);
使能INT2,4自動向量跳轉; USBIE |= bmSUDAV | bmSUTOK | bmSUSP | bmURES | bmHSGRANT;使能所選擇中斷,相關的中斷意義,我也一知半解,后面慢慢學習補充; -  
EA = 1;
開8051中斷。從下面的代碼開始,才真正開始我們自己的USB事務處理: 
7.3 Main Loop主循環
// Main Loop主循環
while(TRUE) {
    // Poll User Device 用戶調度程序
    TD_Poll(); // Check for pending SETUP
    // 等待SETUP令牌數據的到來
    if(GotSUD) {
        SetupCommand(); // Implement setup command // 處理SETUP事務
        GotSUD = FALSE; // Clear SETUP flag 清Setup標志
    }
}
 
        TD_Poll,也就是用戶調度程序,USB空閑時調用,不過為什么要放在開始呢?照理說應該放在令牌包后面的,這里不是很明白。。。。。。
if(GotSUD),GotSUD是令牌包標志,准確的說是“令牌階段數據到來”
7.4 什么是令牌包?
首先,USB一連串的數據傳輸、處理、響應等就叫做USB事務。
 例如,上位機要讀取一個描述符,那么就會觸發一次USB事務。
 一個完整的USB事務處理有三個階段:
- 令牌階段
 - 數據階段
 - 握手階段。
每個階段數據傳輸是有各種包組成的,例如令牌階段:同步字段+令牌包+EOP構成。 
USB主機啟動事務處理,開始發送令牌包,這個時候假如說我們當前的USB設備地址號為2(重枚舉時分配的),而主機發送的地址號也為2,那么這個USB設備硬件會產生中斷,進入中斷處理。也就是periph.c文件中的void ISR_Sudav(void)函數,在這個中斷處理中,設置 GotSUD標志為TRUE,表示收到令牌數據,要啟動USB傳輸了。然后我們固件中判斷if(GotSUD),GotSUD為真,則執行SetupCommand()函數,在這里處理控制傳輸,讀取描述符,設置特性,處理Vendor命令等。完畢后置GotSUD = FALSE;然后檢查USB各種狀態並處理:
7.5 Sleep
if (Sleep){
    // 如果USB進入了休眠狀態,這里Sleep是USB休眠標志,通GotSUD一樣,USB休眠后產生中斷,
    // 進入void ISR_Susp(void)函數處理,置sleep標志為TRUE。
    // 檢測USB是否掛起
    if(TD_Suspend()) {
    Sleep = FALSE; // 清Sleep標志
    do {
        EZUSB_Susp(); // 置8051為空閑狀態.
    } while(!Rwuen && EZUSB_EXTWAKEUP());
    // 如果喚醒
    EZUSB_Resume();
    // 從空閑狀態中恢復
    TD_Resume();
    
}
 
        到這里,一個事務處理完畢,等待下次事務處理中斷的到來.
7.6 To Be Continue...
在繼續看fw.c文件前,我覺得有必要先把USB的整個工作過程搞懂!
 這本書上講的不錯搞懂了USB整個工作過程,對FW中的列舉/重列舉就比較好理解了。這也是我自己曾經學習的一個過程吧,我並沒有一開始就去仔細推敲USB協議方面的東西,而是先看一個大概,然后學到哪里不懂的時候,再回過頭去進一步研究,這樣慢慢就理解深入了。
 個人覺得好多書里一上來就給你細細剖析協議的,完全是浪費腦細胞,越看越糊。哎~USB協議真的非常非常難搞通,我也深深的痛苦中。。。。。。
以下參照《USB原理與工程實踐》這本書上講的,我簡化了一下,算是自己的理解吧:
-  
USB連上電腦(實際是集線器HUB),但是還沒有上電。
也就是說VCC還沒有電壓,到VCC上有5V電壓有一個很短的延時過程。該過程主機PC和HUB通信。 -  
USB上電,但是還沒有被復位。
此時不能響應USB任何事務,也沒有被分配到任何設備地址,包括默認地址。集線器通過檢測D+,D-上的電壓來判斷是否有新的USB設備連接。 當檢測到有新的USB設備連接時,報告給PC。該過程主機和HUB通信。 -  
復位USB總線。
PC通知HUB復位USB總線,獲得傳輸模式低速or全速or高速。復位后,USB設備獲得一個默認的設備地址號0。此過程PC和HUB通信。 -  
USB設備獲得地址號0后,可使用該地址號進行某些事務處理。
使用地址號0,控制傳輸,端點0,主機開始和USB設備功能層開始通信。 USB功能層是USB總線結構中的一個,USB總線結構由USB功能層,USB設備層,USB接口層構成。USB功能層,主要負責數據傳輸操作,就是控制傳輸,中斷傳輸,塊傳輸,同步傳輸。 -  
主機開始獲得USB設備的信息
例如剛開始要獲得USB控制傳輸所支持的最大數據包長度,那么就要向USB設備發命令(發送GetDescription獲得設備描述符信息),於是啟動一個USB事務處理,而USB事務處理分為3個階段:
令牌階段
數據階段
握手階段
也就是說,這一步中,主機發送GetDescription請求讀取設備描述符,獲得USB控制傳輸所支持的最大數據包長度(只需讀取前8個字節即可),這是一個USB事務,既然是 事務,那么所有的USB事務必然從令牌包開始,於是USB固件首先等待令牌包到來,然后處理相應的命令。這樣,主機通過發送GetDescription請求(USB 11中標准請求之一 ),讀取設備描述符DeviceDscr,目的是獲取控制傳輸支持的最大字節數(第8個字節),一旦檢測到這個數,主機復位USB總線並開始進入枚舉過程. -  
至此,開始進入枚舉過程。
主機向USB設備發送SetAddress請求,為其分配一個新的,唯一的設備地址(1~127,總共128個)。以后,USB將使用這個新的地址號與主機通 信。 -  
主機循環向USB設備發出GetDescription請求,讀取所有的描述符,獲得該USB設備的全部配置信息。
首先讀取設備描述符DeviceDscr全部字段,
然后讀取配置描述符 Configuration,接口描述符,端點描述符,其他各種設備類描述符以及自定義描述符等。 -  
然后主機根據讀取的PID,VID選擇一個合適的驅動加載,如果第一次使用,則提示發現新硬件。
 -  
加載了USB驅動后,主機發送SetConfigration()請求為該USB設備選擇合適的配置,主機為該USB設備選擇一個配置值,一個接口,一個可替換設置值。
 -  
至此,USB枚舉結束。
 
7.7 重枚舉
那么,什么是重枚舉呢?
 首先,上面的USB枚舉是對通用USB來說的,一般USB設備只有枚舉過程,沒有重枚舉過程。也就是說其實,
 對EZ-USB系列來說,上面的枚舉舉實際包含了EZ-USB枚舉和重枚舉兩個過程:
-  
對EZ-USB來說,枚舉過程就是USB上電復位到加載固件前這段過程,此時USB設備地址號為默認的0號,枚舉完成后,驅動為cypress...eeprom..missing(FX2/FX2LP來說)。
 -  
然后加載固件,進行重枚舉,重枚舉完成后,顯示驅動為cypress...ez-usb...example或其他自定義設備。
 
為什么EZ-USB有重枚舉過程呢?這就是為了可以讓主機在前期固件未加載前自動枚舉,識別USB,從而可以從上位機加載固件到68013的RAM中,而無需使用ROM,EEPROM,FLASH等內存。實現“軟件”架構加載程序代碼,隨時修改固件。
7.8 EZ-USB如何控制重枚舉?
首先,EZUSB有一個寄存器USBCS,其中第1位USBCS.1為Renum控制枚舉重枚舉。
 在USB上電未加載固件代碼前,Renum=0,表示使用“EZUSB核心”對芯片的初始配置,處理主機設備請求,並負責把固件下載到RAM中,這個過程就是“枚舉”,完成該枚舉過程的設備叫做“缺省USB設備(地址號0)”,即驅動顯示為“...missing”的設備。
當固件被下載到8051 RAM后,此時Renum=1,表示使用“增強8051核心”處理主機設備請求,並按照固件的代碼(讀取所有描述符)重新配置USB設備,這個過程就叫做“重枚舉”,完成重枚舉后,顯示自定義的USB設備“...example”,實際是模擬斷開與連接的過程。 用以下表表示:
| 處理設備請求 | Renum | 8051動作 | |
|---|---|---|---|
| 枚舉 | EZUSB核心 | Renum=0 | 8051置Renum=1 | 
| 重枚舉 | 8051 | Renum=1 | 8051重置Renum=0 | 
在重枚舉完成后,對控制端點0的設備請求可以由“EZUSB核心”處理或由“增強8051核心”處理,有Renum的值決定。芯片上電時Renum=0,由“EZUSB核心”處理;一旦8051開始運行,就可以設置Renum=1,由“增強8051核心”處理,表示按照8051下載的固件代碼處理。
當然,這時也可以設置Renum=0,讓“EZUSB核心”處理端點0的設備請求,而讓8051完成具體的USB數據傳輸,這樣做會大大簡化8051固件代碼。 然后再看接下來的代碼:
注意,在這段代碼之前,EZUSB已經枚舉完成,當然,整個過程是自動的,我們看不見的。
#ifndef NO_RENUM if(!(USBCS & bmRENUM)) // 如果RENUM位為0,則重列舉
{
    EZUSB_Discon(TRUE); // renumerate 重列舉
}
#endif
 
        這段代碼即告訴USB進行重枚舉,用軟件設置,模擬USB斷開與連接,。EZUSB_Discon(TRUE)完成斷開連接,具體實現在EZUSB.lib庫中,大概代碼是這樣的:
void UsbDisconnect(BOOL renum) {
    if(renum)
        USBCS |= (bmDISCON | bmRENUM);
    else
        USBCS |= bmDISCON;
    EZUSB_Delay(1500);
    USBIRQ  = 0xff;
    EPIRQ   = 0xff;
    EZUSB_IRQ_CLEAR();
    USBCS &=~bmDISCON;
}
 
        將USBCS寄存器的DICON位置1,斷開USB,同時如果RENUM位為0,則置1;然后重新連接USB。
// unconditionally re-connect. If we loaded from eeprom we are
// disconnected and need to connect. If we just renumerated this
// is not necessary but doesn't hurt anything
USBCS &=~bmDISCON; // 重新連接
CKCON = (CKCON&(~bmSTRETCH)) | FW_STRETCH_VALUE;
// Set stretch
// clear the Sleep flag.
Sleep = FALSE; // 清sleep標志
 
        這段代碼英文意思說的很清楚了,CKCON還不清楚干什么用的。。。 緊接着主機開始讀描述符,並進行配置(發送SetConfiguration),加載驅動等,完成重枚舉。
8. dscr.a51文件
8.1 設備描述符DeviceDsc
重新看了描述符文件,對照了好多參考書,發現以前很多不明白的地方現在清晰了很多,不過有些地方可能從來沒有用過的緣故,我也是不甚明了(打上了問號),只能以后用到的時候,有新的發現再慢慢理解了...
dscr51里放的是USB描述符表,EZ-USB在重枚舉階段會讀取或設置相應的描述符
db DSCR_DEVICE_LEN  ;; Descriptor length
db DSCR_DEVICE      ;; Decriptor type
dw 0002H            ;; Specification Version (BCD)
db 00H              ;; Device class
db 00H              ;; Device sub-class
db 00H              ;; Device sub-sub-class
db 64               ;; Maximum packet size
dw 0B404H           ;; Vendor ID
dw 0410H            ;; Product ID (Sample Device)
dw 0000H            ;; Product version ID
db 1                ;; Manufacturer string index
db 2                ;; Product string index
db 0                ;; Serial number string index
db 1                ;; Number of configurations
 
        -  
db DSCR_DEVICE_LEN
bLength段, 指明整個設備描述符的長度,單位字節。 -  
db DSCR_DEVICE
bDescriporType段, 描述符類型值。DSCR_DEVICE=04H--設備描述符。 -  
dw 0002H
bcdUSB, 表明該USB設備所遵循的USB協議版本,用bcd碼表示,2字節。例如2.0版本,值為0200H,用bcd碼表示,低字節在前,高字節在后,表示為0002H;同理,1.1版本,則表示為1001H。 -  
db 00H
bDeviceClass段, 指明USB設備所屬的設備類。 
| Value | comment | 
|---|---|
| 0 | 表示USB各接口相互獨立工作,分屬不通的設備類,具體信息在接口描述符中說明 | 
| 1~FEH | 表明該USB設備屬於某個明確的設備類,例如04H代表顯示設備 | 
| FFH | 廠商自定義的設備類 | 
-  
db 00H
bDeviceSubClass段, 指明USB設備所述的設備子類。其值依賴bDeviceClass。 =0,此時bDeviceClass必須首先為0 =1~FEH,詳細的設備子類。例如如果bDeviceClass=04H,是顯示設備,則bDeviceSubClass=01H,表示CRT顯示器; =FFH,廠家自定義 -  
db 00H
bDevicePortcol段, 指明USB所使用的設備類協議。其值依賴bDeviceClass和bDeviceSubClass。 
| Value | comment | 
|---|---|
| 0 | 表示該設備不使用任何設備類協議 | 
| 1~FEH | 該USB必須屬於某個明確的設備類和子設備類。如視頻類協議(UVC),音頻類協議(UAC)等 | 
| FFH | 廠家自定義 | 
-  
db 64
bMaxPacketSize0段, 指明該USB設備端點0控制傳輸所支持的最大數據包長度,單位字節。 -  
dw 0B404H
VID -  
dw 0410H
PID -  
dw 0000H
bcdDevice段, 指明USB設備版本號。產品ID??? -  
db 1
iManuFacture段, 廠商信息字符串索引值,沒有時為0.這里為1,,即下面的“Cypress”字符串。 -  
db 2
iProduct段, 產品信息字符串索引值,沒有時為0.后面的“EZ-USB”字符串。 -  
db 0
iSerial段, USB設備序列號信息字符串索引值,沒有時為0. -  
db 1
bNumConfigurations段 指明USB設備所支持的配置數。???如果USB設備支持兩種傳輸速率,則該字段指出的是該速率下的配置數,而不是兩種速率下的配置數和。 
8.2 設備限定描述符DeviceQualDscr
設備限定描述符:DeviceQualDscr
DeviceQualDscr:
db DSCR_DEVQUAL_LEN     ;; Descriptor length
db DSCR_DEVQUAL         ;; Decriptor type
dw 0002H                ;; Specification Version (BCD)
db 00H                  ;; Device class
db 00H                  ;; Device sub-class
db 00H                  ;; Device sub-sub-class
db 64                   ;; Maximum packet size
db 1                    ;; Number of configurations
db 0                    ;; Reserved
 
        設備限定描述符,9個字段,共10字節。
 僅當該USB為高速USB設備,且設備既需支持高速(High Speed)又需支持全速(Full)時,就需要用到設備限定描述符。
例如該高速USB設備目前工作於全速模式,則該描述符中包含高速模式的總體信息。
在設備請求處理函數SetupCommand(void)中,當收到讀設備限定描述符請求時,會首先判斷是否為高速USB設備。if (HighSpeedCapable())。
-  
db DSCR_DEVQUAL_LEN
bLength段, 整個設備限定描述符的長度,單位字節,共10個字節。 -  
db DSCR_DEVQUAL
bDescriptorType段, 指出該描述符類型。06H->設備限定描述符。 -  
dw 0002H
bcdUSB段, USB協議版本號。 -  
db 00H
bDeviceClass段, 該USB設備所屬的USB設備類。 -  
db 00H
bDeviceSubClass段, 所屬子類。對bDeviceClass的進一步細化分類說明。 -  
db 00H
bDeviceProtocol段, 該設備所使用的設備類協議。 -  
db 64
bMaxPacketSize0段, 端點0控制傳輸所支持的最大數據包長度,單位字節。 -  
db 1
bNumConfigurations段 另一速率所支持的配置數。 -  
db 0
bReserved段 保留,必須為0. 
8.3 配置描述符HighSpeedConfigDscr/FullSpeedConfigDscr
配置描述符:
HighSpeedConfigDscr/FullSpeedConfigDscr
db DSCR_CONFIG_LEN      ;; Descriptor length
db DSCR_CONFIG          ;; Descriptor type
db (HighSpeedConfigDscrEnd-HighSpeedConfigDscr) mod 256 ;; Total Length (LSB)
db (HighSpeedConfigDscrEnd-HighSpeedConfigDscr) / 256   ;; Total Length (MSB)
db 1                    ;; Number of interfaces
db 1                    ;; Configuration number
db 0                    ;; Configuration string
db 10000000b            ;; Attributes (b7 - buspwr, b6 - selfpwr, b5 - rwu)
db 100                  ;; Power requirement (div 2 ma)
 
        配置描述符包含8個字段,共9字節。所有的USB設備至少包含一個配置描述符,例如這里包含兩個配置描述符高速HighSpeedConfigDscr和全速FullSpeedConfigDscr。
-  
db DSCR_CONFIG_LEN
bLength段, 描述符長度,9字節。 -  
db DSCR_CONFIG
bDescriptorType段, 描述符類型。 -  
db (HighSpeedConfigDscrEnd-HighSpeedConfigDscr) mod 256
 -  
db (HighSpeedConfigDscrEnd-HighSpeedConfigDscr) / 256
wTotalLength段,指明配置信息總長度,2字節表示。為配置描述符,接口描述符,端點描述符,設備類定義描述符,供應商自定義描述符長度的和。在這里只有配置、接口和端點描述符。 -  
db 1
bNumInterface段, 指明該配置所支持的接口數(??),最小為1. -  
db 1
bConfigurationValue段, 指明該配置的配置值。例如這里值為1,在重枚舉時,主機發送Setconfiguration(x),當x=1時,就調用該配置。 -  
db 00
iConfiguration段, 該配置的字符串索引值,沒有時為0. -  
db 10000000b
bmAttributes段, 指明該配置的特性,8位。
b0~b4,保留,必須為0.
b5:遠程喚醒選擇。=1,支持遠程喚醒;=0,不支持遠程喚醒。
b6:是否總線電源選擇。如果該USB設備外加了電源,=1,使用總線電源,=0,使用自供的電源。
b7:必須為1. 在主機設備請求case SC_GET_STATUS,case GS_DEVICE中獲得該信息。 -  
db 100
bMaxPower段, 總線供電時,該USB設備可獲得的最大電流。單位2mA,所以最大值為250.如果該電流得不到滿足,USB將不能使用這個配置。 
8.4 接口描述符Interface Descriptor
接口描述符
;; Interface Descriptor
db DSCR_INTRFC_LEN ;; Descriptor length
db DSCR_INTRFC     ;; Descriptor type
db 0               ;; Zero-based index of this interface
db 0               ;; Alternate setting
db 6               ;; Number of end points
db 0ffH            ;; Interface class
db 00H             ;; Interface sub class
db 00H             ;; Interface sub sub class
db 0               ;; Interface descriptor string index
 
        接口描述符有9個字段,共9字節。
 注意,主機不能用SetDescription和GetDescription來設置和讀取接口描述符,它只能作為配置描述符的一部分信息返回,在主機發送case SC_GET_DESCRIPTOR,且case GD_CONFIGURATION時一並讀取。所以我們看到,在fw.c文件中並沒有對接口描述符的判斷。
-  
db DSCR_INTRFC_LEN
bLength段, 描述符長度。 -  
db DSCR_INTRFC
bDescriptorType段, 描述符類型。 -  
db 0
bInterfaceNumber段, 指明該接口的接口號。 -  
db 0
bAlternateSetting段, 指明接口的可替換設置值。 -  
db 6
bNumberEndpoints段, 指明接口所使用的斷點數,不包括端點0. -  
db 0ffH
bInterfaceClass段, 指明接口所使用的設備類。 
| Value | comment | 
|---|---|
| 0 | 保留 | 
| 1~FEH | 表明該接口屬於某個明確的USB設備類 | 
| FFH | 廠家自定義的設備類 | 
-  
db 00H
bInterfaceSubClass段, 該接口所屬的USB設備子類。 -  
db 00H
bInterfaceProtocol段, 該接口所使用的設備類協議。 -  
db 0
iInterface段, 接口字符串描述符的索引值,沒有時為0. 
8.5 端點描述符Endpoint Descriptor
;; Endpoint Descriptor
db DSCR_ENDPNT_LEN ;; Descriptor length
db DSCR_ENDPNT     ;; Descriptor type
db 02H             ;; Endpoint number, and direction
db ET_BULK         ;; Endpoint type
db 00H             ;; Maximun packet size (LSB)
db 02H             ;; Max packect size (MSB)
db 00H             ;; Polling interval
 
        端點描述符有6個字段,共7字節。和接口描述符一樣,也不能由主機通過發送GetDedcription()請求讀取,只能作為配置信息case GD_CONFIGURATION的一部分返回給主機。
-  
db DSCR_ENDPNT_LEN
bLength段, 該描述符長度,單位字節。 -  
db DSCR_ENDPNT
bDescriptorType段, 該描述符類型。 -  
db 02H
bEndpointAddress段, 指明端點的端點號及傳輸方向。
b0~b3:該端點的端點號。如0001端點1,0010端點2;
b4~b6::保留,必須為0
b7:端點傳輸方向。1-IN傳輸;0-OUT傳輸 -  
db ET_BULK
bmAttributes段, 指明端點的一些特性。
b0~b1:端點的傳輸類型。 
| Value | comment | 
|---|---|
| 00 | 控制傳輸 | 
| 01 | 同步傳輸 | 
| 10 | 塊傳輸 | 
| 11 | 中斷傳輸 | 
b2~b3:當該端點為同步端點時,這兩位指出同步類型。
| Value | comment | 
|---|---|
| 00 | 非同步 | 
| 01 | 異步 | 
| 10 | 自適應 | 
| 11 | 同步 | 
b4~b5:端點用法類型。
| Value | comment | 
|---|---|
| 00 | 數據端點 | 
| 01 | 顯示反饋端點 | 
| 10 | 隱式反饋端點 | 
| 11 | 保留 | 
b6~b7:保留,必須為0
- db 00H
wMaxpacketSize段(LSB), 指明端點所支持的最大數據包長度,共16位。
b0~b10:端點所支持的最大數據包長度。
b11~b12:當該端點為高速中斷端點或同步端點時,這兩位指出每小幀中最多傳輸的事務數。 
| key | comment | 
|---|---|
| 00 | 每小幀1次(默認), | 
| 01 | 每小幀2次(附加一次) | 
| 10 | 每小幀3次(附加2次) | 
| 11 | 保留。 | 
b13~b15保留,必須為0
-  
db 02H
wMaxpacketSize段(MSB)
高8位。
數據包大小為:0000 0010 0000 0000,取0~10位,還是0200,512字節。 -  
db 00H
bInterval段, 指明端點數據傳輸的訪問間隔。
低速中斷端點:=10~255ms.
全速中斷端點:=1~255ms
高速中斷端點:=1~16,
訪問間隔為2(bInterval-1)(冪)×1us 全速/高速同步端點:=1~16,
訪問間隔為2(bInterval-1)(冪)×1ms 和2(bInterval-1)(冪)×1us 高速塊/控制out端點:指明其最大NAK握手包發送速率。 =0,表示該端點永遠不會發出NAK握手包 =其他值,表示每個bInterval時間內,該端點最多只能發送一次NAK握手包。 其他類型端點:該字段無效。 
8.6 字符串描述符StringDscr1
字符串描述符:StringDscr1:
StringDscr1:
db StringDscr1End-StringDscr1   ;; String descriptor length
db DSCR_STRING
db 'C',00
db 'y',00
db 'p',00
db 'r',00
db 'e',00
db 's',00
db 's',00
StringDscr1End:
 
        3個自獨,長度是變化的(字節)。
| 偏移量 | 域 | 大小 | 值 | 描述 | 
|---|---|---|---|---|
| 0 | bLength | 1 | N+2 | 此描述表的字節數 | 
| 1 | bDescriptorType | 1 | 常量 | 字串描述表類型 | 
| 2 | wLANGID[0] | 2 數字 | 語言標識 | (LANGID)碼0 | 
| … | … | … | … | … | 
| N | wLANGID[x] | 2 | 數字 | 語言標識(LANGID) | 
-  
db StringDscr1End-StringDscr1
bLength段, 描述符長度 -  
db DSCR_STRING
bDescriptorType段, 描述符類型。 -  
Unicode編碼的字符串。
 
9. 學習資料
【關於USB】這家伙總結得不孬啊,我剛看一會兒就學了不少東西。
 摘自: http://www.daxiamcu.com/bibis/moredata30_1384202.shtml
-  
元器件
CYPRESS 68013A:支持USB 2.0協議,帶增強型8051單片機,時鍾頻率48Mhz。支持串口通訊。 -  
文檔
 
| 文檔名 | 說明 | 
|---|---|
| cy7c68013.pdf | 68013外設手冊 | 
| cy7c68013_5.pdf | 68013外設手冊 | 
| CY3684_A_SCH.PDF | 68013A外圍電路圖 | 
| FX2 TechRefManual.pdf | EZUSB-FX2技術手冊 | 
| fx2_to_fx2lp.pdf | FX2和FX2LP的區別 | 
| CYAPI.PDF | CYAPI手冊 高級類庫 | 
| CYUSB.PDF | CYUSB手冊 底層API | 
10. 開發環境
- Keil C 7.0編譯器
 - C++ Builder 6.0
 - VC++ 6.0
 - EEPROM燒寫器
 - 68013A的開發包(含CYPRESS CONSOLE、CYUSB.SYS、例程等)
 - BUS HOUND 5.0
 
11. 開發流程
11.1 硬件程序編寫
- 根據CYPRESS的示例程序建立工程框架,一般由FW.C PERIPH.C和定義寄存器的幾個頭文件組成。如下圖:
 - FW.C負責了設備連接、重枚舉、設備初始化等過程
 - PERIPH.C負責響應各種中斷事件。
 - dscr.a51文件定義了USB設備握手時需要的各種描述符
 - FX2REGS.H定義了USB中所有的寄存器
 - FX2.H主要定義了各種二級中斷向量和描述符的數據結構
 - 編譯后的二進制代碼和工程同名,擴展名為HEX。
 - 相應的頭文件和類庫在KEIL C的lib和inc文件夾內,需在項目設置中設置路徑。
 
11.2 硬件程序燒錄
-  
EFROM
因為本產品要求將二進制代碼和硬件PID/VID燒錄在EEPROM,而不是使用CYPRESS推薦的在線下載方式,所以外部采用了8K 的EEPROM。上電后68013A會將EEPROM中的數據和程序加載到RAM中運行。 -  
HEX2BIN.EXE
HEX文件只是68013A上8051的程序代碼,還要加上PID/VID等信息才能正確運行,CYPRESS在開發包中提供了HEX2BIN.EXE這個工具,可以根據HEX生成完備的IIC文件,將此文件燒錄到EEPROM上即可。 -  
HEX2BIN.EXE的使用方法如下:
將XXX.HEX文件拷貝到HEX2BIN.EXE所在目錄,打開CMD,按如下格式輸入:
hex2bix -i -o xxx.iic xxx.hex -f 0xC2 -v 0x1234 -p 0x1234 
| key | comment | 
|---|---|
| -i | 表示輸出文件,也就是IIC文件 | 
| -o | 表示輸入文件,也就是HEX文件 | 
| -f | 表示68013A發送PID/VID的方式,這里為C0,即從EEPROM上讀取。 | 
| -v | 表示VID的BCD碼,開發階段使用1234 | 
| -p | 表示PID的BCD碼,開發階段使用1234 | 
- IIC文件燒錄到EEPROM上
將生成的IIC文件用燒寫器燒錄到EEPROM上,本項目使用的是深圳思泰佳電子公司的NSP通用燒寫器,此燒寫器不支持IIC類型,選擇BIN類型可替代。 
11.3 驅動的識別
- 將EEPROM連到68013A上后,接上USB線,上電。計算機提示找到新硬件,要求安裝驅動。
 - CYPRESS針對68013A提供了全新的驅動程序CYUSB.SYS。這個驅動使用了新的API,所以上位機的編寫上和舊的方式完全不同。底層的IOCTL控制字的定義也完全不同,詳見CYAPI.PDF和CYUSB.PDF。
 - 安裝驅動之前,必須先根據VID/PID正確編輯CYUSB.INF文件,在文件中添加自己的PID/VID代碼和設備描述,連接設備時, 將根據硬件上的PID/VID查找INF文件中對應的驅動,如果找不到,在設備管理器中將顯示“68013 EEPROM MISSING”的字樣。
 - 詳細的INF配置方法參考CYUSB.PDF PART1/PART2/PART3。這里不在贅述。
 - 安裝驅動時候找到修改好的CYUSB.INF文件,驅動將被正確安裝,此時設備可以正常使用。
 
11.4 測試過程
- 被正確識別的設備可以在CYPRESS CONSOLE上看到設備信息。如圖:
 - CYPRESS CONSOLE的具體使用方法請參考CyConsole.chm。
 - 要注意的是,除EP0/EP1外,當其他端點Max Pkt Size大小為64字節時,表示工作在USB 1.1模式,有可能是軟件的原因, 也有可能是外圍上拉電阻的問題。開發中要特別注意。
 
11.5 推薦開發流程
- 看本介紹USB 2.0協議的書,對USB 2.0協議有所了解。推薦《USB 2.0原理與工程開發》
 - 看CYUSB.PDF文檔。了解驅動安裝方法。
 - 看KEIL C51的書籍,熟悉C51的編程方法,熟悉KEIL C編程環境。
 - 看CYPRESS提供的例程,了解68013A編程框架。推薦《EZ-USB 2100系列單片機原理、編程及應用》(基本框架類似,部 分寄存器定義不同)。 5.5 對照USB 2.0協議,編寫dscr.a51文件,配置各種描述符。
 - 結合FX2 TechRefManual.pdf,研讀FW.C、PERIPH.C、FX2REGS.H、FX2.H,了解寄存器的定義。
 - 根據系統需求編寫相應代碼,有開發板時,根據開發版上的LED來測試程序正確與否。
 - 根據CYAPI.PDF CYUSB.PDF編寫上位機通訊程序。 同步讀取數據方法 XferData(); 異步讀取數據方法 BeginDataXfer()/WaitForXfer()/FinishDataXfer();
 - 調試程序。
 - 編寫其他8051上的程序,並繼續調試。
 
11.6 發布時應提供的文件
- CYUSB.SYS
 - CYUSB.INF
 - XXX.IIC
 
12. 重點講解
12.1 如何理解CYPRESS 68013A程序框架
CYPRESS提供了非常好的程序框架,免去了用戶自己編寫一些通用性比較強、模式化的程序(如果不提供,很少有人能寫出如 此高效,結構緊湊的程序,實際上此框架和68013A內部結構關系密切,一般人也沒有足夠的內部資料也不可能寫出來)。在框 架的基礎上,用戶只需在相應的地方寫相應的代碼即可完成USB工作。
一般來說框架可以分成3個部分。
-  
描述符文件
例如dscr.a51文件,里面定義了枚舉設備的時候要用的各種描述符信息,這部分用戶需要根據實際的情況自己編寫。我寫的時候發現一個最大的問題就是各種書籍協議版本不同,翻譯質量不同,同一字段的意義表述不同,容易讓人產生困惑。例如USB 1.1/2.0/2.13對設備類型的子類定義都不完全相同,所以寫的時候最好幾種文檔對比起來寫。由於USB官方網站的文檔中字段解釋過於專業化,所以對USB不是很熟悉的人比較難以理解其真正含義。所以要多參考不同的書籍,某種程度上降低了開發速度,但對第一次做USB開發的人來說,這也是值得的。 -  
固件文件
例如FW.C文件,這是硬件程序的函數入口。主要有以下這些方法: 
void SetupCommand(void); // 握手命令處理
void TD_Init(void);      // 初始化,完成配置,啟動時調用一次
void TD_Poll(void);      // 用戶處理程序,循環調用
void IO_Init(void);      // 8051 IO初始化
void REG_Init(void);     // 8051寄存器初始化
BOOL TD_Suspend(void);   // 掛起處理
BOOL TD_Resume(void);    // 喚醒處理
// 以下為各種描述符的獲取和設置函數,重枚舉時自動調用
BOOL DR_GetDescriptor(void);
BOOL DR_SetConfiguration(void);
BOOL DR_GetConfiguration(void);
BOOL DR_SetInterface(void);
BOOL DR_GetInterface(void);
BOOL DR_GetStatus(void);
BOOL DR_ClearFeature(void);
BOOL DR_SetFeature(void);
BOOL DR_VendorCmnd(void);
 
        - 功能文件
處理各種中斷。例如PERIPH.C文件。8051一般默認只有四個中斷,這顯然不夠USB使用,所以 CYPRESS引入了自動向量的概念,相當於軟中斷,大大擴展了現有的中斷數量。主要的中斷有: 
void ISR_Sudav(void) interrupt 0     // 收到setup包
void ISR_Sutok(void) interrupt 0     // 收到SETUP令牌
void ISR_Sof(void) interrupt 0       // 收到起始幀
void ISR_Ures(void) interrupt 0      // 收到RESET
void ISR_Susp(void) interrupt 0      // 收到掛起信息
void ISR_Highspeed(void) interrupt 0 // 高速模式
void ISR_Ep0ack(void) interrupt 0    // 正常響應ACK
void ISR_Stub(void) interrupt 0
void ISR_Ep0in(void) interrupt 0
void ISR_Ep0out(void) interrupt 0
void ISR_Ep1in(void) interrupt 0
void ISR_Ep1out(void) interrupt 0    // EP1輸入中斷
void ISR_Ep2inout(void) interrupt 0  // EP2中斷
void ISR_Ep4inout(void) interrupt 0
void ISR_Ep6inout(void) interrupt 0
void ISR_Ep8inout(void) interrupt 0
void ISR_Ibn(void) interrupt 0
void ISR_Ep0pingnak(void) interrupt 0
void ISR_Ep1pingnak(void) interrupt 0
void ISR_Ep2pingnak(void) interrupt 0
void ISR_Ep4pingnak(void) interrupt 0
void ISR_Ep6pingnak(void) interrupt 0
void ISR_Ep8pingnak(void) interrupt 0
void ISR_Errorlimit(void) interrupt 0
void ISR_Ep2piderror(void) interrupt 0
void ISR_Ep4piderror(void) interrupt 0
void ISR_Ep6piderror(void) interrupt 0
void ISR_Ep8piderror(void) interrupt 0
void ISR_Ep2pflag(void) interrupt 0
void ISR_Ep4pflag(void) interrupt 0
void ISR_Ep6pflag(void) interrupt 0
void ISR_Ep8pflag(void) interrupt 0
void ISR_Ep2eflag(void) interrupt 0
void ISR_Ep4eflag(void) interrupt 0
void ISR_Ep6eflag(void) interrupt 0
void ISR_Ep8eflag(void) interrupt 0
void ISR_Ep2fflag(void) interrupt 0
void ISR_Ep4fflag(void) interrupt 0
void ISR_Ep6fflag(void) interrupt 0
void ISR_Ep8fflag(void) interrupt 0
void ISR_GpifComplete(void) interrupt 0
void ISR_GpifWaveform(void) interrupt 0
 
        特別是對於接受數據,一般都在中斷中完成相應處理,“中斷中適合進行少量簡短的操作,不適合進行復雜操作”,這句話在 此依然有效。如果要進行復雜的操作可以在TD_POLL()中進行(多數操作都是在這個函數中完成的)。
 另外非常重要的一點是,中斷程序的結尾應該讓中斷復位,允許下一次中斷,有些端點的計數器也要清零並允許接受新的中斷 請求。例如:
EP1OUTBC = 0;      // 清空計數
EZUSB_IRQ_CLEAR(); // USB中斷復位
EPIRQ = 0x08;      // 允許EP1中斷請求
 
        12.2 68013A端點寄存器介紹
68013A內部的寄存器約有300個上下,一次都記住是不可能的,而且每個寄存器都有8個位,也就是說一共有2000多個可以配置的位,一次都理解掌握這些位的含義也是不可能的,所幸地是開發中並不會用到所有的寄存器,但是依然強烈建議把 FX2REGS.H和FX2.H走讀一邊,這就像讀書一樣,沒有學會識字,再看都是天書。結合FX2 TechRefManual.pdf走讀這些寄存器 大約需要一到兩天時間,這點時間投入還是值得的。
 在通訊過程中,打交道最多的是各種端點寄存器,掌握好這些寄存器地使用對提升開發效率是很有幫助。值得特別關注的寄存 器和配置位如下:
- Rwuen
 - REVCTL
 - EP1OUTCFG
 - EP1INCFG
 - EP2CFG
 - EP4CFG
 - EP6CFG
 - EP8CFG
 - EP2FIFOCFG
 - EP4FIFOCFG
 - EP6FIFOCFG
 - EP8FIFOCFG
 - FIFORESET
 - EPIRQ
 - EPIE
 - EP1OUTBC
 - APTR1H
 - APTR1L
 - EXTAUTODAT1
 - AUTOPTRH2
 - AUTOPTRL2
 - EXTAUTODAT2
 - EP2BCH
 - EP2BCL
 
其中有些寄存器的設置需要連續設置多次,看似重復了,其實不然,這和設置的緩沖區數量有關。
 有些寄存器中間必須用SYNCDELAY來延時。這類寄存器FX2 TechRefManual.pdf上有說明。
對於EP0,用於系統握手,相關的寄存器操作基本上都由68013A的內核(SIE)來完成了。
 對於EP1,分為OUT/IN兩組配置和寄存器。
 對於EP2~EP8,不分OUT/IN輸入輸出,主要有EP2CFG/ EP2FIFOCFG/ EP2BCH/EP2BCL寄存器。
12.3 什么是自動指針
自動指針是CYPRESS提供的一個非常有用的特性。
 在數據交互的過程中,很多時候都涉及到數據的搬遷,比如從EP2OUT收到的 數據需要轉發到EP6IN上(一些轉換類設備);再比如從RAM中拷貝數據到EP4IN上,傳統的做法是申明兩個指針,指向源和目 的地址,然后用循環一個個字節拷貝,同時還要考慮增加指針地址,對於連續的空間這到不是問題,關鍵是如果數據需要拷貝 到多個緩沖時,指針地址是循環的。這時候如果手工完成操作很容易出錯。 因此CYPRESS提供了兩組自動指針,用的時候一組指向源,一組指向目的地址。然后循環拷貝數據就行了,自動指針會自動指 向下一個源或目的空間,不論是否是循環地址方式。這樣減少了程序出錯的幾率。
下面的程序將EP2OUT接受到的數據拷貝到EP6IN發送出去:
if(!(EP2468STAT & bmEP6FULL)) { 
    // check EP6 FULL(busy) bit in EP2468STAT (SFR), 
    // core set's this bit when FIFO is full APTR1H = MSB( &EP2FIFOBUF ); 
    
    APTR1L = LSB( &EP2FIFOBUF ); 
    AUTOPTRH2 = MSB( &EP6FIFOBUF ); 
    AUTOPTRL2 = LSB( &EP6FIFOBUF ); 
    count = (EP2BCH << 8) + EP2BCL; 
    // loop EP2OUT buffer data to EP6IN 
    for( i = 0x0000; i < count; i++ ) { 
        // setup to transfer EP2OUT buffer to EP6IN buffer using AUTOPOINTER(s) 
        EXTAUTODAT2 = EXTAUTODAT1; 
    } 
    EP6BCH = EP2BCH; 
    SYNCDELAY; 
    EP6BCL = EP2BCL; // arm EP6IN 
    SYNCDELAY; 
    EP2BCL = 0x80; // re(arm) EP2OUT 
}
 
        -  
APTR1H/APTR1H
通過MSB和LSB獲取EP2FIFOBUF的高位地址和地位地址。 -  
EXTAUTODAT1
表示APTR1H/APTR1H指向的數據。 -  
AUTOPTRH2/AUTOPTRL2
通過MSB和LSB獲取EP6FIFOBUF的高位地址和地位地址。 -  
EXTAUTODAT2
表示AUTOPTRH2/AUTOPTRL2指向的數據。 
12.4 CYUSB和CYAPI的關系
以前68013上位機程序的編寫過程中,應用程序端通過調用DeviceIoControl() API或CREATEPIPE() API與驅動進行交互,繼而讀寫控制硬件設備,在新的68013A的驅動中采用了兩種新的調用方法:
第一種
 繼續使用DeviceIoControl()函數讀寫,不同的是,IOCTL控制字和老驅動完全不同,具體定義參考CYUSB.PDF。用戶可以通過這些底層API完成操作。
第二種
 使用CYPRESS提供的面對對象的類,一共有9個類,調用這些類的方法就可以和硬件打交道。這些類是對
第一種方法的封裝,使用起來非常簡便。 用戶可以根據需要選擇這兩種方法或混合使用,使用時需要加上頭文件CyAPI.h和cyioctl.h,另外在項目中還要引用 CyAPI.lib。
12.5 同步和異步讀寫的比較
CYAPI提供了同步和異步讀寫方式。
 同步方式的時候調用線程阻塞在哪里,直到讀寫到數據或超時;
 異步方式的時候調用線程 立即返回。具體實例參考CYAPI.PDF。
12.6 如何用C++ BUILDER寫上位機程序
- 首先確定使用7.4中的第幾種方法,添加相應的頭文件和庫文件。
 - 連接USB設備,確保驅動已經被正確加載。
 - 編寫收發數據線程。通過開發板上的LED或CYPRESS CONSOLE或BUS HOUND分析收發正確與否。
 
12.7 U盤如何正確加載驅動
在WINDOWS 2000/XP上U盤使用的PID/VID應該直接能加載操作系統默認的海量存儲器的驅動程序。為了使用正確的PID/VID,可以通過以下途徑:
- 找一個現有品牌的U盤,看看他的PID/VID是何值。
 - 在注冊表中查找海量存儲器信息。
 
12.8 其他問題
- 編寫上位機的時候要注意添加異常處理。
 - 調試上位機的時候,USB外設應正確連接。
 - 8051其他模塊的編寫請參考相應書籍
 
