/* Selected interface of current configuration */
u8 Current_AlternateSetting;/* Selected Alternate Setting of current
interface*/
ENDPOINT_INFO Ctrl_Info; //端点信息结构体 }DEVICE_INFO;
最后调用pProperty->Init(),实质就是调用Joystick_init(void)。
在这个函数中,首先获取设备版本,并转换为Unicode存入版本号字符串。
——Get_SerialNum();
设备当前配置置为0。然后调用PowerOn(),这个函数实质上将D+上拉,此时USB设备就能被集线器检测到了。因此分析进入下一个流程。
2、进入设备检测状态
(1)在PowerOn()中执行的情况。
在USB_init()中调用PowerOn(),而它先调用USB_Cable_Config(ENABLE),这个函数实质上将USB连接控制线设置为低电平,然后设备就可以检测到设备了。
当集线器报告设备连接状态,并收到主机指令后,会复位USB总线,这需要一定的时间(这段时间内设备应该准备好处理复位指令)。但是现在设备初始化程序将继续往下进行,因为它还没有使能复位中断。
wRegVal = CNTR_FRES;
_SetCNTR(wRegVal);
//这句话实际上使能了USB模块的电源,因为上电复位时,CNTR寄存器的断电控制为PDWN位是1,模块是断电的。
这句话虽然将强制复位USB模块,但由于复位中断允许位没有使能,不会引起复位中断,而间接上由使PDWN=0,模块开始工作。
_SetCNTR是一个宏,将wRegVal赋值给CNTR寄存器,此时所有的中断被屏蔽。
再接下来两句指令又将清除复位信号。
然后清除所有的状态位。——_SetISTR(0); 接下来是很关键的两句话:
wInterrupt_Mask=CNTR_RESETM| CNTR_SUSPM | CNTR_WKUPM;
_SetCNTR(wInterrupt_Mask);
后面一个语句执行后,复位中断已经被允许,而此时集线器多半已经开始复位端口了。或者说稍微有限延迟,设备固件还能继续初始化一些部件,但已经不会影响整个工作流程了。
所以接下来,分析直接进入复位中断。 (2)复位中断的处理。
当复位中断允许、且总线被集线器复位的时候,固件程序进入USB_LP中断。 中断程序直接调用USB_Istr(void)程序。 接下来讲对中断位进行判断:
if (wIstr & ISTR_RESET & wInterrupt_Mask) {
_SetISTR((u16)CLR_RESET); //先清除复位中断位
Device_Property.Reset();
//进入设备定义的复位过程。实际上是调用JoyStick_Reset()函数进行处理。 }
(3)JoyStick_Reset()函数的处理。 这里将一句句来分析:
void Joystick_Reset(void) {
pInformation->Current_Configuration = 0; //当前配置为0
pInformation->Current_Interface = 0;/当前接口为0
pInformation->Current_Feature = Joystick_ConfigDescriptor[7];
//需要总线供电
SetBTABLE(BTABLE_ADDRESS);//设置包缓冲区地址。
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令牌包后面跟着的数据包了。
SetEPType(ENDP1, EP_INTERRUPT); //端点1为中断端点。
SetEPTxAddr(ENDP1, ENDP1_TXADDR); //设置发送缓冲区地址。
SetEPTxCount(ENDP1, 4); //每次发送四个字节
SetEPRxStatus(ENDP1, EP_RX_DIS);
//接收禁止,只发送Mouse信息,而不从主机接收。
SetEPTxStatus(ENDP1, EP_TX_NAK); //现在发送端点还不允许发送数据。
bDeviceState = ATTACHED;
//连接状态改为已经连接,默认地址状态。
SetDeviceAddress(0); //地址默认为0. }
复位中断执行完成后,开发板的USB接口能够以默认地址对主机来的数据包进行响应了。这个阶段的分析到此结束,下一个阶段就是正式分析代码实现的枚举过程了。
四、USB的“JoyStickMouse”工作过程详细分析 1、枚举第一步:获取设备的描述符 从USB_init()开始
(1)先要允许数据传输完成中断
在poweron()函数后面紧跟着几句话:
PowerOn();
//这句执行完,设备被主机检测到,并且能够响应复位中断了。
_SetISTR(0);
/* clear pending interrupts */
wInterrupt_Mask = IMR_MSK;
_SetCNTR(wInterrupt_Mask); /* set interrupts mask */ //以上这两句话将允许所有的USB中断
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主机,将触发正确传输完成中断,下面就进入中断看一看。
_SetISTR((u16)CLR_CTR); /*首先清除传输完成标志*/
EPindex = (u8)(wIstr & ISTR_EP_ID); //获取数据传输针对的端点号。
if (EPindex == 0)
//如果是端点0,这里的确是端点0
{
SaveRState = _GetEPRxStatus(ENDP0); //保存端点0状态,原本是有效状态。
SaveTState = _GetEPTxStatus(ENDP0);
_SetEPRxStatus(ENDP0, EP_RX_NAK); //在本次数据处理好之前,对主机发来的数据包以NAK回应
_SetEPTxStatus(ENDP0, EP_TX_NAK);
if ((wIstr & ISTR_DIR) == 0) //如果是IN令牌,数据被取走 {
_ClearEP_CTR_TX(ENDP0);
In0_Process();
//调用该程序处理固件数据输出后的工作。
_SetEPRxStatus(ENDP0, SaveRState);
_SetEPTxStatus(ENDP0, SaveTState);
return; }
Else
//DIR=1时,要么是SETUP包,要么是OUT包。 {
//这里先分析SETUP包。
wEPVal = _GetENDPOINT(ENDP0); //获取整个端点0状态
if ((wEPVal & EP_CTR_TX) != 0) //这种情况一般不太可能, {