找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2812|回复: 0
打印 上一主题 下一主题
收起左侧

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

[复制链接]
跳转到指定楼层
楼主
ID:105323 发表于 2016-2-12 19:14 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
                       
1、枚举第二步:设置地址
(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()。执行以下代码:

else if (RequestNo ==SET_ADDRESS)

{

Result = USB_SUCCESS;

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

ControlState =WAIT_STATUS_IN;

USB_StatusIn(); //这句话是一个关键,它是一个宏,实际是准备好发送0字节的状态数据包。因为地址设置没有数据过程,建立阶段后直接进入状态阶段,主机发IN令牌包,设备返回0字节数据包,主机再ACK。
它对应的宏是这样的:
#defineUSB_StatusIn() Send0LengthData() //准备发送0字节数据
#defineSend0LengthData() { _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
产生数据接收中断,且端点0的SETUP置位,调用Setup0_Process()函数进行处理。
在Setup0_Process()中,因为主机发送了请求数据8个字节。由调用Data_Setup0()函数进行处理。首先是获取设备描述符的长度,描述符的起始地址,传送的最大字节数,根据这些参数确定本次能够传输的字节数,然后调用DataStageIn()函数进行实际的数据传输操作,设备描述符必须在本次中断中就写入发送缓冲区,因为很快就要进入数据阶段了。
在函数处理的最后:

vSetEPTxStatus(EP_TX_VALID);

USB_StatusOut();
(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[size=12p]Setup0_Process()函数进行处理,取得请求数据后,由于没有数据传输阶段,该函数调用NoData_Setup0()函数进行处理。
设置空闲时一个类特殊请求,其特征码为0x21,2表示类请求而不是标准请求,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))

{

returnJoystick_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;

}

}

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

Result=(*pProperty->Class_Data_Setup)(pInformation->USBbRequest);
调用用户的类特殊实现来获取报告描述符,同时HID类描述符也是通过这种方式取得的。
7、主机从中断端点读取鼠标操作数据
主机会轮询设备,设备数据的准备在主函数中,用Joystick_Send(JoyState())函数来实现。

Mouse_Buffer[1] = X;

Mouse_Buffer[2] = Y;


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


SetEPTxValid(ENDP1);
使能端点1的发送,当主机的IN令牌包来的时候,SIE将数据返回给主机。同时产生 CTR中断。
在中断处理程序中,执行下列代码:

if ((wEPVal & EP_CTR_TX) !=0)

{


_ClearEP_CTR_TX(EPindex);

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

}
这是在函数指针数组中调用函数,跟踪进入:发现这个函数什么也没有做。
经过对程序执行过程的跟踪和分析,我现在对USB设备HID类的工作有了大概的了解,对ST的USB库的工作也有了初步的概念。把所有文件的源代码粗略地浏览了一遍,心里大概有了些底。但现在我还不准备阅读源代码,我先把例程在智林开发板上移植好,再详细的阅读一遍源代码。


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表