USB的“Virtual_COM_Port”例程分析--核心文件core.c流程图分析
1.Setup0_Process()

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(); }
2.In0_Process()

uint8_t In0_Process(void) {//处理主机发来的IN包 uint32_t ControlState = pInformation->ControlState; //有数据发送,继续发送数据,或者已经发送完成了 if ((ControlState == IN_DATA) || (ControlState == LAST_IN_DATA))//进入到这里 { DataStageIn();//第一次取设备描述符只取一次 当前的状态变为WAIT_STATUS_OUT //表明设备等待状态过程 主机输出0字节 /* ControlState may be changed outside the function */ ControlState = pInformation->ControlState; } //处于等待发送状态阶段的数据 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;//停止 } else { ControlState = STALLED; } pInformation->ControlState = ControlState;//保存设置状态 return Post0_Process(); //返回时调用这个函数,没做什么事 }
3.Out0_Process()

uint8_t Out0_Process(void) {//处理主机发来的OUT包 uint32_t ControlState = pInformation->ControlState; /* 如果还处于IN_DATA 或 LAST_IN_DATA 状态,那么说明协议出错了, 设置端点控制状态为STALLED ,同时端点状态会在后面设为STALL */ if ((ControlState == IN_DATA) || (ControlState == LAST_IN_DATA)) { /* host aborts the transfer before finish 提前终止传输*/ ControlState = STALLED;//当控制端口在控制传送的数据和状态阶段发送 STALL 时,在收到 SETUP 之前,对该端点的访问都应该返回 STALL。 } /* 如果还处于 OUT_DATA 或 LAST_OUT_DATA 状态那么继续接收 */ else if ((ControlState == OUT_DATA) || (ControlState == LAST_OUT_DATA)) { DataStageOut(); ControlState = pInformation->ControlState; /* may be changed outside the function */ } //由于此时状态为WAIT_STATUS_OUT,所以执行下面代码 else if (ControlState == WAIT_STATUS_OUT) //之前在等待状态包 {/* 程序运行到这里,说明状态包已经发送过来 */ (*pProperty->Process_Status_OUT)();//状态包发送过来后的处理函数,也可以不处理,什么都不做 ControlState = STALLED; //状态变成了终止发送和接受 } /* Unexpect state, STALL the endpoint */ else { ControlState = STALLED;//控制点的状态为停止 } pInformation->ControlState = ControlState; return Post0_Process();//进行实际的状态操作 }
4.Data_Setup0()



/** * Function Name : Data_Setup0. * Description : Proceed the processing of setup request with data stage. 控制过程有数据阶段的处理,该程序在控制传输过程中只进一次 * Input : None. * Output : None. * Return : None. */ void Data_Setup0(void) { uint8_t *(*CopyRoutine)(uint16_t); RESULT Result; uint32_t Request_No = pInformation->USBbRequest; uint32_t Related_Endpoint, Reserved; uint32_t wOffset, Status; CopyRoutine = NULL; //这是一个函数指针,由用户提供 wOffset = 0; /*GET DESCRIPTOR 获得描述符*/ if (Request_No == GET_DESCRIPTOR) { //如果是标准请求并且接收者是设备 if (Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT)) { uint8_t wValue1 = pInformation->USBwValue1;//得到描述符类型 if (wValue1 == DEVICE_DESCRIPTOR) //如果是获取设备描述符 { CopyRoutine = pProperty->GetDeviceDescriptor;//得到设备描述符函数 } #ifdef LPM_ENABLED else if (wValue1 == DEVICE_BOS_DESCRIPTOR) { CopyRoutine = pProperty->GetBosDescriptor; } #endif else if (wValue1 == CONFIG_DESCRIPTOR) { CopyRoutine = pProperty->GetConfigDescriptor;//得到配置描述符函数 } else if (wValue1 == STRING_DESCRIPTOR) { CopyRoutine = pProperty->GetStringDescriptor; //得到字符串描述符函数 } /* End of GET_DESCRIPTOR */ } } /*GET STATUS 请求为得到状态并且值为0,数据长度为2,偏移为0*/ else if ((Request_No == GET_STATUS) && (pInformation->USBwValue == 0) && (pInformation->USBwLength == 0x0002) && (pInformation->USBwIndex1 == 0)) { /* GET STATUS for Device 得到设备的状态*/ if ((Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT)) && (pInformation->USBwIndex == 0)) { CopyRoutine = Standard_GetStatus;//得到状态 } /* GET STATUS for Interface 得到接口的状态*/ else if (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT)) {/* 检查当前类的接口设置设备是否合适,如果合适并且当前的配置不是0 */ if (((*pProperty->Class_Get_Interface_Setting)(pInformation->USBwIndex0, 0) == USB_SUCCESS) && (pInformation->Current_Configuration != 0)) { CopyRoutine = Standard_GetStatus; } } /* GET STATUS for EndPoint 得到端点的状态*/ else if (Type_Recipient == (STANDARD_REQUEST | ENDPOINT_RECIPIENT)) { Related_Endpoint = (pInformation->USBwIndex0 & 0x0f); Reserved = pInformation->USBwIndex0 & 0x70; if (ValBit(pInformation->USBwIndex0, 7)) { /*Get Status of endpoint & stall the request if the related_ENdpoint is Disabled*/ Status = _GetEPTxStatus(Related_Endpoint); } else { Status = _GetEPRxStatus(Related_Endpoint); } if ((Related_Endpoint < Device_Table.Total_Endpoint) && (Reserved == 0) && (Status != 0)) { CopyRoutine = Standard_GetStatus; } } } /*GET CONFIGURATION 得到配置*/ else if (Request_No == GET_CONFIGURATION) { if (Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT)) { CopyRoutine = Standard_GetConfiguration; } } /*GET INTERFACE 得到接口*/ else if (Request_No == GET_INTERFACE) { if ((Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT)) && (pInformation->Current_Configuration != 0) && (pInformation->USBwValue == 0) && (pInformation->USBwIndex1 == 0) && (pInformation->USBwLength == 0x0001) && ((*pProperty->Class_Get_Interface_Setting)(pInformation->USBwIndex0, 0) == USB_SUCCESS)) { CopyRoutine = Standard_GetInterface; } } if (CopyRoutine) //判断是否取得描述符 { pInformation->Ctrl_Info.Usb_wOffset = wOffset; pInformation->Ctrl_Info.CopyData = CopyRoutine;//将函数指针传入到结构体变量中 /* sb in the original the cast to word was directly */ /* now the cast is made step by step */ (*CopyRoutine)(0); //得到发送数据大小 Result = USB_SUCCESS; } else {//之前的请求都不支持,看看是不是类请求 Result = (*pProperty->Class_Data_Setup)(pInformation->USBbRequest); if (Result == USB_NOT_READY) { pInformation->ControlState = PAUSE; return; } } /*0xffff表示数据没有准备好*/ if (pInformation->Ctrl_Info.Usb_wLength == 0xFFFF) { /* Data is not ready, wait it */ pInformation->ControlState = PAUSE;//暂停 return; } /* 设备不支持或者不需要发送数据 */ if ((Result == USB_UNSUPPORT) || (pInformation->Ctrl_Info.Usb_wLength == 0)) { /* Unsupported request */ pInformation->ControlState = STALLED; return; } if (ValBit(pInformation->USBbmRequestType, 7))//D7表示数据传输方向:设备向主机 { /* Device ==> Host :设备到主机*/ __IO uint32_t wLength = pInformation->USBwLength; //要发送的数据 //这个一般是64 /* Restrict the data length to be the one host asks for */ //设备描述符长度18 if (pInformation->Ctrl_Info.Usb_wLength > wLength)//如果实际的数据长度大于请求的数据长度 { pInformation->Ctrl_Info.Usb_wLength = wLength;//那么只发送请求的长度(64字节) } else if (pInformation->Ctrl_Info.Usb_wLength < pInformation->USBwLength)//如果实际的数据长度小于请求的数据长度 { if (pInformation->Ctrl_Info.Usb_wLength < pProperty->MaxPacketSize)//可以一个包发送出去 { Data_Mul_MaxPacketSize = FALSE; } else if ((pInformation->Ctrl_Info.Usb_wLength % pProperty->MaxPacketSize) == 0)//可以整数包发送 { Data_Mul_MaxPacketSize = TRUE;//整包发送 } //还有一种情况:需要多次发送而最后一个包不足 在后面处理 } pInformation->Ctrl_Info.PacketSize = pProperty->MaxPacketSize;//只有这一次确定了控制端点的发送包大小 DataStageIn(); //调用这个函数实现描述符的输出准备 } else//主机向设备 { pInformation->ControlState = OUT_DATA; vSetEPRxStatus(EP_RX_VALID); /* enable for next data reception 使能接收*/ } return; }
5.NoData_Setup0()





/** * Function Name : NoData_Setup0. * Description : Proceed the processing of setup request without data stage. 处理没有数据阶段的建立过程 * Input : None. * Output : None. * Return : None. */ void NoData_Setup0(void) { RESULT Result = USB_UNSUPPORT; uint32_t RequestNo = pInformation->USBbRequest; uint32_t ControlState; /* 标准请求且接收者为设备 */ if (Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT)) { /* Device Request*/ /* SET_CONFIGURATION 设置配置*/ if (RequestNo == SET_CONFIGURATION) { Result = Standard_SetConfiguration(); } /*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; } } /*SET FEATURE for Device 设置设备特性*/ else if (RequestNo == SET_FEATURE) { //特性选择为远程唤醒并且当前的特性为远程唤醒 if ((pInformation->USBwValue0 == DEVICE_REMOTE_WAKEUP) \ && (pInformation->USBwIndex == 0)) { Result = Standard_SetDeviceFeature(); } else { Result = USB_UNSUPPORT; } } /*Clear FEATURE for Device */ else if (RequestNo == CLEAR_FEATURE) { if (pInformation->USBwValue0 == DEVICE_REMOTE_WAKEUP && pInformation->USBwIndex == 0 && ValBit(pInformation->Current_Feature, 5)) { DevRemoteWakeup = 0; Result = Standard_ClearFeature(); } else { Result = USB_UNSUPPORT; } } } /* Interface Request 接口请求*/ else if (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT)) { /*SET INTERFACE 设置特性*/ if (RequestNo == SET_INTERFACE) { Result = Standard_SetInterface();//设置接口 } } /* EndPoint Request 端点请求*/ else if (Type_Recipient == (STANDARD_REQUEST | ENDPOINT_RECIPIENT)) { /*CLEAR FEATURE for EndPoint*/ if (RequestNo == CLEAR_FEATURE) { Result = Standard_ClearFeature(); } /* SET FEATURE for EndPoint*/ else if (RequestNo == SET_FEATURE) { Result = Standard_SetEndPointFeature(); } } else { Result = USB_UNSUPPORT; } //再检查是不是其他情况 if (Result != USB_SUCCESS) { Result = (*pProperty->Class_NoData_Setup)(RequestNo); if (Result == USB_NOT_READY) { ControlState = PAUSE; goto exit_NoData_Setup0; } } if (Result != USB_SUCCESS) {/* 检查了所有可能的情况还是返回 不是USB_SUCCESS */ ControlState = STALLED;//停止 goto exit_NoData_Setup0; } ControlState = WAIT_STATUS_IN;/* After no data stage SETUP :说明设置地址没有做任何工作*/ USB_StatusIn();//发送状态数据 无数据的控制传输需要发送状态数据,表明数据接收完成 //建立阶段后直接进入状态阶段,主机发IN令牌包,设备返回0字节数据包,主机再ACK。 exit_NoData_Setup0: pInformation->ControlState = ControlState; return; }
6.DataStageIn()


/** * Function Name : DataStageIn. * Description : Data stage of a Control Read Transfer. 控制传输的发送数据阶段,该程序在控制传输过程中会多次进入 * Input : None. * Output : None. * Return : None. */ void DataStageIn(void) {/* 有可能数据已经发送完成了,主机没有发送状态数据,而是继续发送了 IN 令牌包,那么设备的状态设为 WAIT_STATUS_OUT,也就是说端点的反应为 NAK */ /* 此时当主机收到 NAK 握手包时就不会再发 IN 令牌包过来了,而是发送OUT令牌包,同时把 0 字节数据发送过来了 */ ENDPOINT_INFO *pEPinfo = &pInformation->Ctrl_Info; //端点信息保存在指针变量中 uint32_t save_wLength = pEPinfo->Usb_wLength; //得到字符的长度 uint32_t ControlState = pInformation->ControlState; //得到当前的状态 uint8_t *DataBuffer; uint32_t Length; /* 如果要发送的数据长度为0并且是最后一次数据,也就是说数据都发送完了 */ if ((save_wLength == 0) && (ControlState == LAST_IN_DATA)) {//程序运行到这里说明数据都发送完了,这里分两种情况:整包发送和非整包发送,两种处理情况不一样 if(Data_Mul_MaxPacketSize == TRUE) //刚好能整包进行发送则需要发送0字节数据表明数据发送结束 { /* No more data to send and empty packet */ Send0LengthData(); //发送空包并储存状态 ControlState = LAST_IN_DATA; //还有一次数据要发送,就是当前的0字节数据 //整包发送完成之后只要发送一次0字节数据就行了, //在SETUP来到之前都不会进入,防止当前的0数据包发送完成后进入 Data_Mul_MaxPacketSize = FALSE; } else //字符的长度比数据包要小 {//数据已经发送完 /* No more data to send so STALL the TX Status*/ ControlState = WAIT_STATUS_OUT;//发送完成了,等待接收状态数据包 vSetEPTxStatus(EP_TX_STALL);//设置为STALL,既然已经发送完了,发送端点就可以停止了 } goto Expect_Status_Out;//因为数据发送完成了,那么直接可以结束了 } Length = pEPinfo->PacketSize;//最大发送字节数//得到数据包大小 64字节 //要发送的数据小于等于最大发送大小,则处于 LAST_IN_DATA 18字节<64字节 ControlState = LAST_IN_DATA ControlState = (save_wLength <= Length) ? LAST_IN_DATA : IN_DATA; if (Length > save_wLength)//如果要发送的数据小于最大包长 { Length = save_wLength;//就按照实际大小发送 } //否则以一个包的最大数据量进行发送 //以下是主要执行代码: DataBuffer = (*pEPinfo->CopyData)(Length); //得到要发送的数据地址(前面已经确定了发送数据的大小),这里共18个字节 UserToPMABufferCopy(DataBuffer, GetEPTxAddr(ENDP0), Length); //将要发送的数据放到发送缓冲区中 SetEPTxCount(ENDP0, Length); //设置发送字节的数目:18 pEPinfo->Usb_wLength -= Length;//更新需要的数据长度//Usb_wLength等于0 pEPinfo->Usb_wOffset += Length;//更新最新包数据偏移//偏移到18 vSetEPTxStatus(EP_TX_VALID);//使能发送端点 只要主机的IN令牌包一来 SIE就会将描述符返回给主机 /* 设置端点为接收有效 */ USB_StatusOut();/* Expect the host to abort the data IN stage :使能接收也有效,主机可以取消IN*/ Expect_Status_Out: pInformation->ControlState = ControlState;//保存控制状态 }
7.DataStageOut()


void DataStageOut(void) { ENDPOINT_INFO *pEPinfo = &pInformation->Ctrl_Info; uint32_t save_rLength; save_rLength = pEPinfo->Usb_rLength;//保存要读的数据长度 if (pEPinfo->CopyData && save_rLength) { uint8_t *Buffer; uint32_t Length; Length = pEPinfo->PacketSize;//得到数据包大小 if (Length > save_rLength) { Length = save_rLength; } Buffer = (*pEPinfo->CopyData)(Length);//得到数据的地址 pEPinfo->Usb_rLength -= Length;//更新需要的数据长度 pEPinfo->Usb_rOffset += Length;//更新最新包数据偏移 PMAToUserBufferCopy(Buffer, GetEPRxAddr(ENDP0), Length);//复制数据到用户数据Buffer } if (pEPinfo->Usb_rLength != 0)//如果还有数据需要接收 { vSetEPRxStatus(EP_RX_VALID);/* re-enable for next data reception使能下一次数据接收 */ SetEPTxCount(ENDP0, 0);//发送0字节数据,确认数据接收成功 vSetEPTxStatus(EP_TX_VALID);/* Expect the host to abort the data OUT stage 希望主机取消数据阶段*/ } /* Set the next State设置下一次阶段*/ if (pEPinfo->Usb_rLength >= pEPinfo->PacketSize) { pInformation->ControlState = OUT_DATA; } else { if (pEPinfo->Usb_rLength > 0)//还有最后一次数据接收 { pInformation->ControlState = LAST_OUT_DATA; } else if (pEPinfo->Usb_rLength == 0)//没有数据接收 { /* USB spec: section 8.5.3.1 Reporting Status Results */ pInformation->ControlState = WAIT_STATUS_IN; (*pProperty->Process_Status_IN)(); if (pInformation->ControlState == STALLED) { /* command failed to complete: 命令未能完成 in this case we should return a STALL */ vSetEPRxStatus(EP_RX_STALL); vSetEPTxStatus(EP_TX_STALL);//将端点0的发送和接收都设置为:STALL } /* command completed successfully: 命令成功完成 send a zero-length packet during status stage */ else USB_StatusIn();//发送状态阶段的数据 0字节 } } }
8.Post0_Process()

/** * Function Name : Post0_Process * Description : Stall the Endpoint 0 in case of error. 在出现错误的情况下暂停端点0 * Input : None. * Output : None. * Return : - 0 if the control State is in PAUSE * - 1 if not. */ 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); }
9.SetDeviceAddress()

/** * Function Name : SetDeviceAddress. * Description : Set the device and all the used Endpoints addresses. 设置设备和所有使用的端点地址 * Input : - Val: device address. * Output : None. * Return : None. */ void SetDeviceAddress(uint8_t Val) { uint32_t i; uint32_t nEP = Device_Table.Total_Endpoint; /* set address in every used endpoint */ for (i = 0; i < nEP; i++)//在每个使用的端点中设置地址 { _SetEPAddress((uint8_t)i, (uint8_t)i); } /* for */ _SetDADDR(Val | DADDR_EF); /* set device address and enable function 设置设备地址和启用功能*/ }

浙公网安备 33010602011771号