USB的“JoyStickMouse”例程分析——学习笔记(4)__枚举过程

来源:互联网 发布:PHP可以用mvc框架么 编辑:程序博客网 时间:2024/05/18 03:31

四、USB的“JoyStickMouse”工作过程详细分析(2)

1、枚举第一步:获取设备的描述符

1)先要允许数据传输完成中断;

    从USB_init()开始
  1. PowerOn(); //这句话执行完,主机检测到设备,并且能够响应复位中断了
  2. _SetISTR(0); //清除挂起的中断
  3. wInterupt_Mask = IMR_MSK;
  4. _SetCNTR(wInterrupt_Mask); //设置中断屏蔽
  5. //上面两句将允许所有USB中断
  6. bDeviceState = UNCONNECTED; //设备状态值,当前为未连接状态

2)主机获取描述符;

    主机进入控制传输的第一阶段:建立事务、发送setup令牌包、发送请求数据包、设备发送ACK包。
    主机对地址0、端点0发出SETUP令牌包,首先端点0寄存器的第11位SETUP位置位,表明收到了setup令牌包。
    由于此时端点0数据接收有效,所以接下来主机的请求数据包被SIE保存到端点0描述符表的
RxADDR里面,收到的字节数保存到RxCount里面。
    端点0寄存器的CTR_RX被置位为1,ISTR的CTR置位为1,DIR=1,EP_ID=0,表示端点0
接收到主机来的请求数据。此时设备已经ACK主机,将触发正确传输完成中断,下面进入中断看一看。
  1. //* Function Name : CTR_LP.
  2. //* Description : Low priority Endpoint Correct Transfer interrupt's service routine.
  3. void CTR_LP(void)
  4. {
  5. __IO uint16_t wEPVal = 0;
  6. while (((wIstr = _GetISTR()) & ISTR_CTR) != 0)        /* stay in loop while pending interrupts */
  7. {
  8. EPindex = (uint8_t)(wIstr & ISTR_EP_ID);            /* extract low priority endpoint number :获取指针传输针对的端点号*/
  9. if (EPindex == 0)                                   /*如果是端点0,这里确实是端点0*/
  10. {
  11. /* Decode and service control endpoint interrupt calling related service routine
  12. (Setup0_Process, In0_Process, Out0_Process) */
  13. SaveRState = _GetENDPOINT(ENDP0);               /* save RX & TX status and set both to NAK:保存端点0状态,原本是有效状态 */
  14. SaveTState = SaveRState & EPTX_STAT;
  15. SaveRState &= EPRX_STAT;
  16. _SetEPRxTxStatus(ENDP0,EP_RX_NAK,EP_TX_NAK);    /*在本次数据处理好之前,对主机发来的数据包以NAK回应*/
  17. if ((wIstr & ISTR_DIR) == 0)                      /* DIR bit = origin of the interrupt:如果是IN令牌包,数据被取走*/
  18. {
  19. /* DIR = 0 */
  20. /* DIR = 0 => IN int :DIR = 0时,是输入中断*/
  21. /* DIR = 0 implies that (EP_CTR_TX = 1) always :DIR为0总是意味着EP_CTR_TX为1*/
  22. _ClearEP_CTR_TX(ENDP0);                        /*清除传输完成标志*/
  23. In0_Process();
  24. _SetEPRxTxStatus(ENDP0,SaveRState,SaveTState);  /* before terminate set Tx & Rx status */
  25. return;
  26. }
  27. else                                             /*DIR = 1时,要么是SETUP包,要么是OUT包*/
  28. {
  29. /* DIR = 1 */
  30. /* DIR = 1 & CTR_RX => SETUP or OUT int:DIR = 1时,是输出中断 */
  31. /* DIR = 1 & (CTR_TX | CTR_RX) => 2 int pending */
  32. wEPVal = _GetENDPOINT(ENDP0);                  //获取端点0的状态
  33. if ((wEPVal & EP_SETUP) != 0)                   //我们的程序会执行到这里
  34. {
  35. _ClearEP_CTR_RX(ENDP0);                       /* SETUP bit kept frozen while CTR_RX = 1 :*/
  36. Setup0_Process();                             //主要是调用该程序来处理主机请求
  37. _SetEPRxTxStatus(ENDP0,SaveRState,SaveTState);/* before terminate set Tx & Rx status */
  38. return;
  39. }
  40. else if ((wEPVal & EP_CTR_RX) != 0)             //暂时不执行的代码先删除掉
  41. {
  42. _ClearEP_CTR_RX(ENDP0);                       /* clear int flag:清除接收端点0标志 */
  43. Out0_Process();
  44. _SetEPRxTxStatus(ENDP0,SaveRState,SaveTState);/* before terminate set Tx & Rx status */
  45. return;
  46. }
  47. }
  48. }
  49. ................................//其他端点处理代码
  50. }
  51. }
  1. //* Function Name : CTR_HP.
  2. //* Description : High Priority Endpoint Correct Transfer interrupt's service routine.
  3. void CTR_HP(void)
  4. {
  5. uint32_t wEPVal = 0;
  6. while (((wIstr = _GetISTR()) & ISTR_CTR) != 0)
  7. {
  8. _SetISTR((uint16_t)CLR_CTR);             /* clear CTR flag:首先清除传输完成标志 */
  9. EPindex = (uint8_t)(wIstr & ISTR_EP_ID); /* extract highest priority endpoint number */
  10. wEPVal = _GetENDPOINT(EPindex);          /* process related endpoint register: */
  11. if ((wEPVal & EP_CTR_RX) != 0)
  12. {
  13. _ClearEP_CTR_RX(EPindex);              /* clear int flag */
  14. (*pEpInt_OUT[EPindex-1])();            /* call OUT service function */
  15. }
  16. else if ((wEPVal & EP_CTR_TX) != 0)      /* if((wEPVal & EP_CTR_RX) */
  17. {
  18. _ClearEP_CTR_TX(EPindex);              /* clear int flag */
  19. (*pEpInt_IN[EPindex-1])();             /* call IN service function */
  20. }
  21. }
  22. }

3)Setup0_Process()函数的执行分析;

  1. //* Function Name : Setup0_Process
  2. //* Description : Get the device request data and dispatch to individual process.
  3. //* Return : Post0_Process.
  4. uint8_t Setup0_Process(void)
  5. {
  6. union
  7. {
  8. uint8_t* b;
  9. uint16_t* w;
  10. } pBuf;
  11. uint16_t offset = 1;
  12. pBuf.b = PMAAddr + (uint8_t *)(_GetEPRxAddr(ENDP0) * 2); /* *2 for 32 bits addr :这是取得端点0接收缓冲区的起始地址*/
    1. //PMAAddr是包缓冲区起始地址,_GetEPRxAddr(ENDP0)获得端点0描述符表里的接收缓冲区地址。
    2. //乘以2的原因是,描述符里地址项为16位,使用的是相对偏移。
  13. if (pInformation->ControlState != PAUSE)
  14. {
  15. pInformation->USBbmRequestType = *pBuf.b++;     /* bmRequestType:请求类型,表示方向和接收对象(设备、接口还是端点)此时为80,表明设备到主机。*/
  16. pInformation->USBbRequest = *pBuf.b++;          /* bRequest:请求代码,第一次时应该为6,表明主机要获取设备描述符。 */
  17. pBuf.w += offset;                              /* word not accessed because of 32 bits addressing */
  18. pInformation->USBwValue = ByteSwap(*pBuf.w++); /* wValue */
  19. pBuf.w += offset;                              /* word not accessed because of 32 bits addressing */
  20. pInformation->USBwIndex = ByteSwap(*pBuf.w++); /* wIndex */
  21. pBuf.w += offset;                              /* word not accessed because of 32 bits addressing */
  22. pInformation->USBwLength = *pBuf.w;             /* wLength */
  23. }
  24. pInformation->ControlState = SETTING_UP;

  25. if (pInformation->USBwLength == 0)
  26. {
  27. NoData_Setup0(); /* Setup with no data stage */
  28. }
  29. else
  30. {
  31. Data_Setup0(); /* Setup with data stage:这次是数据传输的,所以有进入该函数 */
  32. }

  33. return Post0_Process();
  34. }
    这个函数执行的时候,主机发来的请求数据包已经存在RxADDR缓冲区了。大部分的标志位已经清除,除了SETUP位,这个位将由下一个令牌包自动清除。

4)Data_Setup0()函数的执行分析;

  1. //* Function Name : Data_Setup0.
  2. //* Description : Proceed the processing of setup request with data stage.
  3. void Data_Setup0(void)
  4. {
  5. uint8_t *(*CopyRoutine)(uint16_t);
  6. RESULT Result;
  7. uint32_t Request_No = pInformation->USBbRequest;
  8. uint32_t Related_Endpoint, Reserved;
  9. uint32_t wOffset, Status;
  10. CopyRoutine = NULL; //这是一个函数指针,由用户提供
  11. wOffset = 0;
  12. if (Request_No == GET_DESCRIPTOR) //如果是获取描述符
  13. {
  14. if (Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT))
  15. {
  16. uint8_t wValue1 = pInformation->USBwValue1;
  17. if (wValue1 == DEVICE_DESCRIPTOR) //如果是获取设备描述符
  18. {
  19. CopyRoutine = pProperty->GetDeviceDescriptor; //获取设备描述符的操作由用户提供
  20. }
  21. else if (wValue1 == CONFIG_DESCRIPTOR) //如果是获取配置描述符
  22. {
  23. CopyRoutine = pProperty->GetConfigDescriptor;
  24. }
  25. else if (wValue1 == STRING_DESCRIPTOR) //如果是获取字符串描述符
  26. {
  27. CopyRoutine = pProperty->GetStringDescriptor;
  28. } /* End of GET_DESCRIPTOR */
  29. }
  30. }
  31. /*GET STATUS*/
  32. else if ((Request_No == GET_STATUS) && (pInformation->USBwValue == 0)
  33. && (pInformation->USBwLength == 0x0002)
  34. && (pInformation->USBwIndex1 == 0))
  35. {
  36. ....
  37. }
  38. /*GET CONFIGURATION*/
  39. else if (Request_No == GET_CONFIGURATION)
  40. {
  41. ....
  42. }
  43. /*GET INTERFACE*/
  44. else if (Request_No == GET_INTERFACE)
  45. {
  46. ....
  47. }
  48. if (CopyRoutine)
  49. {
  50. pInformation->Ctrl_Info.Usb_wOffset = wOffset;
  51. pInformation->Ctrl_Info.CopyData = CopyRoutine;
  52. /* sb in the original the cast to word was directly */
  53. /* now the cast is made step by step */
  54. (*CopyRoutine)(0); //这个函数在这里调用的目的是设置pInformation中需要写入的描述符的长度
  55. Result = USB_SUCCESS;
  56. }
  57. else
  58. {
  59. ....
  60. }
  61. if (pInformation->Ctrl_Info.Usb_wLength == 0xFFFF)
  62. {
  63. ....
  64. }
  65. if ((Result == USB_UNSUPPORT) || (pInformation->Ctrl_Info.Usb_wLength == 0))
  66. {
  67. ....
  68. }
  69. if (ValBit(pInformation->USBbmRequestType, 7)) /* Device ==> Host :此时为80,说明方向是IN*/
  70. {
  71. __IO uint32_t wLength = pInformation->USBwLength; //这个一般是64
  72. if (pInformation->Ctrl_Info.Usb_wLength > wLength) /* Restrict the data length to be the one host asks */
  73. {
  74. pInformation->Ctrl_Info.Usb_wLength = wLength; //设备描述符长度18
  75. }
  76. else if (pInformation->Ctrl_Info.Usb_wLength < pInformation->USBwLength)
  77. {
  78. ......
  79. }
  80. pInformation->Ctrl_Info.PacketSize = pProperty->MaxPacketSize;
  81. DataStageIn(); //调用这个函数实现描述符的输出准备
  82. }
  83. else
  84. {
  85. ....
  86. }
  87. return;
  88. }

5)DataStageIn()函数的执行分析;

  1. //* Function Name : DataStageIn.
  2. //* Description : Data stage of a Control Read Transfer.
  3. void DataStageIn(void)
  4. {
  5. ENDPOINT_INFO *pEPinfo = &pInformation->Ctrl_Info;
  6. uint32_t save_wLength = pEPinfo->Usb_wLength;
  7. uint32_t ControlState = pInformation->ControlState;
  8. uint8_t *DataBuffer;
  9. uint32_t Length;
  10. if ((save_wLength == 0) && (ControlState == LAST_IN_DATA))
  11. {
  12. if(Data_Mul_MaxPacketSize == TRUE)
  13. {
  14. /* No more data to send and empty packet */
  15. Send0LengthData();
  16. ControlState = LAST_IN_DATA;
  17. Data_Mul_MaxPacketSize = FALSE;
  18. }
  19. else
  20. {
  21. /* No more data to send so STALL the TX Status*/
  22. ControlState = WAIT_STATUS_OUT;
  23. vSetEPTxStatus(EP_TX_STALL);
  24. }
  25. goto Expect_Status_Out;
  26. }
  27. Length = pEPinfo->PacketSize;
  28. ControlState = (save_wLength <= Length) ? LAST_IN_DATA : IN_DATA;
  29. if (Length > save_wLength)
  30. {
  31. Length = save_wLength;
  32. }
  33. //以下是主要执行代码:
  34. DataBuffer = (*pEPinfo->CopyData)(Length);                    //取得用户描述符缓冲区的地址,这里共18个字节
  35. UserToPMABufferCopy(DataBuffer, GetEPTxAddr(ENDP0), Length);  //将设备描述符复制到用户的发送缓冲区
  36. SetEPTxCount(ENDP0, Length);                                  //设置发送字节的数目:18
  37. pEPinfo->Usb_wLength -= Length;    //等于0
  38. pEPinfo->Usb_wOffset += Length;    //偏移到18
  39. vSetEPTxStatus(EP_TX_VALID);       //使能端点发送,只要主机的IN令牌包一来,SIE就会将描述符返回给主机
  40. USB_StatusOut();/* Expect the host to abort the data IN stage :使能接收也有效,主机可以取消IN*/
  41. Expect_Status_Out:
  42. pInformation->ControlState = ControlState;
  43. }

6)执行流程返回到CTR_LP(void);

    _SetEPRxStatus(ENDP0, SaveRState);
    _SetEPTxStatus(ENDP0, SaveTState);        //因为vSetEPTxStatus(EP_TX_VALID)实际改变了SaveTState,所以此时端点发送已经使能了。
    return;

7)主机的IN令牌包;

      获取描述符的控制传输进入第二阶段,主机首先发送一个IN令牌包,由于端点0发送有效,SIE将数据返回主机。
     主机方返回一个ACK后,主机发送数据的CTR标志置位,DIR = 0,EP_ID = 0,表明主机正确收到了用户发过去的描述符。固件程序由此进入中断。
    此时是由IN引起的。
    主要是调用In0_Process()完成剩下的工作。 

8)追踪进入函数In0_Process();

    此时实际上设备返回描述符已经成功了。
    这一次还是调用DataStageIn()函数,但是目的只是期待主机的0状态字节输出了。
  1. //* Function Name : In0_Process
  2. //* Description : Process the IN token on all default endpoint.
  3. //* Return : Post0_Process.
  4. uint8_t In0_Process(void)
  5. {
  6. uint32_t ControlState = pInformation->ControlState;
  7. if ((ControlState == IN_DATA) || (ControlState == LAST_IN_DATA))
  8. {
  9. DataStageIn();                             //第一次取设备描述符,只取一次。此次调用后,当前
  10. //状态变为WAIT_STATUS_OUT,表明设备等待状态过程,主机输出0字节
  11. ControlState = pInformation->ControlState; /* ControlState may be changed outside the function */
  12. }
  13. else if (ControlState == WAIT_STATUS_IN)
  14. {
  15. ....
  16. }
  17. else
  18. {
  19. ....
  20. }
  21. pInformation->ControlState = ControlState;
  22. return Post0_Process();                        //返回时调用这个函数,没做什么事
  23. }

9)进入状态过程;

    主机收到18字节描述符后,进入状态事务过程,此时过程的令牌包为OUT,字节数为0,只需要用户返回一个ACK。
    所以中断处理程序会进入Out0_Process()。
  1. //* Function Name : Out0_Process
  2. //* Description : Process the OUT token on all default endpoint.
  3. //* Return : Post0_Process.
  4. uint8_t Out0_Process(void)
  5. {
  6. uint32_t ControlState = pInformation->ControlState;
  7. if ((ControlState == OUT_DATA) || (ControlState == LAST_OUT_DATA))
  8. {
  9. DataStageOut();
  10. ControlState = pInformation->ControlState; /* may be changed outside the function */
  11. }
  12.  //由于此时状态为WAIT_STATUS_OUT,所以执行下面代码
  13. else if (ControlState == WAIT_STATUS_OUT)       
  14. {
  15. (*pProperty->Process_Status_OUT)();    //这个是空函数,什么都不做
  16. ControlState = STALLED;                //状态转为STALLED
  17. }
  18. else if ((ControlState == IN_DATA) || (ControlState == LAST_IN_DATA))
  19. {
  20. /* host aborts the transfer before finish */
  21. ControlState = STALLED;
  22. }
  23. /* Unexpect state, STALL the endpoint */
  24. else
  25. {
  26. ControlState = STALLED;
  27. }
  28. pInformation->ControlState = ControlState;
  29. return Post0_Process();
  30. }
    获取设备描述符后,主机再次复位设备,设备又进入初始状态。

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

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

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

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

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

    主机先发一个SETUP令牌包,设备端EP0的SETUP标志置位。然后主机发了一个OUT包,共8字节,里面包含设置地址的要求。
    设备在检验数据后,发一个ACK握手包。同时CTR_RX置位,CTR置位。数据已经保存到RxADDR指向的缓冲区。此时USB产生数据接收中断。
    由于CTR_RX和SETUP同时置位,终端处理程序调用Setup0_Process(),所做的工作任然是先填充pInformation结构、获取请求特征码、请求代码和数据长度。
    由于设置地址不会携带数据,所以接下来调用NoData_Setup0()。执行以下代码:
  1. //* Function Name : NoData_Setup0.
  2. //* Description : Proceed the processing of setup request without data stage.
  3. void NoData_Setup0(void)
  4. {
  5. RESULT Result = USB_UNSUPPORT;
  6. uint32_t RequestNo = pInformation->USBbRequest;
  7. uint32_t ControlState;
  8. if (Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT))
  9. {
  10. /* Device Request*/
  11. if (RequestNo == SET_CONFIGURATION)        /* SET_CONFIGURATION*/
  12. {
  13. Result = Standard_SetConfiguration();
  14. }
  15. else if (RequestNo == SET_ADDRESS)        /*SET ADDRESS*/
  16. {
  17. if ((pInformation->USBwValue0 > 127) || (pInformation->USBwValue1 != 0)
  18. || (pInformation->USBwIndex != 0)
  19. || (pInformation->Current_Configuration != 0))
  20. /* Device Address should be 127 or less*/
  21. {
  22. ControlState = STALLED;
  23. goto exit_NoData_Setup0;
  24. }
  25. //由于设置地址不会携带数据,执行以下代码
  26. else
  27. {
  28. Result = USB_SUCCESS;
  29. }
  30. }
  31. else if (RequestNo == SET_FEATURE)        /*SET FEATURE for Device*/
  32. {
  33. ......
  34. }
  35. else if (RequestNo == CLEAR_FEATURE)      /*Clear FEATURE for Device */
  36. {
  37. ......
  38. }
  39. }
  40. else if (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))    /* Interface Request*/
  41. {
  42. ....
  43. }
  44. else if (Type_Recipient == (STANDARD_REQUEST | ENDPOINT_RECIPIENT))    /* EndPoint Request*/
  45. {
  46. ....
  47. }
  48. else
  49. {
  50. Result = USB_UNSUPPORT;
  51. }
  52. if (Result != USB_SUCCESS)
  53. {
  54. Result = (*pProperty->Class_NoData_Setup)(RequestNo);
  55. if (Result == USB_NOT_READY)
  56. {
  57. ControlState = PAUSE;
  58. goto exit_NoData_Setup0;
  59. }
  60. }
  61. if (Result != USB_SUCCESS)
  62. {
  63. ControlState = STALLED;
  64. goto exit_NoData_Setup0;
  65. }
  66. ControlState = WAIT_STATUS_IN;/* After no data stage SETUP:说明设置地址没有做任何工作 */
  67. USB_StatusIn();                            
    1. //上面这句话是关键,它是一个宏,实际是准备好发送0字节的状态数据包,因为地址设置没有数据过程,
    2. //建立阶段后直接进入状态阶段,主机发IN令牌包,设备返回0字节数据包,主机再ACK。
    3. //它对应的宏是这样的:
    4. //#define USB_StatusIn() Send0LengthData()    //准备发送0字节数据
    5. //#define Send0LengthData() { _SetEPTxCount(ENDP0, 0); vSetEPTxStatus(EP_TX_VALID);//设置发送地址,发送字节数为0}
  68. exit_NoData_Setup0:
  69. pInformation->ControlState = ControlState;
  70. return;
  71. }

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

    前面把 状态设置为WAIT_STATUS_IN是给IN令牌包的处理提供提示。因为建立阶段结束后,主机接着发一个IN令牌包,设备返回0字节数据后,进入中断。
    本次中断由IN0_Process()函数来处理,追踪进入,它执行以下代码:
  1. else if(ControlState == WAIT_STATUS_IN)
  2. {
  3. if ((pInformation->USBbRequest == SET_ADDRESS) && (Type_Recipient==(STANDARD_REQUEST|DEVICE_RECIPIENT)))
  4. {
  5. SetDeviceAddress(pInformation->USBwValue0);
  6. pUser_Standard_Requests->User_SetDeviceAddress(); //这个函数就一个赋值语句, bDeviceState = ADDRESSED。
  7. }
  8. (*pProperty->Process_Status_IN)(); //这是一个空函数。
  9. ControlState = STALLED;
  10. }
  执行设置地址操作、采用新地址后,把设备的状态改为 STALLED。而在处理的出口中调用Post0_Process()函数,这个所做的工作是:
  1. //* Function Name : Post0_Process
  2. //* Description : Stall the Endpoint 0 in case of error.
  3. //* Return : - 0 if the control State is in PAUSE; - 1 if not.
  4. uint8_t Post0_Process(void)
  5. {
  6. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
  7. if (pInformation->ControlState == STALLED)    //将端点0的缓冲区大小设置为64字节
  8. {
  9. vSetEPRxStatus(EP_RX_STALL);
  10. vSetEPTxStatus(EP_TX_STALL);                //将端点0的发送和接收都设置为:STALL,这样只接收SETUP令牌包。
  11. }
  12. return (pInformation->ControlState == PAUSE);
  13. }

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

1)上一阶段末尾的状态

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

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

    产生数据接收中断,且端点0的SETUP置位,调用Setup0_Process()函数进行处理。
    在Setup0_Process()中,因为主机发送了请求数据8个字节,由调用Data_Setup()函数进行处理。首先是获取
设备描述符的长度、描述符起始地址、传送的最大字节数,根据这些参数确定本次能够传输的字节数,然后调用
DataSetageIn()函数进行实际的数据传输操作,设备描述符必须在本次中断中就写入发送缓冲区,因为很快就要
进入数据阶段了。
    在函数处理的最后:
    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_Precess(void)函数进行处理,由于在数据阶段的末尾已经设置设备状态为:
WAIT_STATUS_OUT,所以处理函数基本上没做什么,就退出了,并将状态改为STALLED。

4、对配置描述符、字符串描述符获取

    过程进行简单跟踪,过程就不一一叙述了

5、主机设置配置

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

    进入CTR中断,用户调用Setup0_Precess()函数进行处理,取得请求数据后,由于没有数据传输阶段,
该函数调用NoData_Setup()函数进行处理。
    判断为设置配置后,调用Standard_SetInterface()函数将设备状态结构体的当前配置改为主机数据中的
配置参数。同时调用用户的设置配置函数,将设备状态改为“ configured”。
    退出时,将控制传输状态改为:ControlState = WAIT_STATUS_IN,进入状态阶段。设备期待主机的IN
令牌包,返回状态数据。

2)状态阶段:主机发IN令牌,设备返回0。

    Setup0_Process()函数进行处理,取得请求数据后,由于没有数据传输阶段,该函数调用NoData_Setup()函数进行处理。
    设置空闲时一个类特殊请求,其特征码为0x21,2表示类请求而不是标准请求,1表示接收对象是接口而不是设备。
    USB的底层并不支持类特殊请求,它将调用上层函数提供的函数:
  1. if(Result != USB_SUCCESS)
  2. {
  3. Result = (*pProperty->Class_NoData_Setup)(RequestNo);
  4. //这里就是调用用户提供的类特殊请求的处理函数。结果发现用户提供的类特殊请求(针对无数据情况)只支持:
  5. //SET_PROTOCOL。针对有数据情况只支持:GET_PROTOCOL。
  6. if((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) && (RequestNo == SET_PROTOCOL))
  7.   {
  8. return JoyStick_SetProtocol();
  9. }
  10. }

6、主机获取报告描述符

    建立阶段:主机发送SETUP令牌包、请求数据包(DATA0包)、用户发送ACK。
  1. if (Request_No == GET_DESCRIPTOR)
  2. {
  3. if (Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT))
  4. {
  5. uint8_t wValue1 = pInformation->USBwValue1;
  6. if (wValue1 == DEVICE_DESCRIPTOR)
  7. {
  8. CopyRoutine = pProperty->GetDeviceDescriptor;
  9. }
  10. else if (wValue1 == CONFIG_DESCRIPTOR)
  11. {
  12. CopyRoutine = pProperty->GetConfigDescriptor;
  13. }
  14. else if (wValue1 == STRING_DESCRIPTOR)
  15. {
  16. CopyRoutine = pProperty->GetStringDescriptor;
  17. } /* End of GET_DESCRIPTOR */
  18. }
  19. }
    进入CTR中断,获取描述符时一个标准请求,但是报告描述符并不是需要通用实现的,所以在底层函数中没有实现。
跟踪Setup0_Process(void)——进入Data_Setup(void)函数,它按照上面程序处理。
    可见核心程序只支持设备描述符、配置描述符、字符串描述符。
    最终该函数调用:
    Result = (*pProperty->Class_Data_Setup)(pInformation->USBbRequest);
    调用用户的类特殊实现来获取报告描述符,同时HID类描述符也是通过这种方式获取的。

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

    主机会轮询设备,设备数据的准备在主函数中,用Joystick_Sned(JoyState())函数实现。
  1. Mouse_Buffer[1] = x;
  2. Mouse_Buffer[2] = Y;
  3. UserToPMABufferCopy(Mouse_Buffer, GetEPTxAddr(ENDP1), 4);
  4. SetEPTxValid(ENDP1); //使能端点1的发送,当主机的IN令牌包来时,SIE将数据返回给主机,同时产生CTR中断。
    在中断处理程序中,执行下列代码:
  1. if((wEPVal & EP_CTR_TX) != 0)
  2. {
  3. _ClearEP_CTR_TX(EPindex); //clear int flag
  4. (*pEpInt_IN[EPindex - 1])(); //这是在函数指针中调用函数,跟踪进入,发现什么也没做。
  5. }





















0 0