USB的“Virtual_COM_Port”例程分析--枚举--设置地址

三.SET_ADDRESS设置地址

(1)重新从复位状态开始;

在第一次获取设备描述符后,程序使端点0的发送和接收都无效,状态也设置为STALLED,所以主机再次复位设备,设备又进入初始状态。

 

 

 

(2) 设置设备地址

 

 

 

  1. 建立事务:设置地址

主机先发一个SETUP令牌包,设备端EP0SETUP标志置位。然后主机发了一个OUT包,共8字节,里面包含设置地址的要求。设备在检验数据后,发一个ACK握手包。同时CTR_RX置位,CTR置位。数据已经保存到RxADDR指向的缓冲区。此时USB产生数据接收中断。由于CTR_RXSETUP同时置位,终端处理程序调用Setup0_Process(),所做的工作任然是先填充pInformation结构、获取请求特征码、请求代码和数据长度。

    

 

 

1.1处理主机发来的SETUPSetup0_Process()

   设备收到主机发来的SETUP包,先填充pInformation结构、获取SETUP包的数据。根据SETUP包的wLength=0进入NoData_Setup0()中。

 

uint8_t Setup0_Process(void)
{//处理主机发来的Setup包
  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 这是取得端点0接收缓冲区的起始地址*/
    //PMAAddr是包缓冲区起始地址,_GetEPRxAddr(ENDP0)获得端点0描述符表里的接收缓冲区地址。
  //乘以2的原因是,描述符里地址项为16位,使用的是相对偏移
    
#endif
  if (pInformation->ControlState != PAUSE)//如果不处于暂停状态,那么获取主机发送setup包数据
  {
    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 */    //regs.c ByteSwap()
    pBuf.w += offset;  /* word not accessed because of 32 bits addressing */
    pInformation->USBwIndex  = ByteSwap(*pBuf.w++); /* wIndex */   //regs.c ByteSwap()
    pBuf.w += offset;  /* word not accessed because of 32 bits addressing */
    pInformation->USBwLength = *pBuf.w; /* wLength */
  }

  pInformation->ControlState = SETTING_UP;//设置控制状态为 SETTING_UP设置
  if (pInformation->USBwLength == 0)
  {
    /* 不需要向主机发送数据的处理 */
    NoData_Setup0();
  }
  else
  {
    /* 需要向主机发送数据的处理 */
    Data_Setup0(); 
  }
  return Post0_Process();
}

 

1.2不需要向主机发送数据NoData_Setup0()

 

    /*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 or less*/
      {/* 参数检查发现错误 */
        ControlState = STALLED;//控制状态设置为停止状态
        goto exit_NoData_Setup0;//结束
      }
      else//确认地址无误
      {/* 这里只返回成功,表明可以发送状态阶段的数据了,
                但是还不能设置地址,还必须等到状态数据被主机接收到才行 */
        Result = USB_SUCCESS;
      }
    }
  ControlState = WAIT_STATUS_IN;/* After no data stage SETUP :说明设置地址没有做任何工作*/

  USB_StatusIn();//发送状态数据 无数据的控制传输需要发送状态数据,表明数据接收完成
                 //建立阶段后直接进入状态阶段,主机发IN令牌包,设备返回0字节数据包,主机再ACK。

 

 

1.3 发送0长度的数据USB_StatusIn()

 

/* IN令牌包后发送0长度的数据,表明这次数据传输过程结束 */
#define USB_StatusIn() Send0LengthData()

 

2.1状态阶段

    前面把 状态设置为WAIT_STATUS_IN是给IN令牌包的处理提供提示。因为建立阶段结束后,主机接着发一个IN令牌包,设备返回0字节数据后,进入中断。 本次中断由IN0_Process()函数来处理

 

 

 

2.1 处理主机发来的INIn0_Process()

执行设置地址操作、采用新地址后,把设备的状态改为 STALLED。而在处理的出口中调用Post0_Process()函数

 

  //处于等待发送状态阶段的数据
  else if (ControlState == WAIT_STATUS_IN) //设置地址状态阶段进入这个程序
  {/* 请求为设置地址且接收者为设备 */
    if ((pInformation->USBbRequest == SET_ADDRESS) &&
        (Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT)))
    {/* 之前主机把地址发送过来了,但是还没设置地址。现在有了主机的状态数据,就可以开始设置地址了,之后将以此地址和主机进行通信 */
      SetDeviceAddress(pInformation->USBwValue0);//设置使用新的地址
      pUser_Standard_Requests->User_SetDeviceAddress();//这个函数就一个赋值语句,设置设备为地址状态, bDeviceState = ADDRESSED。
    }
    (*pProperty->Process_Status_IN)();//没有处理
    ControlState = STALLED;//停止
  }

 

返回Post0_Process()

2.2 暂停端点的发送和接收Post0_Process()

 

uint8_t Post0_Process(void)
{
   
  SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);//设置最大接收包为64字节

  if (pInformation->ControlState == STALLED) 
  {
    vSetEPRxStatus(EP_RX_STALL); 
    vSetEPTxStatus(EP_TX_STALL);//将端点0的发送和接收都设置为:STALL,这样只接收SETUP令牌包。
  }

  return (pInformation->ControlState == PAUSE);
}

 

posted @ 2021-03-29 14:11  万事兴  阅读(554)  评论(0)    收藏  举报