打印
[STM32F1]

【转】 STM32USB的枚举过程简介

[复制链接]
1255|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
人丑没人疼|  楼主 | 2016-12-4 01:20 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
STM32的USB枚举过程介绍

    之前的说明:

    文中大量引用网上资料,在文后已给出资料的引用说明。文件涉及到的USB各种传输包各个位的含义以及USB标准设备请求的含义都没有做说明,推荐看《圈圈教你玩USB》里面有详细的说明

一、枚举前的工作

    系统上电后,程序开始运行,简单介绍一下USB的初始化

    根据STM32的USB库做移植,介绍枚举过程

    SetSystem函数是一些初始化化设置,因为我在之前已经做了初始化的操作,所以把这个函数就删了。

    首先系统执行USB中断设置:USB_Interrupts_Config();//设置中断向量表,设置优先级

    然后执行USB时钟设置:Set_USBClock();//时钟设置,USB使用48M时钟,

    然后执行USB初始化设置:USB_Init();//这个函数比较重要

    在执行USB初始化设置主要进行结构体,与函数指针的配置

[cpp] view plain copy


  • void USB_Init(void)  
  • {  
  •     pInformation = &Device_Info;//当前的连接状态与信息,关于Device_Info的信息,自己go to 去查看  
  •     pInformation->ControlState = 2;//当前的控制状态设置为IN_DATA  
  •     pProperty = &Device_Property;//设备本身支持的属性和方法  
  •     pUser_Standard_Requests =&User_Standard_Requests;//主机请求的实现方法  
  •     /* Initialize devices one by one */  
  •     pProperty->Init();//回调设备的初始化例程  
  • }  

    这里面还调用了pProperty->Init();函数进行初始化,它的函数主体是Joystick_init();函数程序执行到Joystick_init();函数,首先Get_SerialNum();获取STM32内部唯一标识码。然后赋给Joystick_StringSerial,不过这个东西好像并没有什么用,因为后面我们在usb_desc.c文件中给改写了。。
    好,接下来执行PowerOn();函数,这个函数首先把D+的上拉电阻上电(我使用的是神舟IIIstm32开发板,对应的D+上拉电阻控制位为PG11,我在之前的函数中已经进行了端口的初始化,你们不要忘记呀),这样电脑就可以检测到设备了。(集线器报告设备连接状态,并收到主机指令后,会复位 USB总线,这需要一定的时间(这段时间内设备应该准备好处理复位指令)。但是现在设备初始化程序将继续往下进行,因为它还没有使能复位中断)
    接着执行语句

[cpp] view plain copy


  • <span style="font-size:10px;">  /*** CNTR_PWDN = 0 ***/  
  •   wRegVal = CNTR_FRES;  
  •   _SetCNTR(wRegVal);</span>  

    这两句话的含义实际上是使能了USB模块电源,因为上电复位时,CNTR寄存器的断电控制位PNWN位是1,模块是断电的,语句将强制复位USB模块,直至
[cpp] view plain copy


  • wInterrupt_Mask = 0;  
  • _SetCNTR(wInterrupt_Mask);  

    语句清除复位信号,然后终止复位操作,这个过程中因为复位中断允许位CNTR_RESETM没有被使能,所以,不会触发复位中断,而是间接使PDWN=0,模块开始工作。
[cpp] view plain copy


  • wInterrupt_Mask =CNTR_RESETM | CNTR_SUSPM| CNTR_WKUPM;  
  • SetCNTR(wInterrupt_Mask);  


    执行语句后,复位中断,挂起中断,唤醒中断被允许,而此时集线器多半已经开始复位端口了,或者说稍微有限延迟,设备固件还能继续初始化一些部件,但已经不会影响整个工作流程了。那么实际上程序确实直接进入了复位中断。

    程序直接进入了USB_LP_CAN1_RX0_IRQHandler的中断口,执行 USB_Istr();,通过匹配发现中断源为复位中断,程序运行到复位中断部分,开始执行复位程序(这里提一下,判断发生复位中断后,首先清中断位。在其它几个中断源也是先清中断位,但是CTR_LP();函数之前却没有清中断位是为什么呢?在STM32参考手册中是这样说的:端点在成功完成一次传输后, CTR位会被硬件置起,如果USB_CNTR上的相应位也被设置的话,就会产生中断。与端点相关的中断标志和USB_CNTR寄存器的CTRM位无关。这两个中断标志位将一直保持有效,直到应用程序清除了USB_EpnR寄存器中的相关中断挂起位(CTR位是个只读位)。)

Device_Property.Reset();
这个函数指针,指向的函数是Joystick_Reset。我们跳过来继续看。

[cpp] view plain copy


  • voidJoystick_Reset(void)  
  • {   
  •   /* Set Joystick_DEVICE as not configured */  
  •   pInformation->Current_Configuration = 0;  
  •   pInformation->Current_Interface = 0;/*thedefault Interface*/  
  •    
  •    
  •   /* Current Feature initialization */  
  •   pInformation->Current_Feature =Joystick_ConfigDescriptor[7];//供电方式,总线供电,自供电  
  •   SetBTABLE(BTABLE_ADDRESS);//设置包缓冲区地址  
  •   /* Initialize Endpoint 0 */  
  •   SetEPType(ENDP0, EP_CONTROL);//端点0为控制端点  
  •   SetEPTxStatus(ENDP0, EP_TX_STALL);//端点状态为发送无效,也就是主机IN令牌包来的时候,回送一个STALL  
  •   SetEPRxAddr(ENDP0, ENDP0_RXADDR);//设置端点0的描述符表,包括接收缓冲区地址,最大允许接收的字节数、发送缓冲区地址三个量。  
  •   SetEPTxAddr(ENDP0, ENDP0_TXADDR);//发送缓冲区地址  
  •   Clear_Status_Out(ENDP0);///清除EP_KIND的STATUS_OUT位,如果改位被设置, 在控制模式下只对0字节数据包相应。其它的都返回STALL。主要用于控制传输的状态过程  
  •   SetEPRxCount(ENDP0,Device_Property.MaxPacketSize);///接收缓冲区支持64个字节  
  •   SetEPRxValid(ENDP0);//使能端点0的接收,因为很快就要接收SETUP令牌包了  
  •    
  •   /* Initialize Endpoint 1 */  
  •   SetEPType(ENDP1, EP_INTERRUPT);//端点1设为中断端点  
  •   SetEPTxAddr(ENDP1, ENDP1_TXADDR);//设置发送缓冲区地址  
  •   SetEPTxCount(ENDP1, 4);//每次发送4个字节  
  •   SetEPRxStatus(ENDP1, EP_RX_DIS);///接收禁止,只发送 Mouse 信息,而不从主机接收  
  •   SetEPTxStatus(ENDP1, EP_TX_NAK);///现在发送端点还不允许发送数据  
  •    
  •   /* Set this device to response on defaultaddress */  
  •   SetDeviceAddress(0);//址默认为 0.  
  •   bDeviceState = ATTACHED;//接状态改为已经连接,默认地址状态  
  • }  

    这个复位的过程我们用一句话总结就是端点初始化。至此RESET中断处理完成,因为我们也没有设置RESET_CALLBACK,所以也不会有RESET_Callback();,不过它本身也是个空函数。在执行完复位函数后PC指针又回到main函数中来,Joystick_init函数往下执行, USB_SIL_Init();函数中使能所有常用中断前面说道,当开复位中断后,程序进入中断,那么复位中断执行完成以后,真正的枚举的过程(获取描述符)并没有开始。而是有一段空余的时间,这段时间内,程序执行了上面的提到的USB_SIL_Init();直至USB_Init();函数结束。那么真正的USB枚举过程都是在中断中完成。

    通过我调试发现,在完成复位中断后,主机好像并没有发送获取描述符的命令,在这个过程中,系统两次进入挂起中断,然后触发唤醒中断,然后复位中断。我个人的理解是,这段时间因为stm32没有收到sof,所有触发挂起中断,当设备发送获取设备描述符命令时,触发唤醒中断,在唤醒中断中,系统设置恢复正常工作。至于最后的复位请求,我表示我也不知道了。。。(这里需要说一下,在移植的挂起中断处理程序中,针对stm32部分也做了挂起处理,我觉得(实际上是我看以前的前辈们都觉得)这样做没必要。所以,只保留了USB挂起,而对整个stm32的挂起被屏蔽掉了。)

(此时pInformation->ControlState=IN_DATA, bDeviceState=UNCONNECTED)


沙发
人丑没人疼|  楼主 | 2016-12-4 01:24 | 只看该作者

二、枚举过程

    枚举过程就是USB设备与主机通讯获取设备的一系列描述符的过程。我们贴图理解



    据说这是圈圈大神整理的枚举过程图,接下来我们来一步一步的看枚举的过程。整个枚举的过程以我实际调试的结果为准。

1、获取设备描述符



注:packet包是实际传输时的数据包。

    首先是建立连接阶段,在经过漫长的等待以后,主机发出对地址0、端点0发出SETUP令牌包和数据包,然后设备返回ACK包。点0寄存器的CTR_RX被置位为1, ISTR的CTR置位为1, DIR=1, EP_ID=0,表示端点0接收到主机来的请求数据。此时设备已经ACK主机,将触发正确传输完成中断。程序进入CTR_LP();中断,我们来详细看一下

[cpp] view plain copy


  • 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)  
  •     {      
  •              SaveRState = _GetENDPOINT(ENDP0);//USB端点0寄存器  USB_EP0R中的数据  
  •              SaveTState = SaveRState & EPTX_STAT;//获取发送状态码  
  •              SaveRState &=  EPRX_STAT;   
  •    
  •              _SetEPRxTxStatus(ENDP0,EP_RX_NAK,EP_TX_NAK);//将TXRX的状态都改为NAK  
  •    
  •       if ((wIstr & ISTR_DIR) == 0)//判断是IN包  
  •       {  
  •    
  •         _ClearEP_CTR_TX(ENDP0);  
  •         In0_Process();  
  •            _SetEPRxTxStatus(ENDP0,SaveRState,SaveTState);  
  •                       return;  
  •       }  
  •      else//判断是OUT包或SETUP包  
  •       {  
  •         wEPVal = _GetENDPOINT(ENDP0);  
  •          
  •        if ((wEPVal &EP_SETUP) != 0)//判断是SETUP包  
  •         {  
  •          Setup0_Process();  
  •          _SetEPRxTxStatus(ENDP0,SaveRState,SaveTState);  
  •           return;  
  •         }  
  •         else if ((wEPVal & EP_CTR_RX) !=0)//判断是OUT包  
  •         {  
  •           _ClearEP_CTR_RX(ENDP0);  
  •           Out0_Process();   
  • _SetEPRxTxStatus(ENDP0,SaveRState,SaveTState);  
  •           return;  
  •         }  
  •       }  
  •     }/* if(EPindex == 0) */  
  •     else//判断是非0端点包  
  •     {  
  •     }/*if(EPindex == 0) else */  
  •   }/* while(...) */  
  • }  


使用特权

评论回复
板凳
人丑没人疼|  楼主 | 2016-12-4 01:25 | 只看该作者

其实以上这些判断在实际的USB协议中,都在令牌包中都有体现,但是STM32的USB硬件都帮我们分析了,这些分析的结果以寄存器的形式显示可以看到程序跳转执行Setup0_Process();我们跟踪过来继续看它的跟踪过程

[cpp] view plain copy


  • uint8_tSetup0_Process(void)  
  • {  
  •    
  •   union  
  •   {  
  •     uint8_t* b;  
  •     uint16_t* w;  
  •   } pBuf;  
  •   uint16_t offset = 1;  
  •      
  •   pBuf.b = PMAAddr + (uint8_t*)(_GetEPRxAddr(ENDP0) * 2); /* *2 for 32 bits addr 要读取数据的地址*/  
  •    
  •   if (pInformation->ControlState!= PAUSE)  
  •   {  
  •     pInformation->USBbmRequestType =*pBuf.b++; /* bmRequestType */  
  •     pInformation->USBbRequest = *pBuf.b++;/* bRequest */  
  •     pBuf.w += offset;  /* word not accessed because of 32 bitsaddressing */  
  •     pInformation->USBwValue =ByteSwap(*pBuf.w++); /* wValue */  
  •     pBuf.w += offset;  /* word not accessed because of 32 bitsaddressing */  
  •     pInformation->USBwIndex  = ByteSwap(*pBuf.w++); /* wIndex */  
  •     pBuf.w += offset;  /* word not accessed because of 32 bitsaddressing */  
  •     pInformation->USBwLength = *pBuf.w; /*wLength */  
  •   }//将USB标准设备请求赋给对应的结构体  
  •    
  •    
  •   pInformation->ControlState = SETTING_UP;  
  •   if (pInformation->USBwLength == 0)  
  •   {  
  •     /* Setup with no data stage */  
  •     NoData_Setup0();  
  •   }  
  • <span style="color:#ff0000;">else</span>  
  •   {  
  •     /* Setup with data stage */  
  •    Data_Setup0();  
  •   }  
  •   return Post0_Process();  
  • }  

    因为这个标准设备请求是来要求设备返回设备描述符的,需要返回数据,所以程序会跳转到Data_Setup0();执行在这个函数里分析要返回的数据。

[cpp] view plain copy


  • voidData_Setup0(void)  
  • {  
  •   uint32_t Request_No =pInformation->USBbRequest;//获取请求代码  
  •    
  •   /*GET DESCRIPTOR*/  
  •   if (<spanstyle="color:#ff0000;">Request_No ==GET_DESCRIPTOR</span>)//判断为获取设备描述符  
  •   {  
  •     if(<span style="color:#ff0000;">Type_Recipient ==(STANDARD_REQUEST | DEVICE_RECIPIENT)</span>)//标准请求  
  •     {  
  •       uint8_t wValue1 =pInformation->USBwValue1;  
  •       if (<spanstyle="color:#ff0000;">wValue1 ==DEVICE_DESCRIPTOR</span>)//判断是获取设备描述符  
  •       {  
  •         <spanstyle="color:#ff0000;">CopyRoutine =pProperty->GetDeviceDescriptor;//复制数据到CopyRoutine</span>  
  •       }  
  •     }  
  •   }  
  •    
  •   if (<spanstyle="color:#ff0000;">CopyRoutine</span>)//有数据将一些信息写入控制结构体  
  •   {  
  •     pInformation->Ctrl_Info.Usb_wOffset =wOffset;  
  •     pInformation->Ctrl_Info.CopyData =CopyRoutine;  
  •     /* sb in the original the cast to word wasdirectly */  
  •     /* now the cast is made step by step */  
  •     (*CopyRoutine)(0);//这个函数这里调用的目的只是设置了 pInformation中需要写入的描述符的长度  
  •    
  •     Result = USB_SUCCESS;  
  •   }  
  •    
  •   if (<spanstyle="color:#ff0000;">ValBit(pInformation->USBbmRequestType,7)</span>)//判断是从设备到主机  
  •   {  
  •     /* Device ==> Host */  
  •     __IO uint32_t wLength =pInformation->USBwLength;  
  •       
  •     /* Restrict the data length to be the onehost asks for */  
  •     if (pInformation->Ctrl_Info.Usb_wLength> wLength)  
  •     {//设备描述符长度为18  
  •       pInformation->Ctrl_Info.Usb_wLength =wLength;  
  •     }   
  •     pInformation->Ctrl_Info.PacketSize =pProperty->MaxPacketSize;     
  •     <spanstyle="color:#ff0000;">DataStageIn();</span>//完成描述符的输出准备  
  •   }  
  •   return;  
  • }  

    最后的重点落在 DataStageIn();函数上,我们来看一下

[cpp] view plain copy


  • voidDataStageIn(void)  
  • {  
  •   ENDPOINT_INFO *pEPinfo =&pInformation->Ctrl_Info;  
  •   uint32_t save_wLength =pEPinfo->Usb_wLength;//还需要发送多少字节  
  •   uint32_t ControlState =pInformation->ControlState;  
  •   DataBuffer= (*pEPinfo->CopyData)(Length);//取得要发送的数据设备描述符  
  •   UserToPMABufferCopy(DataBuffer,GetEPTxAddr(ENDP0), Length);将设备描述符复制到用户的发送缓冲区  
  •   SetEPTxCount(ENDP0, Length);//设置发送字节数目  
  •    
  •   pEPinfo->Usb_wLength -= Length;//等于0  
  •   pEPinfo->Usb_wOffset += Length;//偏移到18  
  •   vSetEPTxStatus(EP_TX_VALID);///使能端点发送,只要主机的 IN令牌包一来,SIE就会将描述符返回给主机  
  •   USB_StatusOut();/* 这个实际上是使接收也有效,主机可取消 IN  
  • Expect_Status_Out:  
  •     pInformation->ControlState =ControlState;  
  • }  

    这一次的CTR中断就执行完了,就等着,主机再发IN包把数据读回去。

    接下来就是可选的数据传输阶段。主机首先发一个 IN令牌包,由于端点0发送有效, SIE将数据返回主机。主机方返回一个 ACK后,主机发送数据的CTR标志置位, DIR=0, EP_ID=0,表明主机正确收到了用户发过去的描述符。固件程序由此进入IN中断。所以接下来进入用 In0_Process()进行处理。但是此时设备描述符返回成功了。同样的在用In0_Process()函数中,其目的只是期待主机的0状态字节输出了。

[cpp] view plain copy


  • uint8_tIn0_Process(void)  
  • {  
  •   uint32_t ControlState =pInformation->ControlState;  
  •      
  •   if ((ControlState == IN_DATA) || (<spanstyle="color:#ff0000;">ControlState == LAST_IN_DATA</span>))  
  •   {  
  •     <span style="color:#ff0000;">DataStageIn();//此次调用后,当前状态变成WAIT_STATUS_OUT,表明设备等待状态过程,主机输出0字节</span>  
  •     /* ControlState may be changed outside thefunction */  
  •     ControlState =pInformation->ControlState;  
  •   }  
  •   else if (ControlState == WAIT_STATUS_IN)  
  •   {  
  •   }   
  •   else  
  •   {  
  •     ControlState = STALLED;  
  •   }  
  •   pInformation->ControlState = ControlState;  
  •   return Post0_Process();  
  • }  

   


使用特权

评论回复
地板
人丑没人疼|  楼主 | 2016-12-4 01:27 | 只看该作者

  我们进入DataStageIn();函数

[cpp] view plain copy


  • voidDataStageIn(void)  
  • {  
  •   ENDPOINT_INFO *pEPinfo =&pInformation->Ctrl_Info;  
  •   uint32_t save_wLength = pEPinfo->Usb_wLength;  
  •   uint32_t ControlState =pInformation->ControlState;  
  •    
  •   uint8_t *DataBuffer;  
  •   uint32_t Length;  
  •       
  •   if ((save_wLength == 0) && (<spanstyle="color:#ff0000;">ControlState == LAST_IN_DATA</span>))  
  •   {  
  •     if(Data_Mul_MaxPacketSize == TRUE)  
  •     {  
  •       /* No more data to send and empty packet*/  
  •       Send0LengthData();  
  •       ControlState = LAST_IN_DATA;  
  •       Data_Mul_MaxPacketSize = FALSE;  
  •     }  
  •    <spanstyle="color:#ff0000;"> else </span>  
  •     {  
  •       /* No more data to send so STALL the TXStatus*/  
  •       ControlState = WAIT_STATUS_OUT;  
  •       vSetEPTxStatus(EP_TX_STALL);  
  •     }     
  •     <spanstyle="color:#ff0000;">goto Expect_Status_Out;</span>  
  •   }  
  •   Length = pEPinfo->PacketSize;  
  •   ControlState = (save_wLength <= Length) ?LAST_IN_DATA : IN_DATA;  
  •    
  •   if (Length > save_wLength)  
  •   {  
  •     Length = save_wLength;  
  •   }  
  •    
  •   DataBuffer = (*pEPinfo->CopyData)(Length);  
  •   
  •   UserToPMABufferCopy(DataBuffer,GetEPTxAddr(ENDP0), Length);  
  •    
  •   SetEPTxCount(ENDP0, Length);  
  •   pEPinfo->Usb_wLength -= Length;  
  •   pEPinfo->Usb_wOffset += Length;  
  •   vSetEPTxStatus(EP_TX_VALID);  
  •   USB_StatusOut();/* Expect the host to abortthe data IN stage */  
  •    
  • Expect_Status_Out:  
  •     pInformation->ControlState =ControlState;   
  • }  

    主机收到18个字节的描述符后,进入状态事务过程,此过程的主句发送令牌包为OUT,和0字节的数据包,设备需要回一个ACK。中断处理程序会进入Out0_Process()。由于此时状态为WAIT_STATUS_OUT,所以执行以下这段

[cpp] view plain copy


  • <spanstyle="font-size:10px;">uint8_t Out0_Process(void)  
  • {  
  •   uint32_t ControlState =pInformation->ControlState;  
  •    
  •   if ((ControlState == IN_DATA) ||(ControlState == LAST_IN_DATA))  
  •   {  
  •   }  
  •   else if ((ControlState == OUT_DATA) ||(ControlState == LAST_OUT_DATA))  
  •   {  
  •   }  
  •    
  •   else if (<spanstyle="color:#ff0000;">ControlState ==WAIT_STATUS_OUT</span>)  
  •   {  
  •     (*pProperty->Process_Status_OUT)();//空函数什么也没干  
  •    <spanstyle="color:#ff0000;"> ControlState = STALLED;</span>  
  •   }  
  •   /* Unexpect state, STALL the endpoint */  
  •   else  
  •   {  
  •     ControlState = STALLED;  
  •   }  
  •   pInformation->ControlState = ControlState;  
  •    
  •   return Post0_Process();  
  • }</span>  


使用特权

评论回复
5
人丑没人疼|  楼主 | 2016-12-4 01:28 | 只看该作者

2、主机发送复位命令

    获取设备描述符后,主机再一次复位设备。设备又进入初始状态

3、设置地址


    1)重新从复位状态开始
    在第一次获取设备描述符后,程序使端点 0 的发送和接收都无效,状态也设置为STALLED,所以主机先发一个复位,使得端点 0接收有效。 虽然说在 NAK和STALL状态下,端点仍然可以响应和接收 SETUP包。
    2)设置地址的建立阶段:
    主机先发一个 SETUP令牌包,设备端 EP0的 SETUP标志置位。然后主机发了一个OUT 包,共 8个字节,里面包含设置地址的要求(USB标准设备请求)。设备在检验数据后,发一个 ACK握手包。同时 CTR_RX 置位,CTR置位。数据已经保存到 RxADDR 所指向的缓冲区。此时 USB产生数据接收中断。由于 CTR_RX和 SETUP同时置位, 终端处理程序调用 Setup0_Process(),所做的工作仍然是先填充 pInformation结构, 获取请求特征码、 请求代码和数据长度。由于设置地址不会携带数据,所以接下来调用 NoData_Setup0()。执行以下代码:

[cpp] view plain copy


  • void NoData_Setup0(void)  
  • {  
  • RESULT Result = USB_UNSUPPORT;  
  • uint32_t RequestNo = pInformation->USBbRequest;  
  • uint32_t ControlState;  
  •      
  • if (Type_Recipient == (STANDARD_REQUEST |DEVICE_RECIPIENT))  
  • {  
  •     if (RequestNo == SET_CONFIGURATION)  
  •     {  
  •     }  
  •    
  •     /*SET ADDRESS*/  
  •     else if (RequestNo== SET_ADDRESS)  
  •     {  
  •       if ((pInformation->USBwValue0 > 127)|| (pInformation->USBwValue1 != 0)  
  •           || (pInformation->USBwIndex != 0)  
  •           ||(pInformation->Current_Configuration != 0))  
  •         /* Device Address should be 127 orless*/  
  •       {  
  •         ControlState = STALLED;  
  •         goto exit_NoData_Setup0;  
  •       }  
  •      else  
  •       {  
  •        Result =USB_SUCCESS;//说明设置地址没有做任何工作  
  •       }  
  •     }  
  •     /*SET FEATURE for Device*/  
  •     else if (RequestNo == SET_FEATURE)  
  •     {  
  •     }  
  •     /*Clear FEATURE for Device */  
  •     else if (RequestNo == CLEAR_FEATURE)  
  •     {  
  •     }  
  • }  
  •    
  • /* Interface Request*/  
  • else if (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))  
  • {  
  • }  
  •    
  • /* EndPoint Request*/  
  • else if (Type_Recipient == (STANDARD_REQUEST | ENDPOINT_RECIPIENT))  
  • {  
  • }  
  • else  
  • {  
  •     Result = USB_UNSUPPORT;  
  • }  
  • if (Result != USB_SUCCESS)  
  • {   
  • }  
  •    
  • if (Result != USB_SUCCESS)  
  • {  
  • }  
  • ControlState = WAIT_STATUS_IN;/* After nodata stage SETUP */  
  • USB_StatusIn();  
  • exit_NoData_Setup0:  
  •   pInformation->ControlState = ControlState;  
  •   return;  
  • }  

    USB_StatusIn(); //这句话是一个关键,它是一个宏,实际是准备好发送 0 字节的状态数据包。 因为地址设置没有数据过程, 建立阶段后直接进入状态阶段,主机发 IN令牌包,设备返回 0 字节数据包,主机再 ACK。

    它对应的宏是这样的:
#define USB_StatusIn() Send0LengthData() //准备发送 0 字节数据
#define Send0LengthData() { _SetEPTxCount(ENDP0, 0); \
vSetEPTxStatus(EP_TX_VALID); \ //设置发送有效,发送字节数为 0}


使用特权

评论回复
6
robter| | 2016-12-4 07:26 | 只看该作者
很好很好,学习了

使用特权

评论回复
7
lanjackg2003| | 2016-12-4 11:02 | 只看该作者
mark

使用特权

评论回复
8
mintspring| | 2016-12-4 11:35 | 只看该作者
USB的应用从来没做过,看看。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

55

主题

97

帖子

0

粉丝