基于STM32的USB开发

基于STM32USB程序开发笔记()

第一篇:须要准备的一些资料

1STM32的参考手册,这对于设备底层USB的硬件配置以及事件驱动机制的了解尤其重要,你须要了解各个寄存器的功能以及如何操做,好比CNTRISTREPnRDADDR等等,若是你想学习USB,这个手册是必须的。
2USB2.0 协议,这个资料一样必不可少,若是由于英语阅读能力而苦苦寻找中文版的USB2.0协议,建议不要这么作,如今网络中的所谓的中文版的USB2.0协议不是官方撰写的,大多数是一些热心朋友本身翻译的,却不是很全面,若是你在为寻找这类的资料而无所获时,建议认真塌实的看看官方英文版的USB2.0协议,官方协议阐述的十分详细,650多页,一字一句的了解所有协议不太可行,可针对性的重点理解,好比对第9USB Device Framework的详细理解对于你的USB Device固件开发不可缺乏(这里就是STM32)。
3ST提供的USB固件库,这个类库较为散乱,但不可不参考

如下是最近这段时间的成果,包含固件、驱动以及应用程序,固件部分有些功能是不被支持的,如SR_SetDescriptor()、 SR_SynchFrame()等等,在此说明暂时不支持非故意如此,而是还没时间仔细深刻编写完善,由于这些目前不被支持的部分目前不被使用到。
后序将接着对各个部分进行一些说明,但愿朋友们多多支持,同时欢迎朋友们讨论。
若是你使用的是万利的STM3210B-LK1开发板,则能够烧写hex文件后进行测试。

下载该文件请参阅:
http://www.ouravr.com/bbs/bbs_content.jsp?bbs_sn=1808061&bbs_page_no=1&bbs_id=3020


2篇:STM32 USB固件函数的驱动原理
首先须要了解一个概念:
USB 设备(DEVICE)历来只是被动触发,USB主机(HOST)掌握主动权,发送什么数据,何时发送,是给设备数据仍是从设备请求数据,都是由USB 主机完成的,USB设备只是配合主机完成设备的枚举、数据方向和大小。根据数据特性再决定该不应回复该如何回复、该不应接收该如何接收这些动做。
了解这些,再仔细查看STM32的参考手册USB部分以及STM32的中断向量表,从中能够找到两个中断:
/*******************************************************************************
* Function Name  : USB_HP_CAN_TX_IRQHandler
* Description    : This function handles USB High Priority or CAN TX interrupts
*                  requests.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void USB_HP_CAN_TX_IRQHandler(void)
{
  USB_HPI();
}

/*******************************************************************************
* Function Name  : USB_LP_CAN_RX0_IRQHandler
* Description    : This function handles USB Low Priority or CAN RX0 interrupts
*                  requests.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void USB_LP_CAN_RX0_IRQHandler(void)
{
  USB_LPI();
}

即 USB的高、低优先权中断处理函数,这也是整个STM32 USB的事件驱动源,USB_HPI()USB_LPI()既而转向usb_core(.c,.h)进行相关处理。中断传输(interrupt)、控制传输(control)、大流量传输(bulk)USB_LPI()响应,大流量传输(bulk)一样可能响应USB_HPI()同步传输 (isochronous)只响应USB_HPI()

这样响应USB的全部请求只须要关注usb_core.c文件中的 USB_LPI()USB_HPI()函数。因为本人也是对USB刚刚有所了解,于是在本例笔记中USB_HPI()函数未作任何处理,在此开源但愿你们能完善与纠正错误并能共享喜悦。如下是USB_LPI()函数:
// *****************************************************************************
// Function Name  : USB_LPI.
// Description    : Low Priority Interrupt's service routine.
// Input          :
// Output         :
// Return         :
// *****************************************************************************
void USB_LPI(void)
{
  unsigned short wValISTR = GetISTR();

#if(CNTR_MASK & ISTR_RESET)   // Reset
  if(wValISTR & ISTR_RESET & vwInterruptMask)
  {
    SetISTR(CLR_RESET);
    INT_ISTR_RESET();
  }
#endif

#if(CNTR_MASK & ISTR_DOVR)   // DMA Over/Underrun
  if(wValISTR & ISTR_DOVR & vwInterruptMask)
  {
    SetISTR(CLR_DOVR);
    INT_ISTR_DOVR();
  }
#endif

#if(CNTR_MASK & ISTR_ERR)   // Error
  if(wValISTR & ISTR_ERR & vwInterruptMask)
  {
    SetISTR(CLR_ERR);
    INT_ISTR_ERROR();
  }
#endif

#if(CNTR_MASK & ISTR_WKUP)    // Wakeup
  if(wValISTR & ISTR_WKUP & vwInterruptMask)
  {
    SetISTR(CLR_WKUP);
    INT_ISTR_WAKEUP();
  }
#endif

#if(CNTR_MASK & ISTR_SUSP)   // Suspend
  if(wValISTR & ISTR_SUSP & vwInterruptMask)
  {
    INT_ISTR_SUSPEND();
    SetISTR(CLR_SUSP);     // must be done after setting of CNTR_FSUSP
  }
#endif

#if(CNTR_MASK & ISTR_SOF)   // Start Of Frame
  if(wValISTR & ISTR_SOF & vwInterruptMask)
  {
    SetISTR(CLR_SOF);
    INT_ISTR_SOF();
  }
#endif

#if(CNTR_MASK & ISTR_ESOF)   // Expected Start Of Frame
  if(wValISTR & ISTR_ESOF & vwInterruptMask)
  {
    SetISTR(CLR_ESOF);
    INT_ISTR_ESOF();
  }
#endif

#if(CNTR_MASK & ISTR_CTR)   // Correct Transfer
  if(wValISTR & ISTR_CTR & vwInterruptMask)
  {
    INT_ISTR_CTR();
  }
#endif
}

// *****************************************************************************
// Function Name  : USB_HPI.
// Description    : High Priority Interrupt's service routine.
// Input          :
// Output         :
// Return         :
// *****************************************************************************
void USB_HPI(void)
{
 
}

能够看出,在USB_LPI()函数中,根据STM32 USB的中断状态寄存器(ISTR)的标志位的状态以及定义的USB控制寄存器中断事件屏蔽码,响应各自的中断事件,好比 INT_ISTR_RESET()响应USB的复位中断,通常可在此函数内进行USB的寄存器的初始化;INT_ISTR_CTR()响应一次正确的数据传输中断,故名思意,在完成一次正确的数据传输操做后,就会响应此函数。
具体含义请仔细查阅STM32参考手册,下篇将针对这些响应函数进行逐一的详细介绍。php

基于STM32USB程序开发笔记(——STM32 USB固件函数的一些介绍网络

 

接着上篇,详细状况能够查看usb_core(.c/.h)STM32 USB中断事件为如下几种:
void ISTR_CTR(void);
void ISTR_SOF(void);
void ISTR_ESOF(void);
void ISTR_DOVR(void);
void ISTR_ERROR(void);
void ISTR_RESET(void);
void ISTR_WAKEUP(void);
void ISTR_SUSPEND(void);

这些处理函数使能由定义CNTR_MASK决定:
// CNTR mask control
#define CNTR_MASK   CNTR_CTRM | CNTR_WKUPM | CNTR_SUSPM | CNTR_ERRM |     \
                    CNTR_SOFM | CNTR_ESOFM | CNTR_RESETM | CNTR_DOVRM     \

其中着重说明的是ISTR_RESET()ISTR_CTR()函数,ISTR_RESET()主要处理USB复位后进行一些初始化任务,ISTR_CTR()则是处理数据正确传输后控制,好比说响应主机。

// *****************************************************************************
// Function Name  : INT_ISTR_RESET
// Description    : ISTR Reset Interrupt service routines.
// Input          :
// Output         :
// Return         :
// *****************************************************************************
void INT_ISTR_RESET(void)
{
  // Set the buffer table address
  SetBTABLE(BASEADDR_BTABLE);

  // Set the endpoint type: ENDP0
  SetEPR_Type(ENDP0, EP_CONTROL);
  Clr_StateOut(ENDP0);

  // Set the endpoint data buffer address: ENDP0 RX
  SetBuffDescTable_RXCount(ENDP0, ENDP0_PACKETSIZE);
  SetBuffDescTable_RXAddr(ENDP0, ENDP0_RXADDR);

  // Set the endpoint data buffer address: ENDP0 TX
  SetBuffDescTable_TXCount(ENDP0, 0);
  SetBuffDescTable_TXAddr(ENDP0, ENDP0_TXADDR);

  // Initialize the RX/TX status: ENDP0
  SetEPR_RXStatus(ENDP0, EP_RX_VALID);
  SetEPR_TXStatus(ENDP0, EP_TX_NAK);

  // Set the endpoint address: ENDP0
  SetEPR_Address(ENDP0, ENDP0);

  // ---------------------------------------------------------------------
  // TODO: Add you code here
  // ---------------------------------------------------------------------
  // Set the endpoint type: ENDP1
  SetEPR_Type(ENDP1, EP_INTERRUPT);
  Clr_StateOut(ENDP1);

  // Set the endpoint data buffer address: ENDP1 RX
  SetBuffDescTable_RXCount(ENDP1, ENDP1_PACKETSIZE);
  SetBuffDescTable_RXAddr(ENDP1, ENDP1_RXADDR);

  // Set the endpoint data buffer address: ENDP1 TX
  SetBuffDescTable_TXCount(ENDP1, 0);
  SetBuffDescTable_TXAddr(ENDP1, ENDP1_TXADDR);

  // Initialize the RX/TX status: ENDP1
  SetEPR_RXStatus(ENDP1, EP_RX_VALID);
  SetEPR_TXStatus(ENDP1, EP_TX_DIS);

  // Set the endpoint address: ENDP1
  SetEPR_Address(ENDP1, ENDP1);
 
 
 
  SetEPR_Type(ENDP2, EP_INTERRUPT);
  Clr_StateOut(ENDP2);

  // Set the endpoint data buffer address: ENDP2 RX
  SetBuffDescTable_RXCount(ENDP2, ENDP2_PACKETSIZE);
  SetBuffDescTable_RXAddr(ENDP2, ENDP2_RXADDR);

  // Set the endpoint data buffer address: ENDP2 TX
  SetBuffDescTable_TXCount(ENDP2, 0);
  SetBuffDescTable_TXAddr(ENDP2, ENDP2_TXADDR);

  // Initialize the RX/TX status: ENDP2
  SetEPR_RXStatus(ENDP2, EP_RX_DIS);
  SetEPR_TXStatus(ENDP2, EP_TX_VALID);

  // Set the endpoint address: ENDP2
  SetEPR_Address(ENDP2, ENDP2);



  // ---------------------------------------------------------------------
  // End of you code
  // ---------------------------------------------------------------------
 
  SetDADDR(0x0080 | vsDeviceInfo.bDeviceAddress);
  vsDeviceInfo.eDeviceState = DS_DEFAULT;
  vsDeviceInfo.bCurrentFeature = 0x00;
  vsDeviceInfo.bCurrentConfiguration = 0x00;
  vsDeviceInfo.bCurrentInterface = 0x00;
  vsDeviceInfo.bCurrentAlternateSetting = 0x00;
  vsDeviceInfo.uStatusInfo.w = 0x0000;
}
在这个ISTR_CTR()函数中,定义了EP012的传输方式以及各自的缓冲描述符,其中EP0是默认端口,负责完成USB设备的枚举,通常状况是不须要更改的。其余端点配置则需根据实际应用而决定,如何设置请仔细理解STM32的参考手册。
值得说明的是STM32的端点RX/TX缓冲描述表是定义在PMA中的,他是基于分组缓冲区描述报表寄存器(BTABLE)而定位的,各端点RX/TX缓冲描述表说明是数据存储地址以及大小,这个概念须要了解,ST提供的固件很含糊,为此,我在usb_regs.h文件中进行了从新定义,以下:
// USB_IP Packet Memory Area base address 
#define PMAAddr  (0x40006000L) 

// Buffer Table address register
#define BTABLE  ((volatile unsigned *)(RegBase + 0x50))

// *****************************************************************************
// Packet memory area: Total 512Bytes
// *****************************************************************************
#define BASEADDR_BTABLE        0x0000
// *****************************************************************************
// PMAAddr + BASEADDR_BTABLE + 0x00000000 : EP0_TX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x00000002 : EP0_TX_COUNT
// PMAAddr + BASEADDR_BTABLE + 0x00000004 : EP0_RX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x00000006 : EP0_RX_COUNT
//
// PMAAddr + BASEADDR_BTABLE + 0x00000008 : EP1_TX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x0000000A : EP1_TX_COUNT
// PMAAddr + BASEADDR_BTABLE + 0x0000000C : EP1_RX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x0000000E : EP1_RX_COUNT
//
// PMAAddr + BASEADDR_BTABLE + 0x00000010 : EP2_TX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x00000012 : EP2_TX_COUNT
// PMAAddr + BASEADDR_BTABLE + 0x00000014 : EP2_RX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x00000016 : EP2_RX_COUNT
//
// PMAAddr + BASEADDR_BTABLE + 0x00000018 : EP3_TX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x0000001A : EP3_TX_COUNT
// PMAAddr + BASEADDR_BTABLE + 0x0000001C : EP3_RX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x0000001E : EP3_RX_COUNT
//
// PMAAddr + BASEADDR_BTABLE + 0x00000020 : EP4_TX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x00000022 : EP4_TX_COUNT
// PMAAddr + BASEADDR_BTABLE + 0x00000024 : EP4_RX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x00000026 : EP4_RX_COUNT
//
// PMAAddr + BASEADDR_BTABLE + 0x00000028 : EP5_TX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x0000002A : EP5_TX_COUNT
// PMAAddr + BASEADDR_BTABLE + 0x0000002C : EP5_RX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x0000002E : EP5_RX_COUNT
//
// PMAAddr + BASEADDR_BTABLE + 0x00000030 : EP6_TX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x00000032 : EP6_TX_COUNT
// PMAAddr + BASEADDR_BTABLE + 0x00000034 : EP6_RX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x00000036 : EP6_RX_COUNT
//
// PMAAddr + BASEADDR_BTABLE + 0x00000038 : EP7_TX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x0000003A : EP7_TX_COUNT
// PMAAddr + BASEADDR_BTABLE + 0x0000003C : EP7_RX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x0000003E : EP7_RX_COUNT
// *****************************************************************************
//
// PMAAddr + BASEADDR_BTABLE + (0x00000040 - 0x000001FF) : assigned to data buffer
//
// *****************************************************************************
#define BASEADDR_DATA   (BASEADDR_BTABLE + 0x00000040)

// ENP0
#define ENDP0_PACKETSIZE      0x40
#define ENDP0_RXADDR          BASEADDR_DATA
#define ENDP0_TXADDR          (ENDP0_RXADDR + ENDP0_PACKETSIZE)

// ENP1
#define ENDP1_PACKETSIZE      0x40
#define ENDP1_RXADDR          (ENDP0_TXADDR + ENDP0_PACKETSIZE)
#define ENDP1_TXADDR          (ENDP1_RXADDR + ENDP1_PACKETSIZE)

// ENP2
#define ENDP2_PACKETSIZE      0x40
#define ENDP2_RXADDR          (ENDP1_TXADDR + ENDP1_PACKETSIZE)
#define ENDP2_TXADDR          (ENDP2_RXADDR + ENDP2_PACKETSIZE)

// ENP3
#define ENDP3_PACKETSIZE      0x40
#define ENDP3_RXADDR          (ENDP2_TXADDR + ENDP2_PACKETSIZE)
#define ENDP3_TXADDR          (ENDP3_RXADDR + ENDP3_PACKETSIZE)

// ENP4
#define ENDP4_PACKETSIZE      0x40
#define ENDP4_RXADDR          (ENDP3_TXADDR + ENDP3_PACKETSIZE)
#define ENDP4_TXADDR          (ENDP4_RXADDR + ENDP4_PACKETSIZE)

// ENP5
#define ENDP5_PACKETSIZE      0x40
#define ENDP5_RXADDR          (ENDP4_TXADDR + ENDP4_PACKETSIZE)
#define ENDP5_TXADDR          (ENDP5_RXADDR + ENDP5_PACKETSIZE)

// ENP6
#define ENDP6_PACKETSIZE      0x40
#define ENDP6_RXADDR          (ENDP5_TXADDR + ENDP5_PACKETSIZE)
#define ENDP6_TXADDR          (ENDP6_RXADDR + ENDP6_PACKETSIZE)

// ENP7
#define ENDP7_PACKETSIZE      0x40
#define ENDP7_RXADDR          (ENDP6_TXADDR + ENDP6_PACKETSIZE)
#define ENDP7_TXADDR          (ENDP7_RXADDR + ENDP7_PACKETSIZE)

这样,通常只要在PMA的大小区域内(512Bytes),修改端点EPnR的数据包大小就能够了,固然,实际状况能够根据须要进行更改。


// *****************************************************************************
// Function Name  : INT_ISTR_CTR
// Description    : ISTR Correct Transfer Interrupt service routine.
// Input          :
// Output         :
// Return         :
// *****************************************************************************
void INT_ISTR_CTR(void)
{
  unsigned short wEPIndex;
  unsigned short wValISTR;
  unsigned short wValENDP;

  while( ((wValISTR=GetISTR()) & ISTR_CTR) != 0 )
  {
    // Get the index number of the endpoints
    wEPIndex =  wValISTR & ISTR_EP_ID;

    if(wEPIndex == 0)
    {
      // Set endpoint0 RX/TX status: NAK (Negative-Acknowlegment)
      SetEPR_RXStatus(ENDP0, EP_RX_NAK);
      SetEPR_TXStatus(ENDP0, EP_TX_NAK);

       // Transfer direction
      if((wValISTR & ISTR_DIR) == 0)
      {
        // DIR=0: IN
        // DIR=0 implies that EP_CTR_TX always 1
        ClrEPR_CTR_TX(ENDP0);
        CTR_IN0();
        return;
      }
      else
      {
        // DIR=1: SETUP or OUT
        // DIR=1 implies that CTR_TX or CTR_RX always 1
        wValENDP = GetEPR(ENDP0);
        if((wValENDP & EP_CTR_TX) != 0)
        {
          ClrEPR_CTR_TX(ENDP0);
          CTR_IN0();
          return;
        }
        else if((wValENDP & EP_SETUP) != 0)
        {
          ClrEPR_CTR_RX(ENDP0);
          CTR_SETUP0();
          return;
        }
        else if((wValENDP & EP_CTR_RX) != 0)
        {
          ClrEPR_CTR_RX(ENDP0);
          CTR_OUT0();
          return;
        }
      }     
    }
    // Other endpoints
    else
    {
      wValENDP = GetEPR(wEPIndex);
     
      SetEPR_RXStatus(wEPIndex, EP_RX_NAK);
      SetEPR_TXStatus(wEPIndex, EP_TX_NAK);
            
      if((wValENDP & EP_CTR_TX) != 0)
      {
        ClrEPR_CTR_TX(wEPIndex);
        switch(wEPIndex)
        {
        case ENDP1: CTR_IN1(); break;
        case ENDP2: CTR_IN2(); break;
        case ENDP3: CTR_IN3(); break;
        case ENDP4: CTR_IN4(); break;
        case ENDP5: CTR_IN5(); break;
        case ENDP6: CTR_IN6(); break;
        case ENDP7: CTR_IN7(); break;
        default: break;
        }
      }

      if((wValENDP & EP_CTR_RX) != 0)
      {
        ClrEPR_CTR_RX(wEPIndex);
        switch(wEPIndex)
        {
        case ENDP1: CTR_OUT1(); break;
        case ENDP2: CTR_OUT2(); break;
        case ENDP3: CTR_OUT3(); break;
        case ENDP4: CTR_OUT4(); break;
        case ENDP5: CTR_OUT5(); break;
        case ENDP6: CTR_OUT6(); break;
        case ENDP7: CTR_OUT7(); break;
        default: break;
        }
      }
    }
  }
}

INT_ISTR_CTR()函数将各自响应事件提取出来,默认端点EP0也是最为复杂的,这个须要查看STM32的参考手册以及USB协议才能更好了解为什么如此。到这里STM32 USB里数据传输事件就指向了各个对应的端点。下篇着重说明USB设备的枚举。数据结构

基于STM32USB程序开发笔记()——USB设备的枚举(上)jsp

 

USB设备可否工做,枚举步骤,用乡村爱情里的话说,必须的!,网上也有不少资料,圈圈就提供了一份详细的枚举过程,但对STM32是怎么响应的没有说明,一会详细道来,先上圈圈的提供的那个枚举图示,但愿圈圈支持,若是不妥,请与我联系,谢谢。
我将此转换成了PDF文件,方便查看。
首先说明一个变量,定义在usb_core.c中:
volatile DEVICE_INFO vsDeviceInfo;
看意思就知道他的做用了,DEVICE_INFO是个结构,定义在usb_type.h中:
// *****************************************************************************
// DEVICE_INFO
// *****************************************************************************
typedef struct _DEVICE_INFO
{
  unsigned char bDeviceAddress;

  unsigned char bCurrentFeature;
  unsigned char bCurrentConfiguration;
  unsigned char bCurrentInterface;
  unsigned char bCurrentAlternateSetting;

  WORD_2BYTE    uStatusInfo;

  DEVICE_STATE  eDeviceState;
  RESUME_STATE  eResumeState;
  CONTROL_STATE eControlState;

  SETUP_DATA    SetupData;

  TRANSFER_INFO TransInfo;
}
DEVICE_INFO,
*PDEVICE_INFO;

在枚举过程当中,就是如何处理好SETUP事件,若是STM32 USB接收到正确的SETUP事件,将响应函数CTR_SETUP0()SETUP事件是特殊的OUT事件,数据方向 Host->DeviceSETUP事件数据长度固定为8,数据定义在DEVICE_INFO.SetupData,其数据结构是(定义在 usb_type.h)
typedef struct _SETUP_DATA
{
  unsigned char bmRequestType;            // request type
  unsigned char bRequest;                 // request code

  WORD_2BYTE wValue;
  WORD_2BYTE wIndex;
  WORD_2BYTE wLength;
}
SETUP_DATA,
*PSETUP_DATA;

WORD_2BYTE是定义的一个共用体:
typedef union _WORD_2BYTE
{
  unsigned short w;
  struct
  {
    unsigned char LSB;
    unsigned char MSB;
  }b;
}
WORD_2BYTE;

为何将SETUP数据结构中的wValue,wIndex,wLength如此定义?
1USB协议中全部数据传输都是依照低位在先的原则
2:高地位字节可能功能复用
这样在后续的程序编写中就变得十分方便,ST提供的USB固件方法一样如此,但这方面的处理让人有些摸不着头脑,详情可参阅。至于具体的SETUP数据结构含义如何,仍是要具有基本知识:了解USB协议

CTR_SETUP0() 函数将SETUP数据提取出来,SETUP数据结构有0长度和非0长度的数据结构,详细参阅USB2.0官方协议第9章。在这将两种区别开来分别执行 SETUP0_NoData()SETUP0_Data()函数,并返回结果,根据返回结果再响应USB主机
// *****************************************************************************
// Function Name  : CTR_SETUP0
// Description    :
// Input          :
// Output         :
// Return         :
// *****************************************************************************
void CTR_SETUP0(void)
{
  RESULT eResult;

  BufferCopy_PMAToUser( (unsigned char *)&vsDeviceInfo.SetupData,
                        GetBuffDescTable_RXAddr(ENDP0),
                        GetBuffDescTable_RXCount(ENDP0));

  if(vsDeviceInfo.SetupData.wLength.w == 0)
  {
    eResult = SETUP0_NoData();
  }
  else
  {
    eResult = SETUP0_Data();
  }

  switch(eResult)
  {
  case RESULT_SUCCESS:

    break;

  case RESULT_LASTDATA:

    break;

  case RESULT_ERROR:
  case RESULT_UNSUPPORT:
    SetEPR_RXStatus(ENDP0, EP_RX_VALID);
    SetEPR_TXStatus(ENDP0, EP_TX_STALL);
    break;
  }
}

SETUP0_Data() SETUP0_NoData()函数支持的全部USB请求类型只有罗列的这些,有多少种组合都定义在USB协议中,程序根据请求代码,再去执行对应函数,这样作的目的就是让程序结构明了。其中注释为"// done"的部分代表此部分功能已完成。对于未完成部分,但愿你们在交流中完善。

// *****************************************************************************
// Routine Groups: SETUP_Data
// *****************************************************************************
RESULT SETUP0_Data(void)
{
  // SetupData.bRequest: request code
  switch(vsDeviceInfo.SetupData.bRequest)
  {
  case SR_GET_STATUS:         return SR_GetStatus();                  // done
  case SR_GET_DESCRIPTOR:     return SR_GetDescriptor();              // done
  case SR_SET_DESCRIPTOR:     return SR_SetDescriptor();              // unsupport
  case SR_GET_CONFIGURATION:  return SR_GetConfiguration();           // done
  case SR_GET_INTERFACE:      return SR_GetInterface();               // unsupport
  case SR_SYNCH_FRAME:        return SR_SynchFrame();                 // unsupport

  default: return RESULT_UNSUPPORT;
  }
}

// *****************************************************************************
// Routine Groups: SETUP_NoData
// *****************************************************************************
RESULT SETUP0_NoData(void)
{
  // SetupData.bRequest: request code
  switch(vsDeviceInfo.SetupData.bRequest)
  {
  case SR_CLEAR_FEATURE:      return SR_ClearFeature();        // unsupport
  case SR_SET_FEATURE:        return SR_SetFeature();          // unsupport
  case SR_SET_ADDRESS:        return SR_SetAddress();          // done
  case SR_SET_CONFIGURATION:  return SR_SetConfiguration();    // done
  case SR_SET_INTERFACE:      return SR_SetInterface();        // unsupport

  default: return RESULT_UNSUPPORT;
  }
}
函数