• CY7C68031A固件程序FW.C详解(转载)


     

     

    //////以下是Cypress公司的官方程序,我不做改动,原英文注释保留,只增加注释
    //
    简单语句就不说了
    ////    //???//
    是我不懂得的地方,希望高手补充
    ////    //###//
    是以后开发可能需要改动的地方//
    ////这是在Keil UV4里编辑的,没有其它格式字符,可以直接编译

     

    //-----------------------------------------------------------------------------
    //   File:      fw.c
    //   Contents: Firmware frameworks task dispatcher and device request parser
    //
    // $Archive: /USB/Examples/FX2LP/bulkext/fw.c $
    // $Date: 3/23/05 2:53p $
    // $Revision: 8 $
    //
    //
    //-----------------------------------------------------------------------------
    // Copyright 2003, Cypress Semiconductor Corporation
    //-----------------------------------------------------------------------------


    #include "fx2.h" 
    ////fx2.h 
    定义EZUSB的宏、数据类型等的头文件 
              
    #include "fx2regs.h"
    ////fx2regs.h 
    定义EZUSB寄存器定义的头文件

    #include "syncdly.h"            // SYNCDELAY macro 
    ////syncdly.h 
    同步延时宏定义

    //-----------------------------------------------------------------------------
    // Constants
    //-----------------------------------------------------------------------------
    #define DELAY_COUNT   0x9248*8L // Delay for 8 sec at 24Mhz, 4 sec at 48
    #define _IFREQ 48000            // IFCLK constant for Synchronization Delay
    #define _CFREQ 48000            // CLKOUT constant for Synchronization Delay
    ////
    以上设置时钟频率为48MHZ

    //-----------------------------------------------------------------------------
    // Random Macros
    //-----------------------------------------------------------------------------
    #define   min(a,b) (((a)<(b))?(a):(b))
    #define   max(a,b) (((a)>(b))?(a):(b))

    //-----------------------------------------------------------------------------
    // Global Variables
    ////
    全局变量
    //-----------------------------------------------------------------------------
    volatile BOOL   GotSUD;
    ////GotSUD
    是令牌包标志,准确的说是令牌阶段数据到来,什么是令牌包?
    ////
    首先,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各种状态并处理:

    BOOL      Rwuen;
    ////Rwuen 
    远程唤醒标志

    BOOL      Selfpwr;
    ////Selfpwr 
    禁止或使能自供电模式

    volatile BOOL   Sleep;                  // Sleep mode enable flag
    ////Sleep   //
    禁止或使能休眠模式


    ////
    以下是定向USB描述符
    WORD   pDeviceDscr;   // Pointer to Device Descriptor; Descriptors may be moved
    ////
    设备描述符指针

    WORD   pDeviceQualDscr;
    ////
    设备限定描述符指针

    WORD   pHighSpeedConfigDscr;
    ////
    高速配置描述符指针

    WORD   pFullSpeedConfigDscr;
    ////
    全速配置描述符指针
       
    WORD   pConfigDscr;
    ////
    配置描述符指针

    WORD   pOtherConfigDscr;
    ////
    其他速率配置描述指针
       
    WORD   pStringDscr; 
    ////
    字符串描述符指针

    //-----------------------------------------------------------------------------
    // Prototypes
    //-----------------------------------------------------------------------------
    ////
    硬件程序的函数入口。主要有以下这些方法:
    ////
    功能在函数中说明

    void SetupCommand(void);
    ////void SetupCommand(void);     //
    握手命令处理
    void TD_Init(void);
    ////void TD_Init(void);   //
    初始化,完成配置,启动时调用一次
    void TD_Poll(void);
    ////void TD_Poll(void);   //
    用户处理程序,循环调用

    ////void IO_Init(void);   //8051 IO初始化
    ////void REG_Init(void); //8051
    寄存器初始化

    BOOL TD_Suspend(void);
    ////BOOL TD_Suspend(void);    //
    挂起处理
    BOOL TD_Resume(void);
    ////BOOL TD_Resume(void);     //
    唤醒处理

    ////以下为各种描述符的获取和设置函数,重枚举时自动调用
    ////
    功能在函数中说明

    BOOL DR_GetDescriptor(void);
    ////DR_GetDescriptor(void)
    获得描述符
    BOOL DR_SetConfiguration(void);
    //// BOOL DR_SetConfiguration(void)
    设置配置子函数
    BOOL DR_GetConfiguration(void);
    ////BOOL DR_GetConfiguration(void) 
    获取配置子函数
    BOOL DR_SetInterface(void);
    //// DR_SetInterface(void)
    配置接口子函数 
    BOOL DR_GetInterface(void);
    //// DR_GetInterface(); 
    获取接口子函数 
    BOOL DR_GetStatus(void);
    /////DR_GetStatus(void) 
    获得状态
    BOOL DR_ClearFeature(void);
    ////DR_ClearFeature(void) 
    清除特性
    BOOL DR_SetFeature(void);
    ////DR_SetFeature(void)
    设置特性
    BOOL DR_VendorCmnd(void);
    //// DR_VendorCmnd(void) 
    自定义的用户请求

     

    // this table is used by the epcs macro      
    ////   //???//
    const char code EPCS_Offset_Lookup_Table[] =
    {
       0,    // EP1OUT
       1,    // EP1IN
       2,    // EP2OUT
       2,    // EP2IN
       3,    // EP4OUT
       3,    // EP4IN
       4,    // EP6OUT
       4,    // EP6IN
       5,    // EP8OUT
       5,    // EP8IN
    };

    // macro for generating the address of an endpoint's control and status register (EPnCS)   
    ////   //???//
    #define epcs(EP) (EPCS_Offset_Lookup_Table[(EP & 0x7E) | (EP > 128)] + 0xE6A1)

    //-----------------------------------------------------------------------------
    // Code
    //-----------------------------------------------------------------------------

    // Task dispatcher
    void main(void)
    {
       DWORD   i;
       WORD   offset;
       DWORD   DevDescrLen;
       DWORD   j=0;
       WORD   IntDescrAddr;
       WORD   ExtDescrAddr;

       // Initialize Global States
       ////
    初始化全局状态变量
       
       Sleep = FALSE;               // Disable sleep mode
       ////Sleep = FALSE; //
    初始化用户变量 休眠使能--禁止
       Rwuen = FALSE;               // Disable remote wakeup
       ////Rwuen = FALSE;//
    远程唤醒--禁止
       Selfpwr = FALSE;            // Disable self powered
       ////Selfpwr = FALSE; //
    禁止自供电模式
       GotSUD = FALSE;               // Clear "Got setup data" flag
       ////GotSUD = FALSE; //
    清除SetUp令牌包到来标志

       // Initialize user device
       TD_Init();
       ////TD_Init(); 
    初始化USB

       // The following section of code is used to relocate the descriptor table. 
       // The frameworks uses SUDPTRH and SUDPTRL to automate the SETUP requests
       // for descriptors. These registers only work with memory locations
       // in the EZ-USB internal RAM. Therefore, if the descriptors are located
       // in external RAM, they must be copied to in internal RAM. 
       // The descriptor table is relocated by the frameworks ONLY if it is found 
       // to be located in external memory.

       //取得定向USB描述符的指针(地址)
       //
    这段代码用来获取USB的各个描述符在68013内存中的地址,
       //
    准确说是在RAM中的地址,在dscrpt.a51文件中有定义,
       //
    所有的描述符组成了整个的描述符表,后面会用到。
       pDeviceDscr = (WORD)&DeviceDscr;
       ////pDeviceDscr = (WORD)&DeviceDscr; //
    设备描述符
       pDeviceQualDscr = (WORD)&DeviceQualDscr;
       //// pDeviceQualDscr = (WORD)&DeviceQualDscr; //
    设备限定描述符 
       pHighSpeedConfigDscr = (WORD)&HighSpeedConfigDscr;
       //// pHighSpeedConfigDscr = (WORD)&HighSpeedConfigDscr; //
    高速配置描述符
       pFullSpeedConfigDscr = (WORD)&FullSpeedConfigDscr;
       //// pFullSpeedConfigDscr = (WORD)&FullSpeedConfigDscr; //
    全速配置描述符
       pStringDscr = (WORD)&StringDscr;
       //// pStringDscr = (WORD)&StringDscr;   //
    字符串描述符

       // 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.
       ////
    意思是说,这段代码用来判断描述符表首址,
    ////
    也就是前面的DeviceDscrDeviceQualDscr等是否位于68013的外部RAM区,
    ////
    如果是,则移除,然后将描述符表移到内部RAM区,为什么要移到内部RAM区,
    ////
    因为当描述符表位于外部RAM时,USB是不工作的。那么如何判断描述符地址
    ////
    是否超出内部RAM的地址呢?首先,&DeviceDscr取得整个描述符表的首地址
    ////
    (它也是DeviceDscr设备描述的首址),然后和0XC000相与,为什么要和0XC000相与?
    ////
    这就牵涉到68013 FX2LP(注意是LP)的内部结构图:
    ////
    见数据单
    ////
    上图针对的是128pinFX2LP,如果是56100pin的,那么没有外部RAM,
    ////
    只有内部RAM。可以看到,FX2LP内部RAM0000-FFFF,其他为外部RAM
    ////
    而内部RAM中,只有从0000-3FFF和从E000-FFFF的区域可用,其他为系统保留。
    ////
    0000-3FFF16K bytes的内部RAM空间,叫做主RAM,对56100128pin来说,
    ////
    都可以同时作为程序或数据存储器(对128pin来说,EA=0)。
    ////
    再看&DeviceDscr & 0xC000的结果,要为的话,显然,
    ////
    必须&DeviceDscr>=0X4000,也就是说判断的是描述表首址&DeviceDsc是否大于3FFF
    ////
    刚好是主RAM区的大小,这就是为什么要用&DeviceDscr  0xC000相与就来
    ////
    判断实现了描述符表首地址的原因了(外部 or 内部 ram?)
        if ((WORD)&DeviceDscr & 0xC000)
       {
          // first, relocate the descriptors
          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区。
          // update all of the descriptor pointers
       //// 
    更新描述符指针
       ////
    完毕后更新描述符指针,指向内部RAM区,通过原指针减去一个偏移量得到。
          pDeviceDscr = IntDescrAddr;
          offset = (WORD)&DeviceDscr - INTERNAL_DSCR_ADDR;
          pDeviceQualDscr -= offset;
          pConfigDscr -= offset;
          pOtherConfigDscr -= offset;
          pHighSpeedConfigDscr -= offset;
          pFullSpeedConfigDscr -= offset;
          pStringDscr -= offset;
       }

       EZUSB_IRQ_ENABLE();            // Enable USB interrupt (INT2)
       //// EZUSB
    中断使能
       ////EZUSB_IRQ_ENABLE();
    预定义是EZUSB=1,ezusbEIE寄存器的第0位,EIE.0=1,使能USB中断;

       EZUSB_ENABLE_RSMIRQ();            // Wake-up interrupt
       ////
    使能远程唤醒中断
       ////EZUSB_ENABLE_RSMIRQ();EICON |= 0x20
    EICON.5=1,使能远程唤醒中断;
      


       INTSETUP |= (bmAV2EN | bmAV4EN);     // Enable INT 2 & 4 autovectoring
       ////
    使能INT2,4自动向量跳转
       ////INTSETUP |= (bmAV2EN | bmAV4EN);
    使能INT2,4自动向量跳转;

       USBIE |= bmSUDAV | bmSUTOK | bmSUSP | bmURES | bmHSGRANT;   // Enable selected interrupts
       //// 
    使能所选择中断
       ////
    相关的中断意义,后面慢慢学习补充;  

       EA = 1;                  // Enable 8051 interrupts
       //// 
    8051中断

    #ifndef NO_RENUM
       // Renumerate if necessary. Do this by checking the renum bit. If it
       // is already set, there is no need to renumerate. The renum bit will
       // already be set if this firmware was loaded from an eeprom.

       if (!(USBCS & bmRENUM)) //如果RENUM位为0,则重列举
       {
           EZUSB_Discon(TRUE);   // renumerate 
    重列举
       }
    #endif

    ////这段代码即告诉USB进行重枚举,用软件设置,模拟USB断开与连接,
    ////EZUSB_Discon(TRUE)
    完成断开连接
    ////
    USBCS寄存器的DICON位置1,断开USB,同时如果RENUM位为0
    ////
    则置1;然后重新连接USB
    ////
    那么,什么是重枚举呢?
    ////
    首先,上面的USB枚举是对通用USB来说的,一般USB设备只有枚举过程,没有重枚举过程。
    ////
    也就是说其实,对EZ-USB系列来说,上面的枚举举实际包含了EZ-USB枚举和重枚举
    ////
    两个过程:
    ////
    EZ-USB来说,枚举过程就是USB上电复位到加载固件前这段过程,此时USB设备
    ////
    地址号为默认的0号,枚举完成后,驱动为cypress...eeprom..missing
    ////(FX2/FX2LP
    来说)。然后加载固件,进行重枚举,重枚举完成后,
    ////
    显示驱动为cypress...ez-usb...example或其他自定义设备。
    ////
    为什么EZ-USB有重枚举过程呢?这就是为了可以让主机在前期固件未加载前自动枚举,
    ////
    识别USB,从而可以从上位机加载固件到68013RAM中,而无需使用ROM,EEPROM,
    ////FLASH
    等内存。实现软件架构加载程序代码,随时修改固件。
    ////EZ-USB
    如何控制重枚举?
    ////
    首先,EZUSB有一个寄存器USBCS,其中第1USBCS.1Renum控制枚举重枚举。
    ////
    USB上电未加载固件代码前,Renum0,表示使用“EZUSB核心对芯片的初始配置,
    ////
    处理主机设备请求,并负责把固件下载到RAM中,这个过程就是枚举
    ////
    完成该枚举过程的设备叫做缺省USB设备(地址号0
    ////
    即驱动显示为“...missing”的设备。当固件被下载到8051 RAM后,此时Renum1
    ////
    表示使用增强8051核心处理主机设备请求,并按照固件的代码(读取所有描述符)
    ////
    重新配置USB设备,这个过程就叫做重枚举,完成重枚举后,
    ////
    显示自定义的USB设备“...example”,实际是模拟断开与连接的过程。
    ////
    用以下表表示:

    ////
    事件----处理设备请求--Renum-----8051动作
    ////
    枚举----EZUSB核心-----Renum0--8051Renum1
    ////
    重枚举--8051----------Renum1--8051重置Renum0

    ////
    在重枚举完成后,对控制端点0的设备请求可以由“EZUSB核心处理或由
    ////“
    增强8051核心处理,有Renum的值决定。芯片上电时Renum0
    ////
    “EZUSB核心处理;一旦8051开始运行,就可以设置Renum1
    ////
    增强8051核心处理,表示按照8051下载的固件代码处理。
    ////
    当然,这时也可以设置Renum0,让“EZUSB核心处理端点0的设备请求,
    ////
    而让8051完成具体的USB数据传输,这样做会大大简化8051固件代码。
    ////
    然后再看接下来的代码:
    ////
    注意,在这段代码之前,EZUSB已经枚举完成,当然,整个过程是自动的,
    ////
    我们看不见的。
    //// 
    详见<<USB设备枚举过程.doc>>


        // 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
       // CKCON = (CKCON&(~bmSTRETCH)) | FW_STRETCH_VALUE;Set stretch to 0 (after renumeration)
       //EZ-USB
    执行指令MOVX(读取外部存储器)时,在两个MOVX之间可以增加的几个时钟周期数
       //
    这对于外部的较慢的存储器或外设(例如LCD)等是很合适的,外部存储器的等待时钟可以在固件中调整
       //
    但是程序存储器不会受到影响

     

       // clear the Sleep flag.
       Sleep = FALSE; 
       ////
    sleep休眠使能标志

    // Task Dispatcher
       ////
    从下面的代码开始,才真正开始我们自己的USB事务处理:
       while(TRUE)               // //Main Loop 
    主循环,一看就是个死循环
       {
          // Poll User Device
          TD_Poll();   ////
    用户调度程序
       ////TD_Poll
    ,也就是用户调度程序,USB空闲时调用

          // Check for pending SETUP
          if(GotSUD) ////
    等待SETUP令牌数据的到来
          {
             SetupCommand();          // Implement setup command
       ////
    SetupCommand()函数中处理控制传输的内容,不管是USB标准
       ////
    设备请求还是其他非标准请求还是Vendor命令等,
       ////
    在调用SetupCommand()前首先判断了一个变量GotSUD是否为
       ////
    查询的GotSUD变量,是在bulkloop.c文件中被设置为的:
       //// Setup Data Available Interrupt Handler
       ////void ISR_Sudav(void) interrupt 0
       ////{
       ////   GotSUD = TRUE;            // Set flag
       ////   EZUSB_IRQ_CLEAR();
       ////   USBIRQ = bmSUDAV;         // Clear SUDAV IRQ
       ////}
      
       ////
    这是一个中断服务程序。这个中断是怎么触发的呢?首先弄明白以下几点:
       //// 
    1USB设备和主机之间传输一次数据,叫做一次"USB事务处理".
       //// 
    2】一个完整的控制传输,包含设置阶段,数据阶段,状态阶段:
       ////       
    例如主机要把“0X01,0X02”传送到下位机,那么设置阶段是一次USB事务处理,
       ////       
    数据阶段是两次USB事务处理等等。
       //// 
    3】一般地,每个"USB事务处理"包含三个阶段:令牌阶段、数据阶段、握手阶段。
       //// 
    4】每个阶段传输的数据,USB根据类型不同,打成不同的包,叫封包。例如, 
       ////       
    令牌阶段传送令牌封包,数据阶段传送数据封包等。
       //// 
    5SETUP寄存中的内容即是在一次“USB事务处理设置阶段传送的,
       ////       
    具体包含在数据阶段的数据封包中。
       ////       
    然后,具体调用SetupCommand()的过程为:
       ////   (a)
    USB枚举阶段,主机需要读取描述符的内容,于是发送标准请求,
       ////      
    例如case SC_GET_DESCRIPTOR读取描述符,将该请求打包,启动“USB”事务处理。
       ////   (b)
    进入“USB事务处理的设置阶段,其中,在数据阶段,USB core的端点0收到
       ////      
    封包中的数据,并将其装载到SETUPDAT寄存器(8字节),如果顺利完成,即握手ACK后,
       ////      
    USB core会发出SUDAV中断请求,于是进入void ISR_Sudav(void) interrupt 0
       ////      
    这个中断服务程序进行处理,设置GotSUD标志为
       ////      SUDAV
    ,即是SetUp ArrivedSetUp数据到达。
       ////      SUDAV
    ,在USBIRQ中定义USBIRQ.0=SUDAV。当8字节的SetUp数据从端点0发送到SETUPDAT寄存器 
       ////      
    后,USB core 将该位置1,清除中断请求。

             GotSUD = FALSE;          //// Clear SETUP flag SETUP标记
       ////GotSUD
    是令牌包标志,准确的说是令牌阶段数据到来,什么是令牌包?
       ////
    首先,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各种状态并处理?
          }

          // check for and handle suspend.
          // NOTE: Idle mode stops the processor clock. There are only two
          // ways out of idle mode, the WAKEUP pin, and detection of the USB
          // resume state on the USB bus. The timers will stop and the
          // processor will not wake up on any other interrupts.

       ////如果USB进入了休眠状态,这里SleepUSB休眠标志,同GotSUD一样,USB休眠后产生中断,
          ////
    进入void ISR_Susp(void)函数处理,置sleep标志为TRUE
       ////
    只有两种方式可以唤醒 USB 设备,一是 WAKEUP 引脚,二是 USB 总线探测到 resume 状态
          if (Sleep) //
    检查sleep休眠使能标志
          {
             if(TD_Suspend())   ////
    挂起处理
             { 
       ////TD_Suspend()
    说明:此函数是在设备进入挂起状态前调用的,开发者在其中加入适当的代码,
       ////
    对设备的工作状态进行配置,可使设备处于低功耗状态并返回真值。
       ////
    但是开发者可以改动TD_Suspend()的程序代码,使其返回为假,这样可以使FX2不进入挂起状态。

                Sleep = FALSE;     // Clear the "go to sleep" flag. Do it here to prevent any race condition between wakeup and the next sleep.
        ////
    在此清除睡眠模式标志是为了避免在唤醒下一个睡眠模式之间有任何的执行状况发生
                do
                {
                   EZUSB_Susp();         //// Place processor in idle mode.
    8051为空闲状态
                }
                while(!Rwuen && EZUSB_EXTWAKEUP());   ////
    如果唤醒
                // above. Must continue to go back into suspend if the host has disabled remote wakeup
                // *and* the wakeup was caused by the external wakeup pin.


                // 8051 activity will resume here due to USB bus or Wakeup# pin activity.
                EZUSB_Resume(); ////
    从空闲状态中恢复 // If source is the Wakeup# pin, signal the host to Resume.//从空闲状态中恢复      
                TD_Resume();
        ////TD_Resume()
    说明:当外部要求重新启动时(如,外界产生Wakeup中断或者USB总线有传输活动发生),
        ////
    设备就会通过调用此函数来对处理器进行重启,也就是TD_Suspend()函数的逆操作。此时,
        ////
    设备在正常电源下重新启动?
             }   
          }

       }
    }

    BOOL HighSpeedCapable()
    {
       ////HighSpeedCapable() 
    检测是否为高速USB芯片
       // this function determines if the chip is high-speed capable.
       // FX2 and FX2LP are high-speed capable. FX1 is not - it does
       // not have a high-speed transceiver.

       if (GPCR2 & bmFULLSPEEDONLY)
       ////GPCR2
    bmFULLSPEEDONLY FX2regs.h 的定义如下:
       ////// Feature Registers (FX2LP/FX1 only)
       ////EXTERN xdata volatile BYTE GPCR2             _AT_ 0xE50D; // Chip Features
       /////* Chip Feature Register (GPCR2) */
       ////#define bmFULLSPEEDONLY    bmBIT4

          return FALSE;
       else
          return TRUE;
    }  

    // Device request parser
    void SetupCommand(void)
    {
       //////etupCommand(void) 
    设备请求剖析器
       void   *dscr_ptr;
       switch(SETUPDAT[1]) 
       /////
    判断何种设备请求,8字节,参阅usb 协议
       ////SETUPDAT[1]
    包含请求,bRequest
       ////
    根据setupdat[1]进行分别处理
       ////Setupdat
    寄存器(只读)中的定义如下
       //// 
    SETUPDAT[0] = bmRequestTypeUSB设备接口或端点的状态
       //// 
    SETUPDAT[1] = bmRequest,请求类型, 8字节,这里是USB 11个标准请求之一
       //// 
    SETUPDAT[2:3] = wValue wValue字段,对bmRequest字段的补充       
       //// 
    SETUPDAT[4:5] = wIndex
       //// 
    SETUPDAT[6:7] = wLength
       ////
    传输我们自定义的命令时,bmRequestType不用处理;
       ////bmRequest
    为我们自定义的命令,上位机和下位机统一即可;
       ////wValue
    wIndex我们可以自由使用,共4个字节;
       ////wLength
    为接下来的数据阶段的数据长度。
       ////
    详见 <<USB设备请求.doc>>
       {
          case SC_GET_DESCRIPTOR:     ////
    命令是取得描述符表
             if(DR_GetDescriptor())    ////
    如果取得描述符表成功
                switch(SETUPDAT[3])    // //
    判断是何种描述符
                {
                   case GD_DEVICE:          ////
    是设备描述符
                      SUDPTRH = MSB(pDeviceDscr); ////
    设备描述符的高位
                      SUDPTRL = LSB(pDeviceDscr); ////
    设备描述符的低位
          ////SUDPTR
    是指向主机请求的描述符的首地址
                      break;
                   case GD_DEVICE_QUALIFIER:    ////
    是设备限定描述符 Device Qualifier
             // only retuen a device qualifier if this is a high speed
          // capable chip.
             if (HighSpeedCapable()) ////
    如果是高速设备
          {
                       SUDPTRH = MSB(pDeviceQualDscr); ////
    设备限定描述符的高位
                       SUDPTRL = LSB(pDeviceQualDscr); ////
    设备限定描述符的低位
          }
          else
          {
           EZUSB_STALL_EP0(); ////
    停滞在端点0
          }
          break;
                   case GD_CONFIGURATION:         ////
    是配置描述符
                      SUDPTRH = MSB(pConfigDscr); ////
    配置描述符的高位
                      SUDPTRL = LSB(pConfigDscr); ////
    配置描述符的低位
                      break;
                   case GD_OTHER_SPEED_CONFIGURATION: ////
    是其他速率配置描述符
                      SUDPTRH = MSB(pOtherConfigDscr); ////
    其他速率配置描述符的高位
                      SUDPTRL = LSB(pOtherConfigDscr); ////
    其他速率配置描述符的低位
                      break;
                   case GD_STRING:            ////
    是字符串描述符String
                      if(dscr_ptr = (void *)EZUSB_GetStringDscr(SETUPDAT[2]))
                      {          ////
    如果取得字符串成功
                         SUDPTRH = MSB(dscr_ptr); ////
    字符串描述符的高位
                         SUDPTRL = LSB(dscr_ptr); ////
    字符串描述符的低位
                      }
                      else        ////
    如果是其它未知类型的描述符
                         EZUSB_STALL_EP0();   ////
    中止在端点0 Stall End Point 0
                      break;
                   default:                 ////
    如果是其它未知类型的描述符 (无效描述符)ivalid request
                      EZUSB_STALL_EP0();     /////
    中止端点0 Stall End Point 0
                }
             break;
          case SC_GET_INTERFACE:                  ////
    命令是获得接口*** Get Interface
             DR_GetInterface();        ////
    获取接口子函数
             break;
          case SC_SET_INTERFACE:                  //// 
    命令是设置接口*** Set Interface
             DR_SetInterface();        ////
    设置接口子函数
             break;
          case SC_SET_CONFIGURATION:               ////
    命令是设置配置*** Set Configuration
             DR_SetConfiguration();      ////
    设置配置子函数
             break;
          case SC_GET_CONFIGURATION:               ////
    命令是获得配置 *** Get Configuration
             DR_GetConfiguration();      ////
    获取配置的子函数 
             break;
          case SC_GET_STATUS:                     //// 
    命令是获得状态 *** Get Status
             if(DR_GetStatus())        ////
    如果获得状态成功
                switch(SETUPDAT[0])       ////
    判断是何状态(设备、接口或端点状态)
                {
                   case GS_DEVICE:            //// 
    是设备状态
                      EP0BUF[0] = ((BYTE)Rwuen << 1) | (BYTE)Selfpwr;
                      EP0BUF[1] = 0;
                      EP0BCH = 0;
                      EP0BCL = 2;     ////
    长度为2
                      break;
                   case GS_INTERFACE:         //// 
    接口状态
                      EP0BUF[0] = 0;
                      EP0BUF[1] = 0;    
                      EP0BCH = 0;
                      EP0BCL = 2;
                      break;
                   case GS_ENDPOINT:         ////
    端点状态 
                      EP0BUF[0] = *(BYTE xdata *) epcs(SETUPDAT[4]) & bmEPSTALL;
                      EP0BUF[1] = 0;
                      EP0BCH = 0;
                      EP0BCL = 2;
                      break;
                   default:            ////
    未知状态(无效命令Invalid Command 
                      EZUSB_STALL_EP0();      ////
    停滞在端点0 Stall End Point 0
                }
             break;
          case SC_CLEAR_FEATURE:                  ////
    命令是清除特性*** Clear Feature
             if(DR_ClearFeature())      ////
    如果清除特性的命令成功
                switch(SETUPDAT[0])      ////
    配置是何种类型
                {
                   case FT_DEVICE:            ////
    设备 Device
                      if(SETUPDAT[2] == 1)
                         Rwuen = FALSE;       ////
    禁止远程唤醒 Disable Remote Wakeup
                      else
                         EZUSB_STALL_EP0();   // Stall End Point 0
                      break;
                   case FT_ENDPOINT:         //// 
    端点 End Point
                      if(SETUPDAT[2] == 0)
                      {
                         *(BYTE xdata *) epcs(SETUPDAT[4]) &= ~bmEPSTALL; //// //???//
                         EZUSB_RESET_DATA_TOGGLE( SETUPDAT[4] );    //// //???//
                      }
                      else
                         EZUSB_STALL_EP0();   // Stall End Point 0
                      break;
                }
             break;
          case SC_SET_FEATURE:                  //// 
    命令是配置特性 *** Set Feature
             if(DR_SetFeature())     ////
    如果配置特性的命令成功
                switch(SETUPDAT[0])     ////
    配置是何种类型
                {
                   case FT_DEVICE:            ////
    设备 Device
                      if(SETUPDAT[2] == 1)    //// //???//
                         Rwuen = TRUE;      ////
    开启远程唤醒 Enable Remote Wakeup
                      else if(SETUPDAT[2] == 2)   //// //???//
                         // Set Feature Test Mode. The core handles this request. However, it is
                         // necessary for the firmware to complete the handshake phase of the
                         // control transfer before the chip will enter test mode. It is also
                         // necessary for FX2 to be physically disconnected (D+ and D-)
                         // from the host before it will enter test mode.
          //// //???//
                         break;
                      else
                         EZUSB_STALL_EP0();   // Stall End Point 0
                      break;
                   case FT_ENDPOINT:         ////
    端点 End Point
                      *(BYTE xdata *) epcs(SETUPDAT[4]) |= bmEPSTALL;   ////   //???//
                      break;
                   default:
                      EZUSB_STALL_EP0();      // Stall End Point 0
                }
             break;
          default:                     ////
    未知命令(无效命令 *** Invalid Command 
             if(DR_VendorCmnd())   //// 
    如果是自定义命令
                EZUSB_STALL_EP0();            // Stall End Point 0
       }

       // Acknowledge handshake phase of device request
       EP0CS |= bmHSNAK;   //
    通过EP0CS |= bmHSNAK;发送ack进行确认
    }

    //唤醒中断的处理 Wake-up interrupt handler
    void resume_isr(void) interrupt WKUP_VECT
    {
       EZUSB_CLEAR_RSMIRQ();   ////   //???//
    }

  • 相关阅读:
    Unix Domain Socket(基于 Linux-2.4.0已更新)
    Eclipse常用设置及部分常用快捷键个人总结(MyEclipse通用)
    使用Docker安装ElasticSearch 以及我遇到的问题
    使用Docker安装MySQL
    es 索引自动删除
    k8s部署zk集群
    k8s-基于Canal来定义网络策略
    Markdown学习
    python ---- Linux压缩某个目录但要跳过指定文件
    WEB前端 ---- 学习第二天(表格、表单、css等)
  • 原文地址:https://www.cnblogs.com/dreamfactory/p/2585861.html
Copyright © 2020-2023  润新知