EZ-USB FX2(68013)固件研究


原始資料來自網絡 整理日: 2015年2月12日


1. Welcome

算是給所有正在學習USB,還徘徊着不得其門而入的朋友一個入門的契機吧,我也深知入門的痛苦,有些人入門就是抱着那什么USB協議,包定義,幀格式......啃來啃去的,結果啃不出個所以然來。

依我的經驗來看,協議方面的東東,隨便找本書,過一遍就行了;然后,你的終點應該放在你如何來寫第一個成功的USB固件;而要寫USB固件,那么了解Cypress固件架構是必要的,也是重中之重;再然后,等你積累了一些端點,控制,bulk,中斷傳輸,SlaveFIFO,GPIF等等的經驗后,再回過頭去看協議方面的內容,就會有更加深刻的體會了;然后,你就可以試着更改FW。c文件了——這個時候你就是高手了。

2. 工具

  1. 68013的USB開發板
    淘寶隨便買塊好了,還送不少資料。

  2. 要准備開發工具
    去Cypress官網下一個Cy3684的開發包;
    全稱: cy3684_ez_usb_fx2lp_development_kit_15.exe
    網址: www.cypress.com/?rID=14321

  3. 安裝開發包
    工具就是Cypress USB Console了。怎么用不用我說了吧.

3. 固件架構

以一個3684開發包自帶的例子講解。

  1. 進入目錄(個人找自己的)
    D:\Program Files\Cypress\USB\Examples\FX2LP\Bulkloop,

  2. 3個頭文件拷到bulkloop文件夾
    文件夾:D:\Program Files\Cypress\USB\Target
    Cypress頭文件: Fx2.h, fx2regs.h, syncdly.h

  3. keil設置
    output里關掉Run User Program #1(前面的勾去掉)

  4. 檢查keil C51文件路徑是否正確
    如果你的keil是直接裝載C:\Keil....下,那不會有錯誤,否則,自行設置正確的路徑。

4. 編譯bulkloop工程

在工程下,有以下幾個文件:其中,USBJmp.OBJ, EZUSB.LIB基本上是每個工程都要添加的,是一些中斷向量表,EZUSB的函數庫等等,不用管它們。 現在重點看前面三個文件:

  1. fw.c
    這個文件是整個USB的固件根本(FirmWare的縮寫),USB協議方面的通信都是在這里完成的,包括上電枚舉,重枚舉,喚醒以及調用用戶自己的程序和控制命令等等。基本上,如非必要,盡量不要動這個文件的內容,也不要在里面書寫你自己的任何代碼。

  2. 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:這幾個函數是當使用端點中斷傳輸時,中斷代碼的書寫處,很少用。 以上,是經常會用到的幾個函數;其他,基本不常用。

  3. dscr.51
    這個文件是USB描述符文件,包括了設備描述符,接口描述符,端點描述符,字符串等等。里面的英文都注釋得很詳細了,我就不多做介紹了,剛開始入門的時候,這個文件也不必改動。

5. 幾個包含文件

  1. fx2.h
    預定義,宏及函數聲明

  2. fx2regs.h
    68013的寄存器地址定義。

  3. syncdly.h
    同步延時。在其他文件里經常調用的一個函數SYNCDELAY就是這里定義的。

  4. 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緩沖區。數據流向為:

  1. PC端(console軟件)設定要傳輸的數據
  2. PC端發起端點2out傳輸,數據到達68013端點2的out緩沖區
  3. bulkloop固件查詢到端點2的out緩沖區有數據,於是將數據發往端點6的IN緩沖區
  4. 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中方便的看到結果,板子不在身邊,就懶的貼圖了。

  1. 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。這里就用到了前面定義的變量,

  1. IntDescrAddr
    保存內部RAM首址0X80,

  2. ExtDescrAddr
    保存我們獲得的當前描述符表外部RAM的首址 ,

  3. 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中斷
  1. EZUSB_IRQ_ENABLE();
    預定義是EZUSB=1,查TRM得知,ezusb是EIE寄存器的第0位,EIE.0=1,使能USB中斷;

  2. EZUSB_ENABLE_RSMIRQ();
    EICON |= 0x20,EICON.5=1,使能遠程喚醒中斷;

  3. INTSETUP |= (bmAV2EN | bmAV4EN);
    使能INT2,4自動向量跳轉; USBIE |= bmSUDAV | bmSUTOK | bmSUSP | bmURES | bmHSGRANT;使能所選擇中斷,相關的中斷意義,我也一知半解,后面慢慢學習補充;

  4. 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事務處理有三個階段:

  1. 令牌階段
  2. 數據階段
  3. 握手階段。
    每個階段數據傳輸是有各種包組成的,例如令牌階段:同步字段+令牌包+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原理與工程實踐》這本書上講的,我簡化了一下,算是自己的理解吧:

  1. USB連上電腦(實際是集線器HUB),但是還沒有上電。
    也就是說VCC還沒有電壓,到VCC上有5V電壓有一個很短的延時過程。該過程主機PC和HUB通信。

  2. USB上電,但是還沒有被復位。
    此時不能響應USB任何事務,也沒有被分配到任何設備地址,包括默認地址。集線器通過檢測D+,D-上的電壓來判斷是否有新的USB設備連接。 當檢測到有新的USB設備連接時,報告給PC。該過程主機和HUB通信。

  3. 復位USB總線。
    PC通知HUB復位USB總線,獲得傳輸模式低速or全速or高速。復位后,USB設備獲得一個默認的設備地址號0。此過程PC和HUB通信。

  4. USB設備獲得地址號0后,可使用該地址號進行某些事務處理。
    使用地址號0,控制傳輸,端點0,主機開始和USB設備功能層開始通信。 USB功能層是USB總線結構中的一個,USB總線結構由USB功能層,USB設備層,USB接口層構成。USB功能層,主要負責數據傳輸操作,就是控制傳輸,中斷傳輸,塊傳輸,同步傳輸。

  5. 主機開始獲得USB設備的信息
    例如剛開始要獲得USB控制傳輸所支持的最大數據包長度,那么就要向USB設備發命令(發送GetDescription獲得設備描述符信息),於是啟動一個USB事務處理,而USB事務處理分為3個階段:
    令牌階段
    數據階段
    握手階段
    也就是說,這一步中,主機發送GetDescription請求讀取設備描述符,獲得USB控制傳輸所支持的最大數據包長度(只需讀取前8個字節即可),這是一個USB事務,既然是 事務,那么所有的USB事務必然從令牌包開始,於是USB固件首先等待令牌包到來,然后處理相應的命令。這樣,主機通過發送GetDescription請求(USB 11中標准請求之一 ),讀取設備描述符DeviceDscr,目的是獲取控制傳輸支持的最大字節數(第8個字節),一旦檢測到這個數,主機復位USB總線並開始進入枚舉過程.

  6. 至此,開始進入枚舉過程。
    主機向USB設備發送SetAddress請求,為其分配一個新的,唯一的設備地址(1~127,總共128個)。以后,USB將使用這個新的地址號與主機通 信。

  7. 主機循環向USB設備發出GetDescription請求,讀取所有的描述符,獲得該USB設備的全部配置信息。
    首先讀取設備描述符DeviceDscr全部字段,
    然后讀取配置描述符 Configuration,接口描述符,端點描述符,其他各種設備類描述符以及自定義描述符等。

  8. 然后主機根據讀取的PID,VID選擇一個合適的驅動加載,如果第一次使用,則提示發現新硬件。

  9. 加載了USB驅動后,主機發送SetConfigration()請求為該USB設備選擇合適的配置,主機為該USB設備選擇一個配置值,一個接口,一個可替換設置值。

  10. 至此,USB枚舉結束。

7.7 重枚舉

那么,什么是重枚舉呢?
首先,上面的USB枚舉是對通用USB來說的,一般USB設備只有枚舉過程,沒有重枚舉過程。也就是說其實,
對EZ-USB系列來說,上面的枚舉舉實際包含了EZ-USB枚舉和重枚舉兩個過程:

  1. 對EZ-USB來說,枚舉過程就是USB上電復位到加載固件前這段過程,此時USB設備地址號為默認的0號,枚舉完成后,驅動為cypress...eeprom..missing(FX2/FX2LP來說)。

  2. 然后加載固件,進行重枚舉,重枚舉完成后,顯示驅動為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
  1. db DSCR_DEVICE_LEN
    bLength段, 指明整個設備描述符的長度,單位字節。

  2. db DSCR_DEVICE
    bDescriporType段, 描述符類型值。DSCR_DEVICE=04H--設備描述符。

  3. dw 0002H
    bcdUSB, 表明該USB設備所遵循的USB協議版本,用bcd碼表示,2字節。例如2.0版本,值為0200H,用bcd碼表示,低字節在前,高字節在后,表示為0002H;同理,1.1版本,則表示為1001H。

  4. db 00H
    bDeviceClass段, 指明USB設備所屬的設備類。

Value comment
0 表示USB各接口相互獨立工作,分屬不通的設備類,具體信息在接口描述符中說明
1~FEH 表明該USB設備屬於某個明確的設備類,例如04H代表顯示設備
FFH 廠商自定義的設備類
  1. db 00H
    bDeviceSubClass段, 指明USB設備所述的設備子類。其值依賴bDeviceClass。 =0,此時bDeviceClass必須首先為0 =1~FEH,詳細的設備子類。例如如果bDeviceClass=04H,是顯示設備,則bDeviceSubClass=01H,表示CRT顯示器; =FFH,廠家自定義

  2. db 00H
    bDevicePortcol段, 指明USB所使用的設備類協議。其值依賴bDeviceClass和bDeviceSubClass。

Value comment
0 表示該設備不使用任何設備類協議
1~FEH 該USB必須屬於某個明確的設備類和子設備類。如視頻類協議(UVC),音頻類協議(UAC)等
FFH 廠家自定義
  1. db 64
    bMaxPacketSize0段, 指明該USB設備端點0控制傳輸所支持的最大數據包長度,單位字節。

  2. dw 0B404H
    VID

  3. dw 0410H
    PID

  4. dw 0000H
    bcdDevice段, 指明USB設備版本號。產品ID???

  5. db 1
    iManuFacture段, 廠商信息字符串索引值,沒有時為0.這里為1,,即下面的“Cypress”字符串。

  6. db 2
    iProduct段, 產品信息字符串索引值,沒有時為0.后面的“EZ-USB”字符串。

  7. db 0
    iSerial段, USB設備序列號信息字符串索引值,沒有時為0.

  8. 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())。

  1. db DSCR_DEVQUAL_LEN
    bLength段, 整個設備限定描述符的長度,單位字節,共10個字節。

  2. db DSCR_DEVQUAL
    bDescriptorType段, 指出該描述符類型。06H->設備限定描述符。

  3. dw 0002H
    bcdUSB段, USB協議版本號。

  4. db 00H
    bDeviceClass段, 該USB設備所屬的USB設備類。

  5. db 00H
    bDeviceSubClass段, 所屬子類。對bDeviceClass的進一步細化分類說明。

  6. db 00H
    bDeviceProtocol段, 該設備所使用的設備類協議。

  7. db 64
    bMaxPacketSize0段, 端點0控制傳輸所支持的最大數據包長度,單位字節。

  8. db 1
    bNumConfigurations段 另一速率所支持的配置數。

  9. 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。

  1. db DSCR_CONFIG_LEN
    bLength段, 描述符長度,9字節。

  2. db DSCR_CONFIG
    bDescriptorType段, 描述符類型。

  3. db (HighSpeedConfigDscrEnd-HighSpeedConfigDscr) mod 256

  4. db (HighSpeedConfigDscrEnd-HighSpeedConfigDscr) / 256
    wTotalLength段,指明配置信息總長度,2字節表示。為配置描述符,接口描述符,端點描述符,設備類定義描述符,供應商自定義描述符長度的和。在這里只有配置、接口和端點描述符。

  5. db 1
    bNumInterface段, 指明該配置所支持的接口數(??),最小為1.

  6. db 1
    bConfigurationValue段, 指明該配置的配置值。例如這里值為1,在重枚舉時,主機發送Setconfiguration(x),當x=1時,就調用該配置。

  7. db 00
    iConfiguration段, 該配置的字符串索引值,沒有時為0.

  8. db 10000000b
    bmAttributes段, 指明該配置的特性,8位。
    b0~b4,保留,必須為0.
    b5:遠程喚醒選擇。=1,支持遠程喚醒;=0,不支持遠程喚醒。
    b6:是否總線電源選擇。如果該USB設備外加了電源,=1,使用總線電源,=0,使用自供的電源。
    b7:必須為1. 在主機設備請求case SC_GET_STATUS,case GS_DEVICE中獲得該信息。

  9. 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文件中並沒有對接口描述符的判斷。

  1. db DSCR_INTRFC_LEN
    bLength段, 描述符長度。

  2. db DSCR_INTRFC
    bDescriptorType段, 描述符類型。

  3. db 0
    bInterfaceNumber段, 指明該接口的接口號。

  4. db 0
    bAlternateSetting段, 指明接口的可替換設置值。

  5. db 6
    bNumberEndpoints段, 指明接口所使用的斷點數,不包括端點0.

  6. db 0ffH
    bInterfaceClass段, 指明接口所使用的設備類。

Value comment
0 保留
1~FEH 表明該接口屬於某個明確的USB設備類
FFH 廠家自定義的設備類
  1. db 00H
    bInterfaceSubClass段, 該接口所屬的USB設備子類。

  2. db 00H
    bInterfaceProtocol段, 該接口所使用的設備類協議。

  3. 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的一部分返回給主機。

  1. db DSCR_ENDPNT_LEN
    bLength段, 該描述符長度,單位字節。

  2. db DSCR_ENDPNT
    bDescriptorType段, 該描述符類型。

  3. db 02H
    bEndpointAddress段, 指明端點的端點號及傳輸方向。
    b0~b3:該端點的端點號。如0001端點1,0010端點2;
    b4~b6::保留,必須為0
    b7:端點傳輸方向。1-IN傳輸;0-OUT傳輸

  4. 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

  1. db 00H
    wMaxpacketSize段(LSB), 指明端點所支持的最大數據包長度,共16位。
    b0~b10:端點所支持的最大數據包長度。
    b11~b12:當該端點為高速中斷端點或同步端點時,這兩位指出每小幀中最多傳輸的事務數。
key comment
00 每小幀1次(默認),
01 每小幀2次(附加一次)
10 每小幀3次(附加2次)
11 保留。

b13~b15保留,必須為0

  1. db 02H
    wMaxpacketSize段(MSB)
    高8位。
    數據包大小為:0000 0010 0000 0000,取0~10位,還是0200,512字節。

  2. 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)
  1. db StringDscr1End-StringDscr1
    bLength段, 描述符長度

  2. db DSCR_STRING
    bDescriptorType段, 描述符類型。

  3. Unicode編碼的字符串。

9. 學習資料

【關於USB】這家伙總結得不孬啊,我剛看一會兒就學了不少東西。
摘自: http://www.daxiamcu.com/bibis/moredata30_1384202.shtml

  1. 元器件
    CYPRESS 68013A:支持USB 2.0協議,帶增強型8051單片機,時鍾頻率48Mhz。支持串口通訊。

  2. 文檔

文檔名 說明
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. 開發環境

  1. Keil C 7.0編譯器
  2. C++ Builder 6.0
  3. VC++ 6.0
  4. EEPROM燒寫器
  5. 68013A的開發包(含CYPRESS CONSOLE、CYUSB.SYS、例程等)
  6. BUS HOUND 5.0

11. 開發流程

11.1 硬件程序編寫

  1. 根據CYPRESS的示例程序建立工程框架,一般由FW.C PERIPH.C和定義寄存器的幾個頭文件組成。如下圖:
  2. FW.C負責了設備連接、重枚舉、設備初始化等過程
  3. PERIPH.C負責響應各種中斷事件。
  4. dscr.a51文件定義了USB設備握手時需要的各種描述符
  5. FX2REGS.H定義了USB中所有的寄存器
  6. FX2.H主要定義了各種二級中斷向量和描述符的數據結構
  7. 編譯后的二進制代碼和工程同名,擴展名為HEX。
  8. 相應的頭文件和類庫在KEIL C的lib和inc文件夾內,需在項目設置中設置路徑。

11.2 硬件程序燒錄

  1. EFROM
    因為本產品要求將二進制代碼和硬件PID/VID燒錄在EEPROM,而不是使用CYPRESS推薦的在線下載方式,所以外部采用了8K 的EEPROM。上電后68013A會將EEPROM中的數據和程序加載到RAM中運行。

  2. HEX2BIN.EXE
    HEX文件只是68013A上8051的程序代碼,還要加上PID/VID等信息才能正確運行,CYPRESS在開發包中提供了HEX2BIN.EXE這個工具,可以根據HEX生成完備的IIC文件,將此文件燒錄到EEPROM上即可。

  3. 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
  1. IIC文件燒錄到EEPROM上
    將生成的IIC文件用燒寫器燒錄到EEPROM上,本項目使用的是深圳思泰佳電子公司的NSP通用燒寫器,此燒寫器不支持IIC類型,選擇BIN類型可替代。

11.3 驅動的識別

  1. 將EEPROM連到68013A上后,接上USB線,上電。計算機提示找到新硬件,要求安裝驅動。
  2. CYPRESS針對68013A提供了全新的驅動程序CYUSB.SYS。這個驅動使用了新的API,所以上位機的編寫上和舊的方式完全不同。底層的IOCTL控制字的定義也完全不同,詳見CYAPI.PDF和CYUSB.PDF。
  3. 安裝驅動之前,必須先根據VID/PID正確編輯CYUSB.INF文件,在文件中添加自己的PID/VID代碼和設備描述,連接設備時, 將根據硬件上的PID/VID查找INF文件中對應的驅動,如果找不到,在設備管理器中將顯示“68013 EEPROM MISSING”的字樣。
  4. 詳細的INF配置方法參考CYUSB.PDF PART1/PART2/PART3。這里不在贅述。
  5. 安裝驅動時候找到修改好的CYUSB.INF文件,驅動將被正確安裝,此時設備可以正常使用。

11.4 測試過程

  1. 被正確識別的設備可以在CYPRESS CONSOLE上看到設備信息。如圖:
  2. CYPRESS CONSOLE的具體使用方法請參考CyConsole.chm。
  3. 要注意的是,除EP0/EP1外,當其他端點Max Pkt Size大小為64字節時,表示工作在USB 1.1模式,有可能是軟件的原因, 也有可能是外圍上拉電阻的問題。開發中要特別注意。

11.5 推薦開發流程

  1. 看本介紹USB 2.0協議的書,對USB 2.0協議有所了解。推薦《USB 2.0原理與工程開發》
  2. 看CYUSB.PDF文檔。了解驅動安裝方法。
  3. 看KEIL C51的書籍,熟悉C51的編程方法,熟悉KEIL C編程環境。
  4. 看CYPRESS提供的例程,了解68013A編程框架。推薦《EZ-USB 2100系列單片機原理、編程及應用》(基本框架類似,部 分寄存器定義不同)。 5.5 對照USB 2.0協議,編寫dscr.a51文件,配置各種描述符。
  5. 結合FX2 TechRefManual.pdf,研讀FW.C、PERIPH.C、FX2REGS.H、FX2.H,了解寄存器的定義。
  6. 根據系統需求編寫相應代碼,有開發板時,根據開發版上的LED來測試程序正確與否。
  7. 根據CYAPI.PDF CYUSB.PDF編寫上位機通訊程序。 同步讀取數據方法 XferData(); 異步讀取數據方法 BeginDataXfer()/WaitForXfer()/FinishDataXfer();
  8. 調試程序。
  9. 編寫其他8051上的程序,並繼續調試。

11.6 發布時應提供的文件

  1. CYUSB.SYS
  2. CYUSB.INF
  3. XXX.IIC

12. 重點講解

12.1 如何理解CYPRESS 68013A程序框架

CYPRESS提供了非常好的程序框架,免去了用戶自己編寫一些通用性比較強、模式化的程序(如果不提供,很少有人能寫出如 此高效,結構緊湊的程序,實際上此框架和68013A內部結構關系密切,一般人也沒有足夠的內部資料也不可能寫出來)。在框 架的基礎上,用戶只需在相應的地方寫相應的代碼即可完成USB工作。

一般來說框架可以分成3個部分。

  1. 描述符文件
    例如dscr.a51文件,里面定義了枚舉設備的時候要用的各種描述符信息,這部分用戶需要根據實際的情況自己編寫。我寫的時候發現一個最大的問題就是各種書籍協議版本不同,翻譯質量不同,同一字段的意義表述不同,容易讓人產生困惑。例如USB 1.1/2.0/2.13對設備類型的子類定義都不完全相同,所以寫的時候最好幾種文檔對比起來寫。由於USB官方網站的文檔中字段解釋過於專業化,所以對USB不是很熟悉的人比較難以理解其真正含義。所以要多參考不同的書籍,某種程度上降低了開發速度,但對第一次做USB開發的人來說,這也是值得的。

  2. 固件文件
    例如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);
  1. 功能文件
    處理各種中斷。例如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走讀這些寄存器 大約需要一到兩天時間,這點時間投入還是值得的。
在通訊過程中,打交道最多的是各種端點寄存器,掌握好這些寄存器地使用對提升開發效率是很有幫助。值得特別關注的寄存 器和配置位如下:

  1. Rwuen
  2. REVCTL
  3. EP1OUTCFG
  4. EP1INCFG
  5. EP2CFG
  6. EP4CFG
  7. EP6CFG
  8. EP8CFG
  9. EP2FIFOCFG
  10. EP4FIFOCFG
  11. EP6FIFOCFG
  12. EP8FIFOCFG
  13. FIFORESET
  14. EPIRQ
  15. EPIE
  16. EP1OUTBC
  17. APTR1H
  18. APTR1L
  19. EXTAUTODAT1
  20. AUTOPTRH2
  21. AUTOPTRL2
  22. EXTAUTODAT2
  23. EP2BCH
  24. 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 
}
  1. APTR1H/APTR1H
    通過MSB和LSB獲取EP2FIFOBUF的高位地址和地位地址。

  2. EXTAUTODAT1
    表示APTR1H/APTR1H指向的數據。

  3. AUTOPTRH2/AUTOPTRL2
    通過MSB和LSB獲取EP6FIFOBUF的高位地址和地位地址。

  4. 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寫上位機程序

  1. 首先確定使用7.4中的第幾種方法,添加相應的頭文件和庫文件。
  2. 連接USB設備,確保驅動已經被正確加載。
  3. 編寫收發數據線程。通過開發板上的LED或CYPRESS CONSOLE或BUS HOUND分析收發正確與否。

12.7 U盤如何正確加載驅動

在WINDOWS 2000/XP上U盤使用的PID/VID應該直接能加載操作系統默認的海量存儲器的驅動程序。為了使用正確的PID/VID,可以通過以下途徑:

  1. 找一個現有品牌的U盤,看看他的PID/VID是何值。
  2. 在注冊表中查找海量存儲器信息。

12.8 其他問題

  1. 編寫上位機的時候要注意添加異常處理。
  2. 調試上位機的時候,USB外設應正確連接。
  3. 8051其他模塊的編寫請參考相應書籍


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM