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

这篇具有很好参考价值的文章主要介绍了USB -- 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

stm32 usbfs,usb,stm32,嵌入式硬件,单片机

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

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

stm32 usbfs,usb,stm32,嵌入式硬件,单片机

2 STM32-USB-FS设备固件库

2.1 USB应用程序层次结构

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

stm32 usbfs,usb,stm32,嵌入式硬件,单片机

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

  • STM32_USB-FS_Device_Driver:该层管理与USB-FS_ Device外围设备和USB标准协议的直接通信。STM32_USBFS_ Device_Driver符合USB 2.0规范,与标准STM32标准外设库分离。
  • Application Interface layer:该层为用户提供了库核心和最终应用程序之间的完整接口。

        下图显示了USB FS设备库的程序包组织以及所有演示和子文件夹。

stm32 usbfs,usb,stm32,嵌入式硬件,单片机

2.2 USB-FS_Device peripheral interface

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

  • 硬件抽象层
  • 正确传输中断服务例程
  • 数据传输管理

stm32 usbfs,usb,stm32,嵌入式硬件,单片机

2.3 USB-FS-Device_Driver medium layer

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

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

stm32 usbfs,usb,stm32,嵌入式硬件,单片机

2.3 Application interface

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

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

stm32 usbfs,usb,stm32,嵌入式硬件,单片机

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);
}

stm32 usbfs,usb,stm32,嵌入式硬件,单片机

  • 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的虚拟串口环回功能实现,敬请期待。。。文章来源地址https://www.toymoban.com/news/detail-781655.html

到了这里,关于USB -- STM32-FS-USB-Device驱动代码简述(二)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32MP157驱动开发——USB设备驱动

    参考文章:【正点原子】I.MX6U嵌入式Linux驱动开发——Linux USB驱动   由于 USB 协议太过庞大和复杂,所以本节只对 STM32MP157 自带的 USB 驱动进行使能和测试。详细的 USB 接口和协议的介绍,可以参考原子哥的资料《USB2.0 协议中文版.pdf》和《USB3.0 协议中文版.pdf》。   USB 全

    2023年04月14日
    浏览(49)
  • STM32使用HAL库SPI驱动W25Q16 使用FATFS文件系统+USB虚拟U盘

    使用stm32F407驱动W25Q16,使用FATFS文件系统,USB虚拟优盘功能,W25Q16一共512个扇区,其中128作为flash存取相关数据,其他的384个扇区用作虚拟U盘使用 W25Q16.c W25Q16.h user_diskio.c usbd_storage_if.c main.c STORAGE_BLK_NBR 表示扇区数量 STORAGE_BLK_SIZ 表示扇区大小 电脑上的U盘容量跟这两个参数密

    2024年04月28日
    浏览(41)
  • STM32 调试TM7711驱动原理图驱动代码

    本文使用工程代码如下 (1条消息) STM32调试TM7711驱动原理图驱动源代码,参考如下博客,有原理图设计资源-CSDN文库 项目选用TM7711,还是很令人吃惊的,主要是有如下几个理由 第一就是便宜 第二精度高 STM32的ADC精度不够,才12bit,TM7711的精度可以,可以提供单通道 24bit 高精度

    2024年01月16日
    浏览(49)
  • STM32标准库开发——PWM驱动代码

    使能定时器二时钟 设置定时器时钟源 配置定时器二的时基单元 初始化定时器二的一二三通道引脚,使用复用推挽输出模式 配置定时器输出比较寄存器 使能定时器二

    2024年01月23日
    浏览(136)
  • stm32驱动NRF24L01_原理+代码解析

    目录 概念 废话篇(24L01简介) 引脚分配 工作模式 通信地址理解(个人疑难点) 原理分析 寄存器赏析 寄存器操作指令  配置寄存器(CONFIG,位置:0X00)  自动应答使能寄存器(EN_AA,0X01) RX地址使能寄存器(EN_RXADDR,0X02)  自动重发设置寄存器(SETUP_RETR,0X04)  射频频

    2024年01月18日
    浏览(43)
  • STM32江科大的oled驱动代码添加显示浮点数

    最近想要用oled显示屏显示一些数据,其中有浮点数据要显示,用的是0.96OLED iic接口的四针。其中驱动代码是江科大(B站做STM32教学的)的,个人认为比较美观和实用,但是没有float类型的显示,虽然也可以放大成整数显示,但还是不够美观,所以自己写了一个函数。 /**   *

    2024年02月04日
    浏览(85)
  • 基于STM32的SYN6288语音播报模块驱动实验(代码开源)

    前言: 本文为手把手教学  SYN6288 语音播报模块的驱动实验,本教程的  MCU  采用 STM32F103ZET6 。通过  CubeMX  软件配置 UART 串口协议驱 SYN6288 模块进行规定的语音播报。考虑到  SYN6288 模块的集成化与智能化很高,所以该模块的使用是极其便利的。( 文末代码开源! ) 硬件

    2024年02月13日
    浏览(46)
  • STM32-PWM驱动无源蜂鸣器播放音乐(附网盘代码)

    一、工作原理: 1.利用STM32的定时器PWM输出通道,驱动蜂鸣器以特定频率发声,实现播放音乐的效果。 2.C调音符与频率对照表: 3.以下为常用的七声音阶频率(Hz): #define  MC         262    Do #define  MD         294    Re #define  ME         330     Mi #define  MF   

    2024年02月07日
    浏览(47)
  • cubemx stm32 pca9685pw模块 16路PWM 可用于舵机驱动 驱动代码

    淘宝链接请点这里 淘宝资料资料: 链接:https://pan.baidu.com/s/1Kda-c7QdZdQ03FBMa0zeRA 提取码:1234 这个模块是 I2C 通信控制 16 路 PWM 的模块。 所有路的 频率 是统一设置的,所以每一路的频率都一样,但是每一路可以设置不同的占空比。 PCA9685的分辨率是12位,即占空比控制时,0-

    2024年02月06日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包