STM32-FS-USB-Device驱动代码简述(二)

目录

链接快速定位 

前沿

1 STM32-FS-USB驱动程序下载

2 STM32-USB-FS设备固件库

2.1 USB应用程序层次结构

2.2 USB-FS_Device peripheral interface

2.3 USB-FS-Device_Driver medium layer

2.3 Application interface

3 代码讲解

3.1 初始化代码讲解

3.2 描述符讲解

3.3 中断处理函数

3.3.1 复位函数

3.3.2 正确传输完成函数

3.3.2.1 枚举过程正确传输完成函数

3.3.2.2 非端点0正确传输完成函数


链接快速定位 

USB — 初识USB协议(一)

STM32F10x, STM32L1xx and STM32F3xx USB full speed device library (UM0424)

STM32 USB-FS-Device development kit

前沿

        本小节主要讲述STM32F103USB设备模式下的标准库驱动程序结构,代码比较复杂,需要读者对USB协议有一个全面的了解,这里只讲述一下比较浅显的东西。

        更多资料请参考:STM32 USB-FS-Device development kit

1 STM32-FS-USB驱动程序下载

        每个芯片厂商都会提供一套属于自己的外设驱动及应用程序,这也是我们所说的生态的一部分,可以在官网下载。我们这里主要讲解STM32F103的USB驱动,所以我们需要去ST的官网下载我们需要的驱动程序,STM32F10x, STM32L1xx and STM32F3xx USB full speed device library (UM0424)。

2 STM32-USB-FS设备固件库

2.1 USB应用程序层次结构

        下图显示了典型USB的不同组件之间的交互应用程序和USB FS设备库。

        从上图可知,USB设备固件库被分为两层:

  • STM32_USB-FS_Device_Driver:该层管理与USB-FS_ Device外围设备和USB标准协议的直接通信。STM32_USBFS_ Device_Driver符合USB 2.0规范,与标准STM32标准外设库分离。
  • Application Interface layer:该层为用户提供了库核心和最终应用程序之间的完整接口。
  •         下图显示了USB FS设备库的程序包组织以及所有演示和子文件夹。

    2.2 USB-FS_Device peripheral interface

            USB全速设备外设接口层主要由以下部分组成:

  • 硬件抽象层
  • 正确传输中断服务例程
  • 数据传输管理
  • 2.3 USB-FS-Device_Driver medium layer

            USB全速设备驱动中间层主要由以下部分组成:

  • USB设备全局变量初始化
  • USB协议管理
  • 简化了对端点的读写访问功能(USB-FS_Device外围设备的抽象层)
  • 库中使用的USB定义和类型
  • 根据使用的评估板定义硬件
  • 2.3 Application interface

            应用接口主要由以下部分组成:

  • USB全速设备配置文件
  • USB设备描述符
  • USB设备应用程序特定属性
  • 不带控制端点的正确传输中断例程
  • USB设备中断处理函数
  • USB设备电源和连接管理函数
  • 3 代码讲解

            代码讲解主要涉及以下几个方面:

  • 初始化
  • 描述符
  • 中断处理函数
  • 复位函数
  • 正确传输完成函数
  •         这里以“VirtualComport_Loopback”工程为例进行讲解。

    3.1 初始化代码讲解

  • GPIO初始化
  •         GPIO初始化程序主要是初始化通信所使用的DM和DP引脚以及USB全速设备的DP上拉引脚。

    void Set_System(void)
    {
      GPIO_InitTypeDef  GPIO_InitStructure;  
    
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
      
      /********************************************/
      /*  Configure USB DM/DP pins                */
      /********************************************/
      
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
      GPIO_Init(GPIOA, &GPIO_InitStructure);
      
      /* USB_DISCONNECT used as USB pull-up */
      GPIO_InitStructure.GPIO_Pin = USB_DISCONNECT_PIN;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
      GPIO_Init(USB_DISCONNECT, &GPIO_InitStructure);
      
      /* Enable the USB disconnect GPIO clock */
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_DISCONNECT, ENABLE);
    }
  • USB时钟配置
  •         USB时钟配置主要是配置USB的PHY时钟,运行在48MHz的时钟下(系统时钟72M),并使能USB时钟。

    void Set_USBClock(void)
    {
      /* Select USBCLK source */
      RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);
      
      /* Enable the USB clock */
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);
    }

  • USB中断初始化
  •         USB中断初始化,主要是使能USB的中断,并且使能中断屏蔽位。

    void USB_Interrupts_Config(void)
    {
        NVIC_InitTypeDef NVIC_InitStructure;
    
        /* 2 bit for pre-emption priority, 2 bits for subpriority */
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    
        /* Enable the USB interrupt */
        NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
    
        /* Enable the USB Wake-up interrupt */
        NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
        NVIC_Init(&NVIC_InitStructure);   
    
    }
    RESULT PowerOn(void)
    {
      uint16_t wRegVal;
      
    #if !defined (USE_NUCLEO)
      /*** cable plugged-in ? ***/
      USB_Cable_Config(ENABLE);
    #endif
    
      /*** CNTR_PWDN = 0 ***/
      wRegVal = CNTR_FRES;
      _SetCNTR(wRegVal);
    
      /* The following sequence is recommended:
        1- FRES = 0
        2- Wait until RESET flag = 1 (polling)
        3- clear ISTR register */
    
      /*** CNTR_FRES = 0 ***/
      wInterrupt_Mask = 0;
      
      _SetCNTR(wInterrupt_Mask);
      
      /* Wait until RESET flag = 1 (polling) */
      while((_GetISTR()&ISTR_RESET) == 1);
      
      /*** Clear pending interrupts ***/
      SetISTR(0);
      
      /*** Set interrupt mask ***/
      wInterrupt_Mask = CNTR_RESETM | CNTR_SUSPM | CNTR_WKUPM;
      _SetCNTR(wInterrupt_Mask);
      
      return USB_SUCCESS;
    }
    
    uint32_t USB_SIL_Init(void)
    {
      /* USB interrupts initialization */
      /* clear pending interrupts */
      _SetISTR(0);
      wInterrupt_Mask = IMR_MSK;
      /* set interrupts mask */
      _SetCNTR(wInterrupt_Mask);
      return 0;
    }
    
    void Virtual_Com_Port_init(void)
    {
    
      /* Update the serial number string descriptor with the data from the unique
      ID*/
      Get_SerialNum();
    
      pInformation->Current_Configuration = 0;
    
      /* Connect the device */
      PowerOn();
    
      /* Perform basic device initialization operations */
      USB_SIL_Init();
    
      bDeviceState = UNCONNECTED;
    }

            经过上面的配置,当USB设备端接入主机的时候,设备端的中断处理函数就能响应相应的中断请求,开始建立通信。

    3.2 描述符讲解

            描述符放在“usb_desc.c”和“usb_desc.h”文件中,更多细节参见USB — 初识USB协议(一)。

  • 设备描述符
  •         包含了设备的基本信息,比如USB版本号,端点0的传输大小,VID,PID,设备的类型等。

    const uint8_t Virtual_Com_Port_DeviceDescriptor[] =
    {
        0x12,   /* bLength */
        USB_DEVICE_DESCRIPTOR_TYPE,     /* bDescriptorType */
        0x00,
        0x02,   /* bcdUSB = 2.00 */
        0x02,   /* bDeviceClass: CDC */
        0x02,   /* bDeviceSubClass */
        0x02,   /* bDeviceProtocol */
        0x40,   /* bMaxPacketSize0 */
        0x83,
        0x04,   /* idVendor = 0x0483 */
        0x40,
        0x57,   /* idProduct = 0x7540 */
        0x00,
        0x02,   /* bcdDevice = 2.00 */
        1,              /* Index of string descriptor describing manufacturer */
        2,              /* Index of string descriptor describing product */
        3,              /* Index of string descriptor describing the device's serial number */
        0x01    /* bNumConfigurations */
    };
  • 配置描述符
  •         配置描述符数组包含了配置描述符、接口描述符、功能描述符,端点描述符等,具体的描述符含义根据每种USB的CLASS不同而含义有所不同,后期小节会陆续讲解。

    const uint8_t Virtual_Com_Port_ConfigDescriptor[] =
    {
        /*Configuration Descriptor*/
        0x09,   /* bLength: Configuration Descriptor size */
        USB_CONFIGURATION_DESCRIPTOR_TYPE,      /* bDescriptorType: Configuration */
        VIRTUAL_COM_PORT_SIZ_CONFIG_DESC,       /* wTotalLength:no of returned bytes */
        0x00,
        0x02,   /* bNumInterfaces: 2 interface */
        0x01,   /* bConfigurationValue: Configuration value */
        0x00,   /* iConfiguration: Index of string descriptor describing the configuration */
        0xC0,   /* bmAttributes: self powered */
        0x32,   /* MaxPower 0 mA */
        /*Interface Descriptor*/
        0x09,   /* bLength: Interface Descriptor size */
        USB_INTERFACE_DESCRIPTOR_TYPE,  /* bDescriptorType: Interface */
        /* Interface descriptor type */
        0x00,   /* bInterfaceNumber: Number of Interface */
        0x00,   /* bAlternateSetting: Alternate setting */
        0x01,   /* bNumEndpoints: One endpoints used */
        0x02,   /* bInterfaceClass: Communication Interface Class */
        0x02,   /* bInterfaceSubClass: Abstract Control Model */
        0x01,   /* bInterfaceProtocol: Common AT commands */
        0x00,   /* iInterface: */
        /*Header Functional Descriptor*/
        0x05,   /* bLength: Endpoint Descriptor size */
        0x24,   /* bDescriptorType: CS_INTERFACE */
        0x00,   /* bDescriptorSubtype: Header Func Desc */
        0x10,   /* bcdCDC: spec release number */
        0x01,
        /*Call Management Functional Descriptor*/
        0x05,   /* bFunctionLength */
        0x24,   /* bDescriptorType: CS_INTERFACE */
        0x01,   /* bDescriptorSubtype: Call Management Func Desc */
        0x00,   /* bmCapabilities: D0+D1 */
        0x01,   /* bDataInterface: 1 */
        /*ACM Functional Descriptor*/
        0x04,   /* bFunctionLength */
        0x24,   /* bDescriptorType: CS_INTERFACE */
        0x02,   /* bDescriptorSubtype: Abstract Control Management desc */
        0x02,   /* bmCapabilities */
        /*Union Functional Descriptor*/
        0x05,   /* bFunctionLength */
        0x24,   /* bDescriptorType: CS_INTERFACE */
        0x06,   /* bDescriptorSubtype: Union func desc */
        0x00,   /* bMasterInterface: Communication class interface */
        0x01,   /* bSlaveInterface0: Data Class Interface */
        /*Endpoint 2 Descriptor*/
        0x07,   /* bLength: Endpoint Descriptor size */
        USB_ENDPOINT_DESCRIPTOR_TYPE,   /* bDescriptorType: Endpoint */
        0x82,   /* bEndpointAddress: (IN2) */
        0x03,   /* bmAttributes: Interrupt */
        VIRTUAL_COM_PORT_INT_SIZE,      /* wMaxPacketSize: */
        0x00,
        0xFF,   /* bInterval: */
        /*Data class interface descriptor*/
        0x09,   /* bLength: Endpoint Descriptor size */
        USB_INTERFACE_DESCRIPTOR_TYPE,  /* bDescriptorType: */
        0x01,   /* bInterfaceNumber: Number of Interface */
        0x00,   /* bAlternateSetting: Alternate setting */
        0x02,   /* bNumEndpoints: Two endpoints used */
        0x0A,   /* bInterfaceClass: CDC */
        0x00,   /* bInterfaceSubClass: */
        0x00,   /* bInterfaceProtocol: */
        0x00,   /* iInterface: */
        /*Endpoint 3 Descriptor*/
        0x07,   /* bLength: Endpoint Descriptor size */
        USB_ENDPOINT_DESCRIPTOR_TYPE,   /* bDescriptorType: Endpoint */
        0x03,   /* bEndpointAddress: (OUT3) */
        0x02,   /* bmAttributes: Bulk */
        VIRTUAL_COM_PORT_DATA_SIZE,             /* wMaxPacketSize: */
        0x00,
        0x00,   /* bInterval: ignore for Bulk transfer */
        /*Endpoint 1 Descriptor*/
        0x07,   /* bLength: Endpoint Descriptor size */
        USB_ENDPOINT_DESCRIPTOR_TYPE,   /* bDescriptorType: Endpoint */
        0x81,   /* bEndpointAddress: (IN1) */
        0x02,   /* bmAttributes: Bulk */
        VIRTUAL_COM_PORT_DATA_SIZE,             /* wMaxPacketSize: */
        0x00,
        0x00    /* bInterval */
    };
  • 其它描述符
  •         其它描述符包括语言描述符,PID描述符、VID描述符,报告描述符等,这些描述符根据不同的厂商,不同的产品以及不同的USB应用而有所不通,我们会在后续的类容对这些描述符做一个简单的讲解。

    3.3 中断处理函数

            USB驱动最关键的部分在于中断处理函数部分,协议的大部分实现都是在中断中实现的,我们这里简单的讲解一下。首先进入USB中断处理函数,进入到USB_Istr()函数中去。

            可以看到USB_Istr()函数实现了USB的中断的所有功能,我们这里只针对几个比较重要的函数进行讲解:

  • 复位函数
  • 正确传输完成函数
  • void USB_Istr(void)
    {
        uint32_t i = 0;
        __IO uint32_t EP[8];
    
        wIstr = _GetISTR();
    
    #if (IMR_MSK & ISTR_SOF)
    
        if (wIstr & ISTR_SOF & wInterrupt_Mask)
        {
            _SetISTR((uint16_t)CLR_SOF);
            bIntPackSOF++;
    
    #ifdef SOF_CALLBACK
            SOF_Callback();
    #endif
        }
    
    #endif
        /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
    
    #if (IMR_MSK & ISTR_CTR)
    
        if (wIstr & ISTR_CTR & wInterrupt_Mask)
        {
            /* servicing of the endpoint correct transfer interrupt */
            /* clear of the CTR flag into the sub */
            CTR_LP();
    #ifdef CTR_CALLBACK
            CTR_Callback();
    #endif
        }
    
    #endif
        /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
    #if (IMR_MSK & ISTR_RESET)
    
        if (wIstr & ISTR_RESET & wInterrupt_Mask)
        {
            _SetISTR((uint16_t)CLR_RESET);
            Device_Property.Reset();
    #ifdef RESET_CALLBACK
            RESET_Callback();
    #endif
        }
    
    #endif
        /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
    #if (IMR_MSK & ISTR_DOVR)
    
        if (wIstr & ISTR_DOVR & wInterrupt_Mask)
        {
            _SetISTR((uint16_t)CLR_DOVR);
    #ifdef DOVR_CALLBACK
            DOVR_Callback();
    #endif
        }
    
    #endif
        /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
    #if (IMR_MSK & ISTR_ERR)
    
        if (wIstr & ISTR_ERR & wInterrupt_Mask)
        {
            _SetISTR((uint16_t)CLR_ERR);
    #ifdef ERR_CALLBACK
            ERR_Callback();
    #endif
        }
    
    #endif
        /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
    #if (IMR_MSK & ISTR_WKUP)
    
        if (wIstr & ISTR_WKUP & wInterrupt_Mask)
        {
            _SetISTR((uint16_t)CLR_WKUP);
            Resume(RESUME_EXTERNAL);
    #ifdef WKUP_CALLBACK
            WKUP_Callback();
    #endif
        }
    
    #endif
        /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
    #if (IMR_MSK & ISTR_SUSP)
    
        if (wIstr & ISTR_SUSP & wInterrupt_Mask)
        {
    
            /* check if SUSPEND is possible */
            if (fSuspendEnabled)
            {
                Suspend();
            }
            else
            {
                /* if not possible then resume after xx ms */
                Resume(RESUME_LATER);
            }
    
            /* clear of the ISTR bit must be done after setting of CNTR_FSUSP */
            _SetISTR((uint16_t)CLR_SUSP);
    #ifdef SUSP_CALLBACK
            SUSP_Callback();
    #endif
        }
    
    #endif
        /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
    
    #if (IMR_MSK & ISTR_ESOF)
    
        if (wIstr & ISTR_ESOF & wInterrupt_Mask)
        {
            /* clear ESOF flag in ISTR */
            _SetISTR((uint16_t)CLR_ESOF);
    
            if ((_GetFNR()&FNR_RXDP) != 0)
            {
                /* increment ESOF counter */
                esof_counter ++;
    
                /* test if we enter in ESOF more than 3 times with FSUSP =0 and RXDP =1=>> possible missing SUSP flag*/
                if ((esof_counter > 3) && ((_GetCNTR()&CNTR_FSUSP) == 0))
                {
                    /* this a sequence to apply a force RESET*/
    
                    /*Store CNTR value */
                    wCNTR = _GetCNTR();
    
                    /*Store endpoints registers status */
                    for (i = 0; i < 8; i++) EP[i] = _GetENDPOINT(i);
    
                    /*apply FRES */
                    wCNTR |= CNTR_FRES;
                    _SetCNTR(wCNTR);
    
                    /*clear FRES*/
                    wCNTR &= ~CNTR_FRES;
                    _SetCNTR(wCNTR);
    
                    /*poll for RESET flag in ISTR*/
                    while ((_GetISTR()&ISTR_RESET) == 0);
    
                    /* clear RESET flag in ISTR */
                    _SetISTR((uint16_t)CLR_RESET);
    
                    /*restore Enpoints*/
                    for (i = 0; i < 8; i++)
                        _SetENDPOINT(i, EP[i]);
    
                    esof_counter = 0;
                }
            }
            else
            {
                esof_counter = 0;
            }
    
            /* resume handling timing is made with ESOFs */
            Resume(RESUME_ESOF); /* request without change of the machine state */
    
    #ifdef ESOF_CALLBACK
            ESOF_Callback();
    #endif
        }
    
    #endif
    } /* USB_Istr */

    3.3.1 复位函数

            复位是在USB识别到设备的时候,主机主动发出的特定序列,当USB设备收到这个特定序列的时候,就会产生中断相应,并进入中断中去处理相应的函数,这里的复位中断处理函数主要是配置端点的属性及端点的大小。

            注意一下宏定义“BTABLE_ADDRESS”的对应的USB中断缓存区地址的偏移,也就是512Byte的RAM空间,宏“ENDP0_RXADDR”等是定义端点的发送和接收地址存放在RAM空间的具体位置,以“BTABLE_ADDRESS”为偏移。

    /* buffer table base address */
    /* buffer table base address */
    #define BTABLE_ADDRESS      (0x00)
    
    /* EP0  */
    /* rx/tx buffer base address */
    #define ENDP0_RXADDR        (0x40)
    #define ENDP0_TXADDR        (0x80)
    
    /* EP1  */
    /* tx buffer base address */
    #define ENDP1_TXADDR        (0xC0)
    #define ENDP2_TXADDR        (0x100)
    #define ENDP3_RXADDR        (0x110)
    
    void Virtual_Com_Port_Reset(void)
    {
      /* Set Virtual_Com_Port DEVICE as not configured */
      pInformation->Current_Configuration = 0;
    
      /* Current Feature initialization */
      pInformation->Current_Feature = Virtual_Com_Port_ConfigDescriptor[7];
    
      /* Set Virtual_Com_Port DEVICE with the default Interface*/
      pInformation->Current_Interface = 0;
    
      SetBTABLE(BTABLE_ADDRESS);
    
      /* Initialize Endpoint 0 */
      SetEPType(ENDP0, EP_CONTROL);
      SetEPTxStatus(ENDP0, EP_TX_STALL);
      SetEPRxAddr(ENDP0, ENDP0_RXADDR);
      SetEPTxAddr(ENDP0, ENDP0_TXADDR);
      Clear_Status_Out(ENDP0);
      SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
      SetEPRxValid(ENDP0);
    
      /* Initialize Endpoint 1 */
      SetEPType(ENDP1, EP_BULK);
      SetEPTxAddr(ENDP1, ENDP1_TXADDR);
      SetEPTxStatus(ENDP1, EP_TX_NAK);
      SetEPRxStatus(ENDP1, EP_RX_DIS);
    
      /* Initialize Endpoint 2 */
      SetEPType(ENDP2, EP_INTERRUPT);
      SetEPTxAddr(ENDP2, ENDP2_TXADDR);
      SetEPRxStatus(ENDP2, EP_RX_DIS);
      SetEPTxStatus(ENDP2, EP_TX_NAK);
    
      /* Initialize Endpoint 3 */
      SetEPType(ENDP3, EP_BULK);
      SetEPRxAddr(ENDP3, ENDP3_RXADDR);
      SetEPRxCount(ENDP3, VIRTUAL_COM_PORT_DATA_SIZE);
      SetEPRxStatus(ENDP3, EP_RX_VALID);
      SetEPTxStatus(ENDP3, EP_TX_DIS);
    
      /* Set this device to response on default address */
      SetDeviceAddress(0);
      
      bDeviceState = ATTACHED;
    }

    3.3.2 正确传输完成函数

            正确传输完成函数分为枚举过程(端点0)的正确传输完成函数和非端点0的正确传输完成函数。

    void CTR_LP(void)
    {
        __IO uint16_t wEPVal = 0;
    
        /* stay in loop while pending interrupts */
        while (((wIstr = _GetISTR()) & ISTR_CTR) != 0)
        {
            /* extract highest priority endpoint number */
            EPindex = (uint8_t)(wIstr & ISTR_EP_ID);
    
            if (EPindex == 0)
            {
                /* Decode and service control endpoint interrupt */
                /* calling related service routine */
                /* (Setup0_Process, In0_Process, Out0_Process) */
    
                /* save RX & TX status */
                /* and set both to NAK */
    
                SaveRState = _GetENDPOINT(ENDP0);
                SaveTState = SaveRState & EPTX_STAT;
                SaveRState &=  EPRX_STAT;
    
                _SetEPRxTxStatus(ENDP0, EP_RX_NAK, EP_TX_NAK);
    
                /* DIR bit = origin of the interrupt */
    
                if ((wIstr & ISTR_DIR) == 0)
                {
                    /* DIR = 0 */
    
                    /* DIR = 0      => IN  int */
                    /* DIR = 0 implies that (EP_CTR_TX = 1) always  */
    
                    _ClearEP_CTR_TX(ENDP0);
                    In0_Process();
    
                    /* before terminate set Tx & Rx status */
    
                    _SetEPRxTxStatus(ENDP0, SaveRState, SaveTState);
                    return;
                }
                else
                {
                    /* DIR = 1 */
    
                    /* DIR = 1 & CTR_RX       => SETUP or OUT int */
                    /* DIR = 1 & (CTR_TX | CTR_RX) => 2 int pending */
    
                    wEPVal = _GetENDPOINT(ENDP0);
    
                    if ((wEPVal & EP_SETUP) != 0)
                    {
                        _ClearEP_CTR_RX(ENDP0); /* SETUP bit kept frozen while CTR_RX = 1 */
                        Setup0_Process();
                        /* before terminate set Tx & Rx status */
    
                        _SetEPRxTxStatus(ENDP0, SaveRState, SaveTState);
                        return;
                    }
    
                    else if ((wEPVal & EP_CTR_RX) != 0)
                    {
                        _ClearEP_CTR_RX(ENDP0);
                        Out0_Process();
                        /* before terminate set Tx & Rx status */
    
                        _SetEPRxTxStatus(ENDP0, SaveRState, SaveTState);
                        return;
                    }
                }
            }/* if(EPindex == 0) */
            else
            {
                /* Decode and service non control endpoints interrupt  */
    
                /* process related endpoint register */
                wEPVal = _GetENDPOINT(EPindex);
    
                if ((wEPVal & EP_CTR_RX) != 0)
                {
                    /* clear int flag */
                    _ClearEP_CTR_RX(EPindex);
    
                    /* call OUT service function */
                    (*pEpInt_OUT[EPindex - 1])();
    
                } /* if((wEPVal & EP_CTR_RX) */
    
                if ((wEPVal & EP_CTR_TX) != 0)
                {
                    /* clear int flag */
                    _ClearEP_CTR_TX(EPindex);
    
                    /* call IN service function */
                    (*pEpInt_IN[EPindex - 1])();
                } /* if((wEPVal & EP_CTR_TX) != 0) */
    
            }/* if(EPindex == 0) else */
    
        }/* while(...) */
    }
    3.3.2.1 枚举过程正确传输完成函数

            枚举过程的正确传输完成函数主要是接收SETUP指令请求,并相应的告知USB主机设备的描述符信息。

            以下代码是处理USB主机的数据,把USB主机的SETUP请求数据存在pInformation结构体中,并在后续的函数进行指令的解析,并通过相对应的SETUP请求发送相对应的数据。

    uint8_t Setup0_Process(void)
    {
    
        union
        {
            uint8_t* b;
            uint16_t* w;
        } pBuf;
    #if defined STM32F303xE || defined STM32F302x8
        uint16_t offset = 0;
        pBuf.b = (uint8_t *)( PMAAddr + _GetEPRxAddr(ENDP0));
    #else
        uint16_t offset = 1;
    
        pBuf.b = PMAAddr + (uint8_t *)(_GetEPRxAddr(ENDP0) * 2); /* *2 for 32 bits addr */
    #endif
    
        if (pInformation->ControlState != PAUSE)
        {
            pInformation->USBbmRequestType = *pBuf.b++; /* bmRequestType */
            pInformation->USBbRequest = *pBuf.b++; /* bRequest */
            pBuf.w += offset;  /* word not accessed because of 32 bits addressing */
            pInformation->USBwValue = ByteSwap(*pBuf.w++); /* wValue */
            pBuf.w += offset;  /* word not accessed because of 32 bits addressing */
            pInformation->USBwIndex  = ByteSwap(*pBuf.w++); /* wIndex */
            pBuf.w += offset;  /* word not accessed because of 32 bits addressing */
            pInformation->USBwLength = *pBuf.w; /* wLength */
        }
    
        pInformation->ControlState = SETTING_UP;
    
        if (pInformation->USBwLength == 0)
        {
            /* Setup with no data stage */
            NoData_Setup0();
        }
        else
        {
            /* Setup with data stage */
            Data_Setup0();
        }
    
        return Post0_Process();
    }
    void Data_Setup0(void)
    {
        uint8_t *(*CopyRoutine)(uint16_t);
        RESULT Result;
        uint32_t Request_No = pInformation->USBbRequest;
    
        uint32_t Related_Endpoint, Reserved;
        uint32_t wOffset, Status;
    
    
    
        CopyRoutine = NULL;
        wOffset = 0;
    
        /*GET DESCRIPTOR*/
        if (Request_No == GET_DESCRIPTOR)
        {
            if (Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT))
            {
                uint8_t wValue1 = pInformation->USBwValue1;
    
                if (wValue1 == DEVICE_DESCRIPTOR)
                {
                    CopyRoutine = pProperty->GetDeviceDescriptor;
                }
    
    #ifdef LPM_ENABLED
                else if (wValue1 == DEVICE_BOS_DESCRIPTOR)
                {
                    CopyRoutine = pProperty->GetBosDescriptor;
                }
    
    #endif
                else if (wValue1 == CONFIG_DESCRIPTOR)
                {
                    CopyRoutine = pProperty->GetConfigDescriptor;
                }
                else if (wValue1 == STRING_DESCRIPTOR)
                {
                    CopyRoutine = pProperty->GetStringDescriptor;
                }  /* End of GET_DESCRIPTOR */
            }
        }
    
        /*GET STATUS*/
        else if ((Request_No == GET_STATUS) && (pInformation->USBwValue == 0)
                 && (pInformation->USBwLength == 0x0002)
                 && (pInformation->USBwIndex1 == 0))
        {
            /* GET STATUS for Device*/
            if ((Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT))
                    && (pInformation->USBwIndex == 0))
            {
                CopyRoutine = Standard_GetStatus;
            }
    
            /* GET STATUS for Interface*/
            else if (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))
            {
                if (((*pProperty->Class_Get_Interface_Setting)(pInformation->USBwIndex0, 0) == USB_SUCCESS)
                        && (pInformation->Current_Configuration != 0))
                {
                    CopyRoutine = Standard_GetStatus;
                }
            }
    
            /* GET STATUS for EndPoint*/
            else if (Type_Recipient == (STANDARD_REQUEST | ENDPOINT_RECIPIENT))
            {
                Related_Endpoint = (pInformation->USBwIndex0 & 0x0f);
                Reserved = pInformation->USBwIndex0 & 0x70;
    
                if (ValBit(pInformation->USBwIndex0, 7))
                {
                    /*Get Status of endpoint & stall the request if the related_ENdpoint
                    is Disabled*/
                    Status = _GetEPTxStatus(Related_Endpoint);
                }
                else
                {
                    Status = _GetEPRxStatus(Related_Endpoint);
                }
    
                if ((Related_Endpoint < Device_Table.Total_Endpoint) && (Reserved == 0)
                        && (Status != 0))
                {
                    CopyRoutine = Standard_GetStatus;
                }
            }
    
        }
    
        /*GET CONFIGURATION*/
        else if (Request_No == GET_CONFIGURATION)
        {
            if (Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT))
            {
                CopyRoutine = Standard_GetConfiguration;
            }
        }
        /*GET INTERFACE*/
        else if (Request_No == GET_INTERFACE)
        {
            if ((Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))
                    && (pInformation->Current_Configuration != 0) && (pInformation->USBwValue == 0)
                    && (pInformation->USBwIndex1 == 0) && (pInformation->USBwLength == 0x0001)
                    && ((*pProperty->Class_Get_Interface_Setting)(pInformation->USBwIndex0, 0) == USB_SUCCESS))
            {
                CopyRoutine = Standard_GetInterface;
            }
    
        }
    
        if (CopyRoutine)
        {
            pInformation->Ctrl_Info.Usb_wOffset = wOffset;
            pInformation->Ctrl_Info.CopyData = CopyRoutine;
            /* sb in the original the cast to word was directly */
            /* now the cast is made step by step */
            (*CopyRoutine)(0);
            Result = USB_SUCCESS;
        }
        else
        {
            Result = (*pProperty->Class_Data_Setup)(pInformation->USBbRequest);
    
            if (Result == USB_NOT_READY)
            {
                pInformation->ControlState = PAUSE;
                return;
            }
        }
    
        if (pInformation->Ctrl_Info.Usb_wLength == 0xFFFF)
        {
            /* Data is not ready, wait it */
            pInformation->ControlState = PAUSE;
            return;
        }
    
        if ((Result == USB_UNSUPPORT) || (pInformation->Ctrl_Info.Usb_wLength == 0))
        {
            /* Unsupported request */
            pInformation->ControlState = STALLED;
            return;
        }
    
    
        if (ValBit(pInformation->USBbmRequestType, 7))
        {
            /* Device ==> Host */
            __IO uint32_t wLength = pInformation->USBwLength;
    
            /* Restrict the data length to be the one host asks for */
            if (pInformation->Ctrl_Info.Usb_wLength > wLength)
            {
                pInformation->Ctrl_Info.Usb_wLength = wLength;
            }
    
            else if (pInformation->Ctrl_Info.Usb_wLength < pInformation->USBwLength)
            {
                if (pInformation->Ctrl_Info.Usb_wLength < pProperty->MaxPacketSize)
                {
                    Data_Mul_MaxPacketSize = FALSE;
                }
                else if ((pInformation->Ctrl_Info.Usb_wLength % pProperty->MaxPacketSize) == 0)
                {
                    Data_Mul_MaxPacketSize = TRUE;
                }
            }
    
            pInformation->Ctrl_Info.PacketSize = pProperty->MaxPacketSize;
            DataStageIn();
        }
        else
        {
            pInformation->ControlState = OUT_DATA;
            vSetEPRxStatus(EP_RX_VALID); /* enable for next data reception */
        }
    
        return;
    }
    3.3.2.2 非端点0正确传输完成函数

            非控制端点0正确传输完成函数根据不同的USB应用存在不同的差距,但是主体框架已经完成,只需要用户在特定的框架中填写特定的程序即可。

            比如当前程序只用到端点1的TX和端点3的RX,就只需要在端点1的TX和端点3的RX中编写相应的应用程序,其它端点不必理会。

    /* associated to defined endpoints */
    /*#define  EP1_IN_Callback   NOP_Process*/
    #define  EP2_IN_Callback   NOP_Process
    #define  EP3_IN_Callback   NOP_Process
    #define  EP4_IN_Callback   NOP_Process
    #define  EP5_IN_Callback   NOP_Process
    #define  EP6_IN_Callback   NOP_Process
    #define  EP7_IN_Callback   NOP_Process
    
    #define  EP1_OUT_Callback   NOP_Process
    #define  EP2_OUT_Callback   NOP_Process
    /*#define  EP3_OUT_Callback   NOP_Process*/
    #define  EP4_OUT_Callback   NOP_Process
    #define  EP5_OUT_Callback   NOP_Process
    #define  EP6_OUT_Callback   NOP_Process
    #define  EP7_OUT_Callback   NOP_Process
    
    void (*pEpInt_IN[7])(void) =
    {
        EP1_IN_Callback,
        EP2_IN_Callback,
        EP3_IN_Callback,
        EP4_IN_Callback,
        EP5_IN_Callback,
        EP6_IN_Callback,
        EP7_IN_Callback,
    };
    
    void (*pEpInt_OUT[7])(void) =
    {
        EP1_OUT_Callback,
        EP2_OUT_Callback,
        EP3_OUT_Callback,
        EP4_OUT_Callback,
        EP5_OUT_Callback,
        EP6_OUT_Callback,
        EP7_OUT_Callback,
    };
    
    void EP1_IN_Callback (void)
    {
        packet_sent = 1;
    }
    
    void EP3_OUT_Callback(void)
    {
        packet_receive = 1;
        Receive_length = GetEPRxCount(ENDP3);
        PMAToUserBufferCopy((unsigned char*)Receive_Buffer, ENDP3_RXADDR, Receive_length);
    }

            到这里,本章就基本上讲完了,主要还是从应用的角度去简单的讲解了一下代码,如果读者需要更深入的了解代码的结构,还是需要大家仔细去学习USB的驱动源码的。

    接下来讲解STM32 USB的虚拟串口环回功能实现,敬请期待。。。

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32-FS-USB-Device驱动代码简述(二)

    发表评论