USB设备开发---- USB固件开发

       上篇介绍了基于libusb的无驱动设计,上位机没问题了,现在还留下个下位机的问题,该项目中USB下位机采用的Cypress的CY7C68013A控制芯片, 下面来仔细看如何编写下位机的固件程序(firmware)。

先看看工程的结构,如下图所示(开发工具为Keil uVision3):

其中:

(1): fw.c: 这个文件是整个USB的固件的根本,USB协议方面的通信都是在这里完成的,包括上电枚举,重枚举,唤醒以及调用用户自己的程序和控制命令等等。 一般的开发,该文件无需修改。固件的main程序就位于该文件中,概括来说,它首先进行一系列通用初始化设置,并调用void TD_Init(void)函数来实现用户的特殊初始化设置。之后,程序会一直监视是否有指令收到,并在空闲的时候反复调用TD_Poll()函数来实现我们用户的功能。代码如下:

				//-----------------------------------------------------------------------------
// File:      fw.c
// Contents:  Firmware frameworks task dispatcher and device request parser
//            source.
//
// indent 3.  NO TABS!
//
// $Revision: 18 $
// $Date: 12/04/01 5:33p $
//
//   Copyright (c) 1997 AnchorChips, Inc. All rights reserved
//

//-----------------------------------------------------------------------------
#include ".\Inc\fx2.h"
#include ".\Inc\fx2regs.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

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

  // Registers which require a synchronization delay, see section 15.14
  // FIFORESET        FIFOPINPOLAR
  // INPKTEND         OUTPKTEND
  // EPxBCH:L         REVCTL
  // GPIFTCB3         GPIFTCB2
  // GPIFTCB1         GPIFTCB0
  // EPxFIFOPFH:L     EPxAUTOINLENH:L
  // EPxFIFOCFG       EPxGPIFFLGSEL
  // PINFLAGSxx       EPxFIFOIRQ
  // EPxFIFOIE        GPIFIRQ
  // GPIFIE           GPIFADRH:L
  // UDMACRCH:L       EPxGPIFTRIG
  // GPIFTRIG
  
  // Note: The pre-REVE EPxGPIFTCH/L register are affected, as well...
  //      ...these have been replaced by GPIFTC[B3:B0] registers
  
#include ".\Inc\fx2sdly.h"             // Define _IFREQ and _CFREQ above this #include

//-----------------------------------------------------------------------------
// Global Variables
//-----------------------------------------------------------------------------
volatile BOOL   GotSUD;
BOOL      Rwuen;
BOOL      Selfpwr;
volatile BOOL   Sleep;                  // Sleep mode enable flag

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 TD_Init(void);
void TD_Poll(void);
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);

// 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
   Rwuen = FALSE;               // Disable remote wakeup
   Selfpwr = FALSE;            // Disable self powered
   GotSUD = FALSE;               // Clear "Got setup data" flag

   // Initialize user device
   TD_Init();

   // The following section of code is used to relocate the descriptor table. 
   // Since the SUDPTRH and SUDPTRL are assigned the address of the descriptor 
   // table, the descriptor table must be located in on-part memory.
   // The 4K demo tools locate all code sections in external memory.
   // 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;

   if ((WORD)&DeviceDscr & 0xe000)
   {
      IntDescrAddr = INTERNAL_DSCR_ADDR;
      ExtDescrAddr = (WORD)&DeviceDscr;
      DevDescrLen = (WORD)&UserDscr - (WORD)&DeviceDscr + 2;
      for (i = 0; i < DevDescrLen; i++)
         *((BYTE xdata *)IntDescrAddr+i) = 0xCD;
      for (i = 0; i < DevDescrLen; i++)
         *((BYTE xdata *)IntDescrAddr+i) = *((BYTE xdata *)ExtDescrAddr+i);
      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_ENABLE_RSMIRQ();            // Wake-up interrupt

   INTSETUP |= (bmAV2EN | bmAV4EN);     // Enable INT 2 & 4 autovectoring

   USBIE |= bmSUDAV | bmSUTOK | bmSUSP | bmURES | bmHSGRANT;   // Enable selected interrupts
   EA = 1;                  // Enable 8051 interrupts

#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))
   {
       EZUSB_Discon(TRUE);   // renumerate
   }
#endif

   // 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 to 0 (after renumeration)

   // clear the Sleep flag.
   Sleep = FALSE;

   // Task Dispatcher
   while(TRUE)               // Main Loop
   {
      if(GotSUD)            // Wait for SUDAV
      {
         SetupCommand();          // Implement setup command
         GotSUD = FALSE;          // Clear SUDAV flag
      }

      // Poll User Device
      // 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());
                // 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_Poll();
   }
}

// Device request parser
void SetupCommand(void)
{
   void   *dscr_ptr;

   switch(SETUPDAT[1])
   {
      case SC_GET_DESCRIPTOR:                  // *** Get Descriptor
         if(DR_GetDescriptor())
            switch(SETUPDAT[3])         
            {
               case GD_DEVICE:            // Device
                  SUDPTRH = MSB(pDeviceDscr);
                  SUDPTRL = LSB(pDeviceDscr);
                  break;
               case GD_DEVICE_QUALIFIER:            // Device Qualifier
                  SUDPTRH = MSB(pDeviceQualDscr);
                  SUDPTRL = LSB(pDeviceQualDscr);
                  break;
               case GD_CONFIGURATION:         // Configuration
                  SUDPTRH = MSB(pConfigDscr);
                  SUDPTRL = LSB(pConfigDscr);
                  break;
               case GD_OTHER_SPEED_CONFIGURATION:  // 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();   // Stall End Point 0
                  break;
               default:            // Invalid request
                  EZUSB_STALL_EP0();      // 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:            // Device
                  EP0BUF[0] = ((BYTE)Rwuen << 1) | (BYTE)Selfpwr;
                  EP0BUF[1] = 0;
                  EP0BCH = 0;
                  EP0BCL = 2;
                  break;
               case GS_INTERFACE:         // Interface
                  EP0BUF[0] = 0;
                  EP0BUF[1] = 0;
                  EP0BCH = 0;
                  EP0BCL = 2;
                  break;
               case GS_ENDPOINT:         // End Point
                  EP0BUF[0] = *(BYTE xdata *) epcs(SETUPDAT[4]) & bmEPSTALL;
                  EP0BUF[1] = 0;
                  EP0BCH = 0;
                  EP0BCL = 2;
                  break;
               default:            // Invalid Command
                  EZUSB_STALL_EP0();      // 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;
            }
         break;
      default:                     // *** Invalid Command
         if(DR_VendorCmnd())
            EZUSB_STALL_EP0();            // Stall End Point 0
   }

   // Acknowledge handshake phase of device request
   EP0CS |= bmHSNAK;
}

// Wake-up interrupt handlear
void resume_isr(void) interrupt WKUP_VECT
{
   EZUSB_CLEAR_RSMIRQ();
}




(2):dscr.a51: 这个文件是USB描述符文件,包括了设备描述符,接口描述符,端点描述符,字符串等等。如要修改从PC端看到的该USB设备的名字,可通过修改该文件中的字符串内容来实现。

(3):GPIF.c:本项目中用到了GPIF功能(Cypress定义的一种接口),(安装gpif designer软件,根据自己需要完成相应时序图,即可自动生成GPIF.c文件)。

// GPIF Program Code                          
                                              
// DO NOT EDIT ...                            
#include "fx2.h"                            
#include "fx2regs.h"                        
#include "fx2sdly.h"     // SYNCDELAY macro 
// END DO NOT EDIT                            
                                              
// DO NOT EDIT ...                     
const char xdata WaveData[128] =     
{                                      
// Wave 0 
/* LenBr */ 0x01,     0x01,     0x9A,     0x01,     0x01,     0x01,     0x01,     0x07,
/* Opcode*/ 0x00,     0x00,     0x01,     0x02,     0x02,     0x02,     0x02,     0x02,
/* Output*/ 0x03,     0x01,     0x01,     0x01,     0x01,     0x01,     0x01,     0x07,
/* LFun  */ 0x00,     0x00,     0x00,     0x00,     0x00,     0x00,     0x00,     0x3F,
// Wave 1 
/* LenBr */ 0x01,     0x02,     0x01,     0x01,     0x01,     0x01,     0x01,     0x07,
/* Opcode*/ 0x02,     0x02,     0x02,     0x02,     0x02,     0x02,     0x02,     0x02,
/* Output*/ 0x02,     0x00,     0x00,     0x00,     0x00,     0x00,     0x00,     0x07,
/* LFun  */ 0x00,     0x00,     0x00,     0x00,     0x00,     0x00,     0x00,     0x3F,
// Wave 2 
/* LenBr */ 0x01,     0x01,     0x01,     0x01,     0x01,     0x01,     0x01,     0x07,
/* Opcode*/ 0x02,     0x02,     0x02,     0x02,     0x02,     0x02,     0x02,     0x02,
/* Output*/ 0x07,     0x07,     0x07,     0x07,     0x07,     0x07,     0x07,     0x07,
/* LFun  */ 0x00,     0x00,     0x00,     0x00,     0x00,     0x00,     0x00,     0x3F,
// Wave 3 
/* LenBr */ 0x01,     0x01,     0x01,     0x01,     0x01,     0x01,     0x01,     0x07,
/* Opcode*/ 0x02,     0x02,     0x02,     0x02,     0x02,     0x02,     0x02,     0x02,
/* Output*/ 0x07,     0x07,     0x07,     0x07,     0x07,     0x07,     0x07,     0x07,
/* LFun  */ 0x00,     0x00,     0x00,     0x00,     0x00,     0x00,     0x00,     0x3F,
};                     
// END DO NOT EDIT     
                       
// DO NOT EDIT ...                     
const char xdata FlowStates[36] =   
{                                      
/* Wave 0 FlowStates */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* Wave 1 FlowStates */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* Wave 2 FlowStates */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* Wave 3 FlowStates */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};                     
// END DO NOT EDIT     
                       
// DO NOT EDIT ...                                               
const char xdata InitData[7] =                                   
{                                                                
/* Regs  */ 0xE0,0x10,0x00,0x07,0xEE,0x4E,0x00     
};                                                               
// END DO NOT EDIT                                               
                                                                 
// TO DO: You may add additional code below.

void GpifInit( void )
{
  BYTE i;
 
  // Registers which require a synchronization delay, see section 15.14
  // FIFORESET        FIFOPINPOLAR
  // INPKTEND         OUTPKTEND
  // EPxBCH:L         REVCTL
  // GPIFTCB3         GPIFTCB2
  // GPIFTCB1         GPIFTCB0
  // EPxFIFOPFH:L     EPxAUTOINLENH:L
  // EPxFIFOCFG       EPxGPIFFLGSEL
  // PINFLAGSxx       EPxFIFOIRQ
  // EPxFIFOIE        GPIFIRQ
  // GPIFIE           GPIFADRH:L
  // UDMACRCH:L       EPxGPIFTRIG
  // GPIFTRIG
  
  // Note: The pre-REVE EPxGPIFTCH/L register are affected, as well...
  //      ...these have been replaced by GPIFTC[B3:B0] registers
 
  // 8051 doesn't have access to waveform memories 'til
  // the part is in GPIF mode.
 
  IFCONFIG = 0xEE;
  // IFCLKSRC=1   , FIFOs executes on internal clk source
  // xMHz=1       , 48MHz internal clk rate
  // IFCLKOE=0    , Don't drive IFCLK pin signal at 48MHz
  // IFCLKPOL=0   , Don't invert IFCLK pin signal from internal clk
  // ASYNC=1      , master samples asynchronous
  // GSTATE=1     , Drive GPIF states out on PORTE[2:0], debug WF
  // IFCFG[1:0]=10, FX2 in GPIF master mode
 
  GPIFABORT = 0xFF;  // abort any waveforms pending
 
  GPIFREADYCFG = InitData[ 0 ];
  GPIFCTLCFG = InitData[ 1 ];
  GPIFIDLECS = InitData[ 2 ];
  GPIFIDLECTL = InitData[ 3 ];
  GPIFWFSELECT = InitData[ 5 ];
  GPIFREADYSTAT = InitData[ 6 ];
 
  // use dual autopointer feature... 
  AUTOPTRSETUP = 0x07;          // inc both pointers, 
                                // ...warning: this introduces pdata hole(s)
                                // ...at E67B (XAUTODAT1) and E67C (XAUTODAT2)
  
  // source
  AUTOPTRH1 = MSB( &WaveData );
  AUTOPTRL1 = LSB( &WaveData );
  
  // destination
  AUTOPTRH2 = 0xE4;
  AUTOPTRL2 = 0x00;
 
  // transfer
  for ( i = 0x00; i < 128; i++ )
  {
    EXTAUTODAT2 = EXTAUTODAT1;
  }
 
// Configure GPIF Address pins, output initial value,
  PORTCCFG = 0xFF;    // [7:0] as alt. func. GPIFADR[7:0]
  OEC = 0xFF;         // and as outputs
  PORTECFG |= 0x80;   // [8] as alt. func. GPIFADR[8]
  OEE |= 0x80;        // and as output
 
// ...OR... tri-state GPIFADR[8:0] pins
//  PORTCCFG = 0x00;  // [7:0] as port I/O
//  OEC = 0x00;       // and as inputs
//  PORTECFG &= 0x7F; // [8] as port I/O
//  OEE &= 0x7F;      // and as input
 
// GPIF address pins update when GPIFADRH/L written
  SYNCDELAY;                    // 
  GPIFADRH = 0x00;    // bits[7:1] always 0
  SYNCDELAY;                    // 
  GPIFADRL = 0x00;    // point to PERIPHERAL address 0x0000
 
// Configure GPIF FlowStates registers for Wave 0 of WaveData
  FLOWSTATE = FlowStates[ 0 ];
  FLOWLOGIC = FlowStates[ 1 ];
  FLOWEQ0CTL = FlowStates[ 2 ];
  FLOWEQ1CTL = FlowStates[ 3 ];
  FLOWHOLDOFF = FlowStates[ 4 ];
  FLOWSTB = FlowStates[ 5 ];
  FLOWSTBEDGE = FlowStates[ 6 ];
  FLOWSTBHPERIOD = FlowStates[ 7 ];
}


(4):periph.c: 这个就是我们用户实现功能文件,根据用户要实现的功能在此添加相应代码即可。主要函数如下:

         void TD_Init(void):这个函数只会在USB启动后调用一次。在这个函数里添加自己的初始化代码,也就是传输数据前要处理的,例如IO口配置,时钟,端点,FIFO的选择等,本例中详细代码如下:

void TD_Init(void)             // Called once at startup
{

    // set the CPU clock to 48MHz
   CPUCS = ((CPUCS & ~bmCLKSPD) | bmCLKSPD1) ;
   SYNCDELAY;
   REVCTL = 0x03;	//REVCTL.0 and REVCTL.1 set to 1

   //Endpoint configuration 
   SYNCDELAY;           // see TRM section 15.14
   EP2CFG = 0xA2;		//valid, Type is BULK OUT, double buffer, size is 512bytes 
   SYNCDELAY;                    
   EP6CFG = 0xE2;
   SYNCDELAY; 
   EP1INCFG = 0xB0;		//valid, Type is INTERRUPT
 
   //FIFO intialization(Restore FIFOs to default state)
   SYNCDELAY;
   FIFORESET = 0x80;             // activate NAK-ALL to avoid race conditions
   SYNCDELAY;
   FIFORESET = 0x02;             // reset, FIFO 
   SYNCDELAY;
   FIFORESET = 0x00;             // deactivate NAK-ALL

   SYNCDELAY;
   OUTPKTEND = 0x82;	//Arm both EP2 buffer
   SYNCDELAY;
   OUTPKTEND = 0x82;

   // enable dual autopointer feature
   AUTOPTRSETUP |= 0x01; 
   
   Rwuen = TRUE;            // Enable remote-wakeup
   
   // NAK INT Enable
   SYNCDELAY;
   NAKIRQ = bmBIT0;			// Clear the global IBN IRQ
   SYNCDELAY;
   NAKIE |= bmBIT0;			// Enable the global IBN IRQ
   SYNCDELAY;
   IBNIRQ = 0xFF;			// Clear any pending IBN IRQ
   SYNCDELAY;
   IBNIE |= bmEP1IBN;		// Enable the IBN interrupt for EP1

   memset(gpio_expan1_save, 0xFF, 2);
   memset(gpio_expan2_save, 0xFF, 2); 
   memset(gpio_expan3_save, 0xFF, 2);

   EZUSB_InitI2C();			// Initialize EZ-USB I2C controller
 			 
	// Configure GPIF from GPIFTool generated waveform data
   GpifInit();	

   OEA 		= 0xFF;			// Port A Output Enable   
   PORTACFG  = 0;			// use Port A

}

        

          void TD_Poll(void):这个函数就是用户调度程序,USB会在空闲的时候反复调用该函数,所以我们把自己需要反复执行的代码放在这里。本例里,该函数根据收到的指令,通过GPIF接口实现对FPGA的配置,数据读写等功能,具体代码如下:

void TD_Poll(void)     // Called repeatedly while the device is idle
{
   WORD i;
   WORD count;
   WORD *temp_buff;
   DWORD *send_buff;
   DWORD *recv_buff;  
   DWORD temp_size;
   WORD status = 0;
   
   /******** USB data write mode *******/
   if (usb_data_write_flag) {
 	//EP2E = 0, when endp FIFO not emptry , it means the data is commming	
	if (!(EP2468STAT & bmEP2EMPTY)) { 
    	count = ((EP2BCH << 8) + EP2BCL);
		SYNCDELAY;
		send_buff = (DWORD *)EP2FIFOBUF;
		if (usb_access_type == TYPE_REG) {	// register write
			FpgaRegWrite(ddr3_start_add, *send_buff);
			usb_data_write_flag = 0;	
		} else {  							// memory write	
			for (i = 0; i < count/4; i++) {
		    	//parse the address information			
				ddr3_temp_add =  ddr3_start_add & 0xFFFFF000;
				if ((ddr3_first_flag == 1) || (ddr3_start_add == ddr3_temp_add )) {
					FpgaRegWrite(FPGAE_DDR3_BASE_ADDR_REG, ddr3_temp_add);
					ddr3_first_flag = 0;
				}
				ddr3_temp_add = ddr3_start_add & 0x00000FFF;
				FpgaRegWrite(FPGAE_DDR3_WINDOW_AREA_REG + ddr3_temp_add, *send_buff); 
				send_buff ++; 
				ddr3_start_add += 4;                  		    
			}
			left_size -= count;
			if (left_size == 0) {	
                ddr3_temp_add = 0;
				usb_data_write_flag = 0;
	    	} 
		}
		SYNCDELAY;
  		EP2BCL = 0x00; // re(arm) EP2OUT		
  	}
  }  

  
  /******** USB data read mode	********/
  if (usb_data_read_flag) {
	if(!(EP2468STAT & bmEP6FULL)) {
	    recv_buff =  (DWORD *)EP6FIFOBUF;
		if (usb_access_type == TYPE_REG) {	  // register read
			FpgaRegRead(ddr3_start_add, recv_buff);
			usb_data_read_flag = 0;
		} else { 							//memory read
		    temp_size = left_size;	
		    if (left_size >= BLOCK_SIZE) {		/* left_size is larger than buffer size(512) */ 						
		  		for (i = 0; i < BLOCK_SIZE/4; i ++) {
					ddr3_temp_add =  ddr3_start_add & 0xFFFFF000;
					if ((ddr3_first_flag) || (ddr3_start_add == ddr3_temp_add )) {
						FpgaRegWrite(FPGAE_DDR3_BASE_ADDR_REG, ddr3_temp_add);
						ddr3_first_flag = 0;
					}
					ddr3_temp_add = ddr3_start_add & 0x00000FFF;
					FpgaRegRead(FPGAE_DDR3_WINDOW_AREA_REG + ddr3_temp_add, recv_buff);
					recv_buff ++; 
					ddr3_start_add += 4;
				}
				left_size -= BLOCK_SIZE;
		 	 } else {				 /* left_size is less than buffer size(512)	*/
			    for (i = 0; i < left_size/4; i ++) {
					ddr3_temp_add =  ddr3_start_add & 0xFFFFF000;
					if ((ddr3_first_flag) || (ddr3_start_add == ddr3_temp_add )) {
						FpgaRegWrite(FPGAE_DDR3_BASE_ADDR_REG, ddr3_temp_add);
						ddr3_first_flag = 0;
					}
					ddr3_temp_add = ddr3_start_add & 0x00000FFF;
					FpgaRegRead(FPGAE_DDR3_WINDOW_AREA_REG + ddr3_temp_add, recv_buff);
					recv_buff ++; 
	
					ddr3_start_add += 4;
				}
				left_size = 0;
			 }		         
		}	
	 	if (left_size == 0) {		
            ddr3_temp_add = 0;
			usb_data_read_flag = 0;
	 	}
		if (usb_access_type == TYPE_REG) {	 // register read
			SYNCDELAY;
			EP6BCH = 0x00;  
    		SYNCDELAY;  
    		EP6BCL = 0x04; // re(arm) EP6IN
		} else {
		    if (temp_size >= BLOCK_SIZE) {		// memory read
		  		SYNCDELAY;
				EP6BCH = 0x02;  
    			SYNCDELAY;  
    			EP6BCL = 0x00; // re(arm) EP6IN
			} else {
			    SYNCDELAY;
				EP6BCH = 0x00;  
    			SYNCDELAY;  
    			EP6BCL = temp_size; // re(arm) EP6IN
			}
		}  
	}
  } 

  /******** Configurate mode *********/
  if (fpga_config_start_flag ) {
  	if (!(EP2468STAT & bmEP2EMPTY)) {   //EP2E = 0, when endp FIFO not emptry , it means the config data is commming
 		count = ((EP2BCH << 8) + EP2BCL);
		SYNCDELAY;
		temp_buff = (WORD *)EP2FIFOBUF;	

		for (i = 0; i < count/2; i++) {
        	//wait until the data bus is idle 
			CpldRegWrite(CPLD_CONFIG_DATA_REG, *temp_buff);
			while(!(status & USB_CONFIG_READY)) {
            	CpldRegRead(CPLD_CONFIG_STATUS_REG, &status);
                EZUSB_Delay(1);
    		} 
			temp_buff ++;                
            left_size -= 2;
		}
   
		if (left_size == 0) {  //it means all config data send
			fpga_config_start_flag = 0;
            config_flag &= ~USB_CONFIG_START;
            CpldRegWrite(CPLD_CONFIG_CONTROL_REG, config_flag);
		}
        SYNCDELAY;
        EP2BCL = 0x00;       // re(arm) EP2OUT
	 }
  }



}

         

         BOOL DR_VendorCmnd(void):这个函数就是自定义命令代码的书写处。我们的Vendor命令都会写在这里,fw.c固件会自动调用我们的代码来解析指令。

本项目中该函数的部分代码如下:

BOOL DR_VendorCmnd(void)
{
   BYTE gpio_value;

   switch (SETUPDAT[1])
   {  
      case FPGA_ID_SELECT:
   		fpga_id = SETUPDAT[2]; 		//get the FPGA ID number which will be configurated
        config_flag = (USB_CONFIG_MODE  | USB_CONFIG_START) | ((WORD)fpga_id);
		CpldRegWrite(CPLD_CONFIG_CONTROL_REG, config_flag);
 		break;

      case FPGA_CONFIG_DATA_SIZE_SET:
		fpga_config_file_size =  (DWORD)SETUPDAT[2];           
		fpga_config_file_size |= ((DWORD)SETUPDAT[3]) << 8; 
        fpga_config_file_size |= ((DWORD)SETUPDAT[4]) << 16; 
		fpga_config_file_size |= ((DWORD)SETUPDAT[5]) << 24; 
		left_size = fpga_config_file_size;
      	break;
 
      case FPGA_CONFIG_DATA_TRANS_START:
		fpga_config_start_flag = 1;
		break;

     case FPGA_CONFIG_DATA_TRANS_STOP:
      	fpga_config_start_flag = 0;
      	break;

     case USB_DATA_READ:
	 	 if (SETUPDAT[7] != 0 || SETUPDAT[6] != 0) {
			EP0BCH = SETUPDAT[7];
			SYNCDELAY;
			EP0BCL = SETUPDAT[6];
			SYNCDELAY;
			while(EP0CS & bmEPBUSY);
		}
		usb_access_type =  SETUPDAT[4];

		ddr3_start_add = (DWORD)EP0BUF[0];           
		ddr3_start_add |= ((DWORD)EP0BUF[1]) << 8; 
        ddr3_start_add |= ((DWORD)EP0BUF[2]) << 16; 
		ddr3_start_add |= ((DWORD)EP0BUF[3]) << 24;

		ddr3_trans_size = (DWORD)EP0BUF[4];           
		ddr3_trans_size |= ((DWORD)EP0BUF[5]) << 8; 
        ddr3_trans_size |= ((DWORD)EP0BUF[6]) << 16; 
		ddr3_trans_size |= ((DWORD)EP0BUF[7]) << 24;

		left_size =  ddr3_trans_size;
		ddr3_first_flag = 1;
		usb_data_read_flag = 1;	
      	break;

      case USB_DATA_WRITE:
        if (SETUPDAT[7] != 0 || SETUPDAT[6] != 0) {
			EP0BCH = SETUPDAT[7];
			SYNCDELAY;
			EP0BCL = SETUPDAT[6];
			SYNCDELAY;
			while(EP0CS & bmEPBUSY);
		}
		usb_access_type =  SETUPDAT[4];	

		ddr3_start_add = (DWORD)EP0BUF[0];           
		ddr3_start_add |= ((DWORD)EP0BUF[1]) << 8; 
        ddr3_start_add |= ((DWORD)EP0BUF[2]) << 16; 
		ddr3_start_add |= ((DWORD)EP0BUF[3]) << 24;

		ddr3_trans_size = (DWORD)EP0BUF[4];           
		ddr3_trans_size |= ((DWORD)EP0BUF[5]) << 8; 
        ddr3_trans_size |= ((DWORD)EP0BUF[6]) << 16; 
		ddr3_trans_size |= ((DWORD)EP0BUF[7]) << 24;
		left_size =  ddr3_trans_size;
		ddr3_first_flag = 1;
		usb_data_write_flag = 1;

		break;

        //..........
        //..........
        //.......... 

      default:
      	return(TRUE); 
   }

   return(FALSE);

}

          void ISR_Ep0in(void) interrupt 0~void ISR_Ep8inout(void) interrupt 0:这几个函数是当使用端点中断传输时,中断代码的书写处,可根据需要添加相应代码。

(5):resume.c: USB设备唤醒时调用。

(6):delay.c: 延时调用。

(7):discon.c: 设备连接断开时进行相应处理。

(8):i2c.c 和i2c_rw.c都是用于i2c的相关函数。不需要该功能的,不包含进来即可。


总结: 由于Cypress已经帮我们开发者搭建好了一个很好的框架,基于它的USB固件开发还是比较简单的。弄清整个工程的结构,以及各个文件的具体作用之后,即可在相应地方添加我们的代码,实现我们USB设备的具体功能。

你可能感兴趣的:(USB设备)