USB的“Virtual_COM_Port”例程分析--枚举--设置地址
三.SET_ADDRESS设置地址
(1)重新从复位状态开始;
在第一次获取设备描述符后,程序使端点0的发送和接收都无效,状态也设置为STALLED,所以主机再次复位设备,设备又进入初始状态。

(2) 设置设备地址

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

1.1处理主机发来的SETUP包Setup0_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 处理主机发来的IN包In0_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); }

浙公网安备 33010602011771号