USB2.0开发之bulkloop固件工程代码注释

bulkloop固件工程介绍

  固件程序是指运行在芯片内置内核中的程序。固件程序的主要功能有功能设备的各硬件单元初始化、重新配置功能设备、中断处理、定义寄存器以及通信控制等等。

  Cypress公司提供的固件框架已经完成和实现了对于设备的重列举、初始化和电源管理等功能。用户可以在提供的固件构架基础上添加或修改相应的程序代码,这样大大缩短了设计周期,提高了设计效率。

  本文附上bulkloop例程核心代码及注释帮助大家对固件代码进行快速理解。

 

 

   上图是Cpress公司提供的bulkloop例程的工程文件图。

  fw.c包含固件程序主框架,实现USB总线协议,提供用户函数接口。

  bulkloop.c是用户程序文件,可以根据自己的需求编写不同功能。

  dscr.a51用来修改USB的描述符。

  EZUSB.LIB是库代码文件。

  USBJmpTb.OBJ定义USB中断跳转表。

  在用户开发过程中主要修改的是fw.c、bulkloop.c和dscr.c三个文件。本文对这三个文件进行介绍。

 

Dscr.a51代码部分

 

Dscr.a51文件主要配置了设备描述符、设备限定描述符、高速配置描述符、全速配置描述符、接口描述符和端点描述符等。

以下为几个主要描述符的配置和注释:
DeviceDscr:
db DSCR_DEVICE_LEN ;; Descriptor length                                               指明整个设备描述符的长度,单位字节。18
db DSCR_DEVICE ;; Decriptor type                                                            描述符类型值。1
dw 0002H ;; Specification Version (BCD)                                                    表明该USB设备所遵循的USB协议版本,用bcd码表示,2字节。例如2.0版本,值为0200H,用bcd码表示,低字节在前,高字节在后,表示为0002H;
                                                                                                                   ;;同理,1.1版本,则表示为1001H。
db 00H ;; Device class                                                                                指明USB设备所属的设备类。=0,表示USB各接口相互独立工作,分属不通的设备类,具体信息在接口描述符中说明;
db 00H ;; Device sub-class                                                                         指明USB设备所述的设备子类。其值依赖bDeviceClass。=0,此时bDeviceClass必须首先为0;
db 00H ;; Device sub-sub-class                                                                  指明USB所使用的设备类协议。其值依赖bDeviceClass和bDeviceSubClass。=0,表示该设备不使用任何设备类协议;
db 64 ;; Maximum packet size                                                                     指明该USB设备端点0控制传输所支持的最大数据包长度,单位字节。
dw 0B404H ;; Vendor ID                                                                              VID
dw 0410H ;; Product ID (Sample Device)                                                    PID
dw 0000H ;; Product version ID                                                                  指明USB设备版本号。
db 1 ;; Manufacturer string index                                                               厂商信息字符串索引值,没有时为0。这里为1,即下面的“Cypress”字符串。
db 2 ;; Product string index                                                                        产品信息字符串索引值,没有时为0。后面的“EZ-USB”字符串。
db 0 ;; Serial number string index                                                              USB设备序列号信息字符串索引值,没有时为0。
db 1 ;; Number of configurations                                                               指明USB设备所支持的配置数。如果USB设备支持两种传输速率,则该字段指出的是该速率下的配置数,而不是两种速率下的配置数和。

 

DeviceQualDscr:
db DSCR_DEVQUAL_LEN ;; Descriptor length                                         整个设备限定描述符的长度,单位字节,共10个字节。
db DSCR_DEVQUAL ;; Decriptor type                                                      指出该描述符类型。06H->设备限定描述符。
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                                                                  端点0控制传输所支持的最大数据包长度,单位字节。
db 1 ;; Number of configurations                                                             另一速率所支持的配置数。
db 0 ;; Reserved                                                                                     保留,必须为0。

 

HighSpeedConfigDscr:
db DSCR_CONFIG_LEN ;; Descriptor length                                            描述符长度,9字节。
db DSCR_CONFIG ;; Descriptor type 配置描述符。
db (HighSpeedConfigDscrEnd-HighSpeedConfigDscr) mod 256 ;; Total Length (LSB)             指明配置信息总长度,2字节表示。为配置描述符,接口描述符,端点描述符,设备类定义描述符,供应商自定义描述符长度的和。
;;在这里只有配置、接口和端点描述符。低有效位
db (HighSpeedConfigDscrEnd-HighSpeedConfigDscr) / 256 ;; Total Length (MSB)                   高有效位。
db 1 ;; Number of interfaces                                                                                                       指明该配置所支持的接口数,最小为1。
db 1 ;; Configuration number                                                                                                     指明该配置的配置值。例如这里值为1,在重枚举时,主机发送Setconfiguration(x),当x=1时,就调用该配置。
db 0 ;; Configuration string                                                                                                        该配置的字符串索引值,没有时为0。
db 10000000b ;; Attributes (b7 - buspwr, b6 - selfpwr, b5 - rwu)                                                指明该配置的特性,8位。b0~b4,保留,必须为0;b5:远程唤醒选择。=1,支持远程唤醒;=0,不支持远程唤醒;
                                                                                                                                                  ;;b6:是否总线电源选择。如果该USB设备外加了电源,=1,使用总线电源;=0,使用自供的电源;b7:必须为1。
db 50 ;; Power requirement (div 2 ma)                                                                                      总线供电时,该USB设备可获得的最大电流。单位2mA,所以最大值为250。如果该电流得不到满足,USB将不能使用这个配置。

 

;; 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 4 ;; Number of end points                                                                                                       指明接口所使用的端点数,不包括端点0。
db 0ffH ;; Interface class                                                                                                              指明接口所使用的设备类。=0,保留;=1~FEH,表明该接口属于某个明确的USB设备类;=FFH,厂家自定义的设备类。
db 00H ;; Interface sub class
db 00H ;; Interface sub sub class
db 0 ;; Interface descriptor string index

;; Endpoint Descriptor
db DSCR_ENDPNT_LEN ;; Descriptor length
db DSCR_ENDPNT ;; Descriptor type
db 02H ;; Endpoint number, and direction                                                                                   指明端点的端点号及传输方向。b0~b3:该端点的端点号。如0001端点1,0010端点2;b7:端点传输方向。1-IN传输;0-OUT传输。
db ET_BULK ;; Endpoint type                                                                                                     指明端点的一些特性。b0~b1:端点的传输类型。00-控制传输,01-同步传输,10-块传输,11-中断传输。
;;b2~b3:                                                                                                                                    当该端点为同步端点时,这两位指出同步类型。00-非同步,01-异步,10-自适应,11-同步。
;;b4~b5:                                                                                                                                    端点用法类型。00-数据端点,01-显示反馈端点,10-隐式反馈端点,11-保留。b6~b7:保留,必须为0。
db 00H ;; Maximun packet size (LSB)
db 02H ;; Max packect size (MSB)
db 00H ;; Polling interval

 

fw.c代码部分

 

Sleep = FALSE; // Disable sleep mode                              初始化用户变量 休眠使能--禁止
Rwuen = FALSE; // Disable remote wakeup                       远程唤醒--禁止
Selfpwr = FALSE; // Disable self powered
GotSUD = FALSE; // Clear "Got setup data" flag SetUp     令牌包到来标志

// Initialize user device
TD_Init();

 

// The following section of code is used to relocate the descriptor table.                          下面的代码用于重新定位描述符表。
// The frameworks uses SUDPTRH and SUDPTRL to automate the SETUP requests      框架使用SUDPTRH和SUDPTRL来自动化设置请求
// for descriptors. These registers only work with memory locations                                 描述符。这些寄存器只对内存位置有效
// in the EZ-USB internal RAM. Therefore, if the descriptors are located                          在EZ-USB内部RAM中。因此,如果描述符位于
// in external RAM, they must be copied to in internal RAM.                                             在外部RAM中,它们必须被复制到内部RAM中。
// The descriptor table is relocated by the frameworks ONLY if it is found                        描述符表只有在找到时才会被框架重新定位
// to be located in external memory.
pDeviceDscr = (WORD)&DeviceDscr;
pDeviceQualDscr = (WORD)&DeviceQualDscr;
pHighSpeedConfigDscr = (WORD)&HighSpeedConfigDscr;
pFullSpeedConfigDscr = (WORD)&FullSpeedConfigDscr;
pStringDscr = (WORD)&StringDscr;

 

// Is the descriptor table in external RAM (> 16Kbytes)? If yes,                                        这段代码用来判断描述符表首址,如果是,则移除,然后将描述符表移到
// then relocate.                                                                                                               内部RAM区.描述符表位于外部RAM时,USB是不工作的。
// 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.
if ((WORD)&DeviceDscr & 0xC000)                                                               //外部RAM首地址
{
// first, relocate the descriptors                                                                       判断发现描述符表首址位于外部RAM后,就将外部RAM的描述符移到内部RAM。
IntDescrAddr = INTERNAL_DSCR_ADDR; //IntDescrAddr                              保存内部RAM首址0X80
ExtDescrAddr = (WORD)&DeviceDscr; //ExtDescrAddr                                  保存获得的当前描述符表外部RAM的首址
DevDescrLen = (WORD)&UserDscr - (WORD)&DeviceDscr + 2; //                整个描述表的长度,从DeviceDscr段到UserDscr段,在dscrptr中有定义。
for (i = 0; i < DevDescrLen; i++) //                                                                  将从ExtDescrAddr地址开始的外部RAM中的数据逐个copy到从IntDescrAddr地址开始的内部RAM区。
*((BYTE xdata *)IntDescrAddr+i) = *((BYTE xdata *)ExtDescrAddr+i);

 

// 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_ENABLE_RSMIRQ(); // Wake-up interrupt                                                                                        使能远程唤醒中断

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

 

USBIE |= bmSUDAV | bmSUTOK | bmSUSP | bmURES | bmHSGRANT; // Enable selected interrupts            使能所选择中断
EA = 1; // Enable 8051 interrupts 开8051中断

while(TRUE) // Main Loop                                                                                          主循环
{
// Poll User Device
TD_Poll();                                                                                                           

// Check for pending SETUP
if(GotSUD) //                                                                                                             等待SETUP令牌数据的到来
{
SetupCommand(); // Implement setup command                                                      处理SETUP事务
GotSUD = FALSE; //                                                                                                 Clear SETUP flag 清Setup标志
}

// 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.
if (Sleep)
{
if(TD_Suspend())
{
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.
}
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();
}
}

}

 

bulkloop.c代码部分

void TD_Init(void)             // Called once at startup
{
   // set the CPU clock to 48MHz
   CPUCS = ((CPUCS & ~bmCLKSPD) | bmCLKSPD1) ;    //F:CLKSPD1=1且CLKSPD0=0 意思是48MHz

   // set the slave FIFO interface to 48MHz      //F:时钟来源定为外部,内部FIFO\GPIF时钟设为48MHz,IFCLK输出端口为三态,IFCLK极性不翻转,同步方式,PE012为端口,ABD端口为端口模式
   IFCONFIG |= 0x40;
  EP1OUTCFG = 0xA0;
   EP1INCFG = 0xA0;
   SYNCDELAY;                    // see TRM section 15.14
   EP2CFG = 0xA2;                            //F:1010 0010意思是:有效,OUT,Bulk,512,0,Double.
   SYNCDELAY;                    
   EP4CFG = 0xA0;                            //F:1010 0000意思是:有效,OUT,Bulk,512,0,00(4和8端点的末尾两位只能是0,在2和6都是Double情况下,意味着Double).
   SYNCDELAY;                    
   EP6CFG = 0xE2;                            //F:1110 0010意思是:有效,IN,Bulk,512,0,Double.
   SYNCDELAY;                    
   EP8CFG = 0xE0;                            //F:1110 0000意思是:有效,OUT,Bulk,512,0,00.
// out endpoints do not come up armed        //F:输出端点一开始没有被arm.
  
  // since the defaults are double buffered we must write dummy byte counts twice   //F:因为端点默认是双倍缓冲(512*2),我们必须用无用数据写两次字节计数,用来arm输出端点.
  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;    

  // enable dual autopointer feature        //F:使能自动指针
  AUTOPTRSETUP |= 0x01;
}


void TD_Poll(void)              // Called repeatedly while the device is idle //F:重复调用
{
  WORD i;
  WORD count;

  if(!(EP2468STAT & bmEP2EMPTY))                      //F:如果EP2的buff不空.EP2468STAT中的各个位其实就是EPxCS中的F和E位,标识满\空.
  { // check EP2 EMPTY(busy) bit in EP2468STAT (SFR), core set's this bit when FIFO is empty
     if(!(EP2468STAT & bmEP6FULL))                    //F:如果EP6的buff不满.
     {  // check EP6 FULL(busy) bit in EP2468STAT (SFR), core set's this bit when FIFO is full
        APTR1H = MSB( &EP2FIFOBUF );                  //F:自动指针1指向EP2的buffer
        APTR1L = LSB( &EP2FIFOBUF );

        AUTOPTRH2 = MSB( &EP6FIFOBUF );              //F:自动指针2指向EP6的buffer
        AUTOPTRL2 = LSB( &EP6FIFOBUF );

        count = (EP2BCH << 8) + EP2BCL;              //F:计算EP2有多少字节

        // loop EP2OUT buffer data to EP6IN
        for( i = 0x0000; i < count; i++ )
        {
           // setup to transfer EP2OUT buffer to EP6IN buffer using AUTOPOINTER(s) // F:利用自动指针进行EP2和EP6之间的数据转移
           EXTAUTODAT2 = EXTAUTODAT1;                                            // F:自动指针1指向的数据到自动指针2指向的空间
        }
        EP6BCH = EP2BCH;                                                       //F:拷贝数据长度到EP6的计数,准备接下来的IN操作
        SYNCDELAY;  
        EP6BCL = EP2BCL;        // arm EP6IN
        SYNCDELAY;                    
        EP2BCL = 0x80;          // re(arm) EP2OUT
     }
  }

  if(!(EP2468STAT & bmEP4EMPTY))
  { // check EP4 EMPTY(busy) bit in EP2468STAT (SFR), core set's this bit when FIFO is empty
     if(!(EP2468STAT & bmEP8FULL))
     {  // check EP8 FULL(busy) bit in EP2468STAT (SFR), core set's this bit when FIFO is full
        APTR1H = MSB( &EP4FIFOBUF );
        APTR1L = LSB( &EP4FIFOBUF );

        AUTOPTRH2 = MSB( &EP8FIFOBUF );
        AUTOPTRL2 = LSB( &EP8FIFOBUF );

        count = (EP4BCH << 8) + EP4BCL;

        // loop EP4OUT buffer data to EP8IN
        for( i = 0x0000; i < count; i++ )
        {
           // setup to transfer EP4OUT buffer to EP8IN buffer using AUTOPOINTER(s)
           EXTAUTODAT2 = EXTAUTODAT1;
        }
        EP8BCH = EP4BCH;  
        SYNCDELAY;  
        EP8BCL = EP4BCL;        // arm EP8IN
        SYNCDELAY;                    
        EP4BCL = 0x80;          // re(arm) EP4OUT
     }
  }
}
posted @ 2019-10-16 17:25  细节怪  阅读(769)  评论(0编辑  收藏  举报