原创 USB的“JoyStickMouse”例程分析05

2010-4-18 17:42 2954 10 13 分类: MCU/ 嵌入式

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

 


点击看大图


<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />


 


 


上图很好地描述了枚举阶段“获取描述符”和“设置地址”两个阶段主机和设备数据交换的过程。


五、USB的“JoyStickMouse”工作过程详细分析


1、枚举第二步:设置地址


1)重新从复位状态开始


在第一次获取设备描述符后,程序使端点0的发送和接收都无效,状态也设置为STALLED,所以主机先发一个复位,使得端点0接收有效。虽然说在NAKSTALL状态下,端点仍然可以响应和接收SETUP


 


2)设置地址的建立阶段:


主机先发一个SETUP令牌包,设备端EP0SETUP标志置位。然后主机发了一个OUT包,共8个字节,里面包含设置地址的要求。


设备在检验数据后,发一个ACK握手包。同时CTR_RX置位,CTR置位。数据已经保存到RxADDR所指向的缓冲区。此时USB产生数据接收中断


由于CTR_RXSETUP同时置位,终端处理程序调用Setup0_Process(),所做的工作仍然是先填充pInformation结构,获取请求特征码、请求代码和数据长度。


由于设置地址不会携带数据,所以接下来调用NoData_Setup0()执行以下代码:


    else if (RequestNo == SET_ADDRESS)


    {


     Result = USB_SUCCESS;


    }


说明设置地址没有做任何工作。


 


  ControlState = WAIT_STATUS_IN;/* After no data stage SETUP */


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


 


它对应的宏是这样的:


#define USB_StatusIn() Send0LengthData() //准备发送0字节数据


#define Send0LengthData() { _SetEPTxCount(ENDP0, 0); \


    vSetEPTxStatus(EP_TX_VALID); \ //设置发送有效,发送字节数为0


  }


 


3)设置地址的状态阶段:


 


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


本次中断由IN0_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;


  }


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


  SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);  //将端点0的缓冲区大小设置为64字节


  if (pInformation->ControlState == STALLED)


  {


    vSetEPRxStatus(EP_RX_STALL);


    vSetEPTxStatus(EP_TX_STALL);


  }


将端点0的发送和接收都设置为:STALL,这种状态下只接受SETUP令牌包。


2、枚举第三步:从新地址获取设备描述符


1)上一阶段末尾的状态


端点0的发送和接收都设置为:STALL,只接收SETUP令牌包。


2)建立阶段:主机发令牌包、数据包、设备ACK


产生数据接收中断,且端点0SETUP置位,调用Setup0_Process()函数进行处理。


Setup0_Process()中,因为主机发送了请求数据8个字节。由调用Data_Setup0()函数进行处理。首先是获取设备描述符的长度,描述符的起始地址,传送的最大字节数,根据这些参数确定本次能够传输的字节数,然后调用DataStageIn()函数进行实际的数据传输操作,设备描述符必须在本次中断中就写入发送缓冲区,因为很快就要进入数据阶段了。


在函数处理的最后:


  vSetEPTxStatus(EP_TX_VALID);


  USB_StatusOut();/* 本来期待IN令牌包,但用户可以取消数据阶段,一般不会用到 */


3)数据阶段:主机发IN包,设备返回数据,主机ACK


本次操作会产生数据发送完成中断,由In0_Process(void)来处理中断,它也调用DataStageIn()函数来进行处理。


如果数据已经发送完:


      ControlState = WAIT_STATUS_OUT;


      vSetEPTxStatus(EP_TX_STALL);  //转入状态阶段。


有可能的话:


      Send0LengthData();


      ControlState = LAST_IN_DATA;


      Data_Mul_MaxPacketSize = FALSE; //这一次发送0个字节,状态转为最后输入阶段。


否则,继续准备数据,调整剩余字节数、发送指针位置,等待主机的下一个IN令牌包


4)状态阶段:主机发OUT包、0字节包,设备ACK


数据发送完成中断,调用Out0_Process(void)函数进行处理,由于在数据阶段的末尾已经设置设备状态为:WAIT_STATUS_OUT,所以处理函数基本上没有做什么事,就退出了。并将状态设为STALLED


 


3、对配置描述符、字符串描述符获取过程进行简单跟踪,过程就不再一一叙述了。


 


4、主机设置配置。


建立阶段:主机发SETUP包、发请求数据包(DATA0包)、用户ACK


进入CTR中断,用户调用Setup0_Process()函数进行处理,取得请求数据后,由于没有数据传输阶段,该函数调用NoData_Setup0()函数进行处理。


判断为设置配置后,调用Standard_SetInterface()函数将设备状态结构体的当前配置改为主机数据中的配置参数。同时调用用户的设置配置函数,将设备状态改为“configured”。


退出时,将控制传输状态改为:ControlState = WAIT_STATUS_IN,进入状态阶段。设备期待主机的IN令牌包,返回状态数据。


状态阶段:主机发IN令牌、设备返回0字节DATA1、主机ACK


主机ACK之后,设备进入CTR中断,调用函数In0_Process(void)来处理。根据当前控制传输状态,该函数把状态改为“STALLED”,退出时将端点状态改为“STALL”。状态阶段完成。


 


5、主机类特殊请求:设置空闲


建立阶段:主机发SETUP包、发请求数据包(DATA0包)、用户ACK


进入CTR中断,用户调用Setup0_Process()函数进行处理,取得请求数据后,由于没有数据传输阶段,该函数调用NoData_Setup0()函数进行处理。


设置空闲时一个类特殊请求,其特征码为0x212表示类请求而不是标准请求,1表示接收对象是接口而不是设备。


USB的底层并不支持类特殊请求,它将调用上层函数提供的函数:


  if (Result != USB_SUCCESS)


  {


    Result = (*pProperty->Class_NoData_Setup)(RequestNo); //这里就是调用用户提供的类特殊请求的处理函数。结果发现用户提供的类特殊请求(针对无数据情况)只支持SET_PROTOCOL。针对有数据情况只支持:GET_PROTOCOL


  if ((Type_Recipient==(CLASS_REQUEST | INTERFACE_RECIPIENT))


      && (RequestNo == SET_PROTOCOL))


  {


    return Joystick_SetProtocol();


  }


  }


 


6、主机获取报告描述符


建立阶段:主机发SETUP包、发请求数据包(DATA0包)、用户ACK


进入CTR中断,获取描述符是一个标准请求,但是报告描述符并不是需要通用实现的,所以在底层函数中没有实现。跟踪Setup0_Process(void)——进入Data_Setup(void)函数,它是这么处理的:


  if (Request_No == GET_DESCRIPTOR)


  {


    if(Type_Recipient==(STANDARD_REQUEST| EVICE_RECIPIENT))


    {


      u8 wValue1 = pInformation->USBwValue1;


      if (wValue1 == DEVICE_DESCRIPTOR)


      {


        CopyRoutine = pProperty->GetDeviceDescriptor;


      }


      else if (wValue1 == CONFIG_DESCRIPTOR)


      {


        CopyRoutine = pProperty->GetConfigDescriptor;


      }


      else if (wValue1 == STRING_DESCRIPTOR)


      {


        CopyRoutine = pProperty->GetStringDescriptor;


      }  /* End of GET_DESCRIPTOR */


    }


  }


可见核心函数只支持设备描述符、配置描述符以及字符串描述符。最终该函数将调用:


  Result= (*pProperty->Class_Data_Setup)(pInformation->USBbRequest);


调用用户的类特殊实现来获取报告描述符,同时HID类描述符也是通过这种方式取得的。


 


7、主机从中断端点读取鼠标操作数据


主机会轮询设备,设备数据的准备在主函数中,用Joystick_Send(JoyState())函数来实现。


  Mouse_Buffer[1] = X;


  Mouse_Buffer[2] = Y;


  /*copy mouse position info in ENDP1 Tx Packet Memory Area*/


  UserToPMABufferCopy(Mouse_Buffer, GetEPTxAddr(ENDP1), 4);


  /* enable endpoint for transmission */


  SetEPTxValid(ENDP1);


使能端点1的发送,当主机的IN令牌包来的时候,SIE将数据返回给主机。同时产生 CTR中断。


在中断处理程序中,执行下列代码:


      if ((wEPVal & EP_CTR_TX) != 0)


      {


        /* clear int flag */


        _ClearEP_CTR_TX(EPindex);


        (*pEpInt_IN[EPindex-1])();


      } /* if((wEPVal & EP_CTR_TX) != 0) */


这是在函数指针数组中调用函数,跟踪进入:发现这个函数什么也没有做。


 


经过对程序执行过程的跟踪和分析,我现在对USB设备HID类的工作有了大概的了解,对STUSB库的工作也有了初步的概念。把所有文件的源代码粗略地浏览了一遍,心里大概有了些底。但现在我还不准备阅读源代码,我先把例程在智林开发板上移植好,再详细的阅读一遍源代码。


 


 


 

文章评论3条评论)

登录后参与讨论

用户377235 2014-3-13 11:43

真不错

用户376159 2011-9-1 13:57

thanks

tengjingshu_112148725 2010-4-19 09:39

哈哈,我正在看USB的,希望能赶上博主的进度啊,不过博主真是快啊,哈哈
相关推荐阅读
nthq2004 2010-05-08 20:04
USB自定义设备驱动02
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />  本来还想编写应用程序测试一下自定...
nthq2004 2010-05-07 21:35
USB自定义设备驱动01
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />  一、USB设备驱动入门1、学习目...
nthq2004 2010-05-04 21:01
智林开发板上实现自定义的USB HID设备
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />  一、自定义HID设备的相关概念1...
nthq2004 2010-05-01 21:58
U盘例程在智林开发板上的移植
 <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 一、移植前的准备工作1、有哪些操...
nthq2004 2010-04-30 19:19
U盘实现流程跟踪分析02
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />   二、追踪USB大容量设备的实现...
nthq2004 2010-04-27 21:51
U盘实现流程跟踪分析01
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />   一、追踪USB大容量设备的实现...
我要评论
3
10
关闭 站长推荐上一条 /2 下一条