This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
您好!
我已经设法修改了 tivaware usblib、以便成功响应 Microsoft 的操作系统字符串描述符-这是一种非标准的描述符类型。 下面显示了这个 USB 交易交换的屏幕截图。 此描述符交换由 Data0设置数据包中的 EE 表示。
此描述符交换看起来与所有标准描述符交换完全匹配。 请参阅下面的标准设计。
现在、我遇到的问题是、在 windows 发送一个 OS 字符串描述符后、它会发送一个兼容的 ID 描述符。 我以相同的方式对该描述符请求进行响应、响应会失败。 我一直在测试修改 usblib 以进行响应的不同方式、它们似乎都以如下所示的相同方式失败。
我在协议分析器上遇到的错误说明"Invalid PID sequence"、并详细说明"已观察到无效的数据包序列"。
我已经尝试使用以下函数进行响应、但运气不好。 不过、我确实看到"in"数据包的 Data1具有正确的数据。 我正在尝试发送以下描述符响应。
const uint8_t g_pui8MicrosoftCompatibleIDFeatureDesc[] = //uint8_t g_pui8MicrosoftCompatibleIDFeatureDesc[] = { 0x28, 0x00, 0x00, 0x00, // Desc Length 0x00, 0x01, // Version '1.0' 0x04, 0x00, // Compatibility ID descriptor index 0x0004 0x01, // Number of Sections 0x00, 0x00, 0x00, 0x00, // RESERVED 0x00, 0x00, 0x00, // RESERVED 0x00, // Interface Number (Interface #0) 0x01, // RESERVED 0x57, 0x49, 0x4E, 0x55, // (8 bytes) Compatibility ID "WINUSB\0\0" 0x53, 0x42, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x00, // (8 bytes) Sub-Compatible ID (UNUSED) 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x00, // (6 bytes) RESERVED 0x00, 0x00, // };
使用此函数:
USBDCDSendDataEP0(0, g_pui8MicrosoftCompatibleIDFeatureDesc, 40);
目前为了测试、我还尝试了以下方法进行回应。
// // Need to ACK the data on end point 0 without setting last data as there // will be a data phase. // MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); // // // Return the externally specified MS OS string descriptor. // g_psDCDInst[0].pui8EP0Data = (uint8_t *)g_pui8MicrosoftCompatibleIDFeatureDesc; // // The total size of a string descriptor is in byte 0. // g_psDCDInst[0].ui32EP0DataRemain = g_pui8MicrosoftCompatibleIDFeatureDesc[0]; // // Save the total size of the data sent. // g_psDCDInst[0].ui32OUTDataSize = g_pui8MicrosoftCompatibleIDFeatureDesc[0]; USBDEP0StateTx(0);
这些代码片段可在 usbdenum.c 文件的以下函数中找到。
//***************************************************************************** // // This internal function reads a request data packet and dispatches it to // either a standard request handler or the registered device request // callback depending upon the request type. // // \return None. // //***************************************************************************** static void USBDReadAndDispatchRequest(uint32_t ui32Index) { uint32_t ui32Size; tUSBRequest *psRequest; // // Cast the buffer to a request structure. // psRequest = (tUSBRequest *)g_pui8DataBufferIn; // // Set the buffer size. // ui32Size = EP0_MAX_PACKET_SIZE; // // Get the data from the USB controller end point 0. // MAP_USBEndpointDataGet(USB0_BASE, USB_EP_0, g_pui8DataBufferIn, &ui32Size); // // If there was a null setup packet then just return. // if(!ui32Size) { return; } // // See if this is a standard request or not. // if((psRequest->bmRequestType & USB_RTYPE_TYPE_M) != USB_RTYPE_STANDARD) { // // Since this is not a standard request, see if there is // an external handler present. // if(g_ppsDevInfo[0]->psCallbacks->pfnRequestHandler) { // !!RV!! maybe pass entire g_psDCDInst instead of just pvCBData to // get access to EP0 stuff. - doesn't work due to casting but can // add the main g_... pointer to pvCBData, but usbdbulk.c doesn't // know about g_psDCDInst data type so can't use it. //g_psDCDInst[0].pvCBData = (void*)g_psDCDInst; //g_ppsDevInfo[0]->psCallbacks->pfnRequestHandler( // g_psDCDInst[0].pvCBData, // psRequest); //USBDCDSendDataEP0(0, g_pui8MicrosoftCompatibleIDFeatureDesc, 40); //USBDCDStallEP0(0); // // Need to ACK the data on end point 0 without setting last data as there // will be a data phase. // MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); // // // Return the externally specified MS OS string descriptor. // g_psDCDInst[0].pui8EP0Data = (uint8_t *)g_pui8MicrosoftCompatibleIDFeatureDesc; // // The total size of a string descriptor is in byte 0. // g_psDCDInst[0].ui32EP0DataRemain = g_pui8MicrosoftCompatibleIDFeatureDesc[0]; // // Save the total size of the data sent. // g_psDCDInst[0].ui32OUTDataSize = g_pui8MicrosoftCompatibleIDFeatureDesc[0]; USBDEP0StateTx(0); } else { // // If there is no handler then stall this request. // USBDCDStallEP0(0); } } else { // // Assure that the jump table is not out of bounds. // if((psRequest->bRequest < (sizeof(g_psUSBDStdRequests) / sizeof(tStdRequest))) && (g_psUSBDStdRequests[psRequest->bRequest] != 0)) { // // Jump table to the appropriate handler. // g_psUSBDStdRequests[psRequest->bRequest](&g_psDCDInst[0], psRequest); } else { // // If there is no handler then stall this request. // USBDCDStallEP0(0); } } }
我还尝试过使用处理程序/回调方法、但由于这不起作用、我正在简化这种情况、将我需要用来直接响应此描述符的代码添加到非标准描述符的调度处理程序功能调用区域。 有人知道为什么这些数据包/PID 序列不符合处理描述符请求的这些(目前粗略的)代码模块的顺序吗?
对于以下代码、处理操作系统描述符(第一个)似乎很奇怪。 您可以看到、我在这里添加了一条 if 语句、来查找0xEE 值并在底部的字符串处理程序情况中响应该描述符。
//***************************************************************************** // // This function handles the GET_DESCRIPTOR standard USB request. // // \param pvInstance is the USB device controller instance data. // \param psUSBRequest holds the data for this request. // // This function will return most of the descriptors requested by the host // controller. The descriptor specified by \e // pvInstance->psInfo->pui8DeviceDescriptor will be returned when the device // descriptor is requested. If a request for a specific configuration // descriptor is made, then the appropriate descriptor from the \e // g_pConfigDescriptors will be returned. When a request for a string // descriptor is made, the appropriate string from the // \e pvInstance->psInfo->pStringDescriptors will be returned. If the // \e pvInstance->psInfo->psCallbacks->GetDescriptor is specified it will be // called to handle the request. In this case it must call the // USBDCDSendDataEP0() function to send the data to the host controller. If // the callback is not specified, and the descriptor request is not for a // device, configuration, or string descriptor then this function will stall // the request to indicate that the request was not supported by the device. // // \return None. // //***************************************************************************** static void USBDGetDescriptor(void *pvInstance, tUSBRequest *psUSBRequest) { bool bConfig; tDCDInstance *psUSBControl; tDeviceInfo *psDevice; const tConfigHeader *psConfig; const tDeviceDescriptor *psDeviceDesc; uint8_t ui8Index; int32_t i32Index; ASSERT(psUSBRequest != 0); ASSERT(pvInstance != 0); // // Create the device information pointer. // psUSBControl = (tDCDInstance *)pvInstance; psDevice = g_ppsDevInfo[0]; // // Need to ACK the data on end point 0 without setting last data as there // will be a data phase. // MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); // // Assume we are not sending the configuration descriptor until we // determine otherwise. // bConfig = false; // // Which descriptor are we being asked for? // switch(psUSBRequest->wValue >> 8) { // // This request was for a device descriptor. // case USB_DTYPE_DEVICE: { // // Return the externally provided device descriptor. // psUSBControl->pui8EP0Data = (uint8_t *)psDevice->pui8DeviceDescriptor; // // The size of the device descriptor is in the first byte. // psUSBControl->ui32EP0DataRemain = psDevice->pui8DeviceDescriptor[0]; break; } // // This request was for a configuration descriptor. // case USB_DTYPE_CONFIGURATION: { // // Which configuration are we being asked for? // ui8Index = (uint8_t)(psUSBRequest->wValue & 0xFF); // // Is this valid? // psDeviceDesc = (const tDeviceDescriptor *)psDevice->pui8DeviceDescriptor; if(ui8Index >= psDeviceDesc->bNumConfigurations) { // // This is an invalid configuration index. Stall EP0 to // indicate a request error. // USBDCDStallEP0(0); psUSBControl->pui8EP0Data = 0; psUSBControl->ui32EP0DataRemain = 0; } else { // // Return the externally specified configuration descriptor. // psConfig = psDevice->ppsConfigDescriptors[ui8Index]; // // Start by sending data from the beginning of the first // descriptor. // psUSBControl->ui8ConfigSection = 0; psUSBControl->ui16SectionOffset = 0; psUSBControl->pui8EP0Data = (uint8_t *)psConfig->psSections[0]->pui8Data; // // Determine the total size of the configuration descriptor // by counting the sizes of the sections comprising it. // psUSBControl->ui32EP0DataRemain = USBDCDConfigDescGetSize(psConfig); // // Remember that we need to send the configuration descriptor // and which descriptor we need to send. // psUSBControl->ui8ConfigIndex = ui8Index; bConfig = true; } break; } // // This request was for a string descriptor. // case USB_DTYPE_STRING: { // // Determine the correct descriptor index based on the requested // language ID and index. // i32Index = USBDStringIndexFromRequest(psUSBRequest->wIndex, psUSBRequest->wValue & 0xFF); // // If the mapping function returned -1 then stall the request to // indicate that the request was not valid. // if(i32Index == -1) { USBDCDStallEP0(0); break; } // // !!RV!! // Check for microsoft OS string descriptor and if found // handle. Break immediatley after handling. // if(i32Index == 0xEE) { // // // Return the externally specified MS OS string descriptor. // psUSBControl->pui8EP0Data = (uint8_t *)g_pui8MicrosoftOSString; // // The total size of a string descriptor is in byte 0. // psUSBControl->ui32EP0DataRemain = g_pui8MicrosoftOSString[0]; //USBDCDStallEP0(0); break; } // // // Return the externally specified configuration descriptor. // psUSBControl->pui8EP0Data = (uint8_t *)psDevice->ppui8StringDescriptors[i32Index]; // // The total size of a string descriptor is in byte 0. // psUSBControl->ui32EP0DataRemain = psDevice->ppui8StringDescriptors[i32Index][0]; break; } // // Any other request is not handled by the default enumeration handler // so see if it needs to be passed on to another handler. // default: { // // If there is a handler for requests that are not handled then // call it. // if(psDevice->psCallbacks->pfnGetDescriptor) { psDevice->psCallbacks->pfnGetDescriptor(g_psDCDInst[0].pvCBData, psUSBRequest); } else { // // Whatever this was this handler does not understand it so // just stall the request. // USBDCDStallEP0(0); } return; } } // // If this request has data to send, then send it. // if(psUSBControl->pui8EP0Data) { // // If there is more data to send than is requested then just // send the requested amount of data. // if(psUSBControl->ui32EP0DataRemain > psUSBRequest->wLength) { psUSBControl->ui32EP0DataRemain = psUSBRequest->wLength; } // // Now in the transmit data state. Be careful to call the correct // function since we need to handle the configuration descriptor // differently from the others. // if(!bConfig) { USBDEP0StateTx(0); } else { USBDEP0StateTxConfig(0); } } }
所以我在此停留、试图调试这一非标准描述符处理不起作用的问题。 即使是 TI 的文档也说第一个函数应该能够处理这种"我认为"。
非常感谢任何帮助!
尊敬的 Robert:
我希望能够就错误的地方提供更多的指导。 看着你的第97帧和第101帧,我甚至不知道该期待什么。 您是否能够通过 Google 搜索非标准字符串描述符的 USB 分析器中有效的 ID 描述符交换示例? 它不需要是一个 Tiva 器件、而是 显示此器件如何以非标准格式与 MS OS 主机交换字符串和 ID 描述符的任何器件。 我认为这有助于了解器件返回的预期数据和序列。
尊敬的 Charles:
我一直在四处搜寻几天。 这是如此奇怪,因为非标准的"字符串"描述符工作正常,但同样的方法不适用于"非标准"描述符。 看来基本上有两种办法来解决这个问题。
方法1是根据 usblib 用户指南使用此文件:
//***************************************************************************** // //! This function requests transfer of data to the host on endpoint zero. //! //! \param ui32Index is the index of the USB controller which is to be used to //! send the data. //! \param pui8Data is a pointer to the buffer to send via endpoint zero. //! \param ui32Size is the amount of data to send in bytes. //! //! This function handles sending data to the host when a custom command is //! issued or non-standard descriptor has been requested on endpoint zero. If //! the application needs notification when this is complete, //! <tt>psCallbacks->pfnDataSent</tt> in the tDeviceInfo structure must //! contain a valid function pointer. This callback could be used to free up //! the buffer passed into this function in the \e pui8Data parameter. The //! contents of the \e pui8Data buffer must remain unchanged until the //! <tt>pfnDataSent</tt> callback is received. //! //! \return None. // //***************************************************************************** void USBDCDSendDataEP0(uint32_t ui32Index, uint8_t *pui8Data, uint32_t ui32Size) { ASSERT(ui32Index == 0); // // Return the externally provided device descriptor. // g_psDCDInst[0].pui8EP0Data = pui8Data; // // The size of the device descriptor is in the first byte. // g_psDCDInst[0].ui32EP0DataRemain = ui32Size; // // Save the total size of the data sent. // g_psDCDInst[0].ui32OUTDataSize = ui32Size; // // Now in the transmit data state. // USBDEP0StateTx(0); }
方法2是像 TI 在 enum.c 文件中那样、专门以与 TI 处理标准字符串描述符相同的方式使用指针/结构。
函数调用标题:
//***************************************************************************** // // This function handles the GET_DESCRIPTOR standard USB request. // // \param pvInstance is the USB device controller instance data. // \param psUSBRequest holds the data for this request. // // This function will return most of the descriptors requested by the host // controller. The descriptor specified by \e // pvInstance->psInfo->pui8DeviceDescriptor will be returned when the device // descriptor is requested. If a request for a specific configuration // descriptor is made, then the appropriate descriptor from the \e // g_pConfigDescriptors will be returned. When a request for a string // descriptor is made, the appropriate string from the // \e pvInstance->psInfo->pStringDescriptors will be returned. If the // \e pvInstance->psInfo->psCallbacks->GetDescriptor is specified it will be // called to handle the request. In this case it must call the // USBDCDSendDataEP0() function to send the data to the host controller. If // the callback is not specified, and the descriptor request is not for a // device, configuration, or string descriptor then this function will stall // the request to indicate that the request was not supported by the device. // // \return None. // //***************************************************************************** static void USBDGetDescriptor(void *pvInstance, tUSBRequest *psUSBRequest)
平方根
// // This request was for a string descriptor. // case USB_DTYPE_STRING: { // // Determine the correct descriptor index based on the requested // language ID and index. // i32Index = USBDStringIndexFromRequest(psUSBRequest->wIndex, psUSBRequest->wValue & 0xFF); // // If the mapping function returned -1 then stall the request to // indicate that the request was not valid. // if(i32Index == -1) { USBDCDStallEP0(0); break; } // // Return the externally specified configuration descriptor. // psUSBControl->pui8EP0Data = (uint8_t *)psDevice->ppui8StringDescriptors[i32Index]; // // The total size of a string descriptor is in byte 0. // psUSBControl->ui32EP0DataRemain = psDevice->ppui8StringDescriptors[i32Index][0]; break; }
结尾不要忘记实际 TX 功能:
USBDEP0StateTx(0);
是否有任何人可以验证这些是两个好的方法,两个坏的方法或一个好的一个坏的等。? 经过一段时间后、driverlib 似乎很有意义、在这两种情况下、只要浏览 EP0 TX 的"引擎盖下"代码、最终就在同一位置。 此外、协议分析器还会显示没有与其他字符串描述符相同的数据输出事务。
我看到、设置事务和事务中的数据都正确、并且这些事务的 PID 值看起来都正确。 我希望在此时根据 TI usblib 用户指南使用以下函数、至少可以用作基本案例。
USBDCDSendDataEP0(uint32_t ui32Index, uint8_t *pui8Data, uint32_t ui32Size)
是否有人可以将此帖子转发给在 USB 驱动程序代码上工作的人?
尊敬的 Robert:
是否有任何人可以验证这些是两个好的方法,两个坏的方法或一个好的一个坏的等。? 经过一段时间后、driverlib 似乎很有意义、在这两种情况下、只要浏览 EP0 TX 的"引擎盖下"代码、最终就在同一位置。 此外、协议分析器还会显示没有与其他字符串描述符相同的数据输出事务。
我看到、设置事务和事务中的数据都正确、并且这些事务的 PID 值看起来都正确。 我希望在此时根据 TI usblib 用户指南使用以下函数、至少可以用作基本案例。
[/报价]抱歉、我真的不知道哪种方法是首选的。 我建议您尝试使用 方法2 、因为我希望您只需要扩展当前的字符串描述符处理、而不是从头开始。
是否有人可以将此帖子转发至处理 USB 驱动程序代码的页面?遗憾的是、USB 库驱动程序开发人员不再与我们相伴。
[/quote]
嗨、Charles、
那很糟糕、但我明白这个 USB 驱动程序库可能是在很久以前编写的。 实际上我最终弄清楚了发生的事情、看起来 TI 的代码在经过一些基本修改后运转良好。 正如上面所张贴的,我尝试了几种方法,问题不是 driverlib 代码,而是处理 Microsoft 兼容的 id 功能描述符的方法。 事实证明,我的问题的关键是这个描述符处理是两个部分,而不是一个部分。 Microsoft 仅发送描述符的标头请求、然后发送整个内容的请求。 您可以在此处看到有关此方面的更多信息-如果其他人有此问题、我建议下载 Microsoft OS 描述符规范1.0并进行检查。
我确实有一个问题,但现在我已通过枚举,并继续尝试批量转移,我想知道是否有一个"推荐"的方法从你们。 正如您在下面的代码中看到的、这是用于将批量器件数据传输回主机的函数。 这里需要注意的是、没有选择不同端点的选项。
我正在尝试启动并运行所有端点、以便我可以使用多个管道。 配置此器件以支持 Tiva 部件中所有8个 USB 端点后、如何告知驱动程序库使用特定端点进行传输? 请注意、下面的代码已硬编码、以使用 psInst 对象中的端点。
// // Copy the data into the USB endpoint FIFO. // i32Retcode = MAP_USBEndpointDataPut(psInst->ui32USBBase, psInst->ui8INEndpoint, pi8Data, ui32Length);
在整体职能方面:
uint32_t USBDBulkPacketWrite(void *pvBulkDevice, uint8_t *pi8Data, uint32_t ui32Length, bool bLast) { tBulkInstance *psInst; int32_t i32Retcode; ASSERT(pvBulkDevice); // // Get a pointer to the bulk device instance data pointer // psInst = &((tUSBDBulkDevice *)pvBulkDevice)->sPrivateData; // // Can we send the data provided? // if((ui32Length > g_ui16MaxPacketSize) || (psInst->iBulkTxState != eBulkStateIdle)) { // // Either the packet was too big or we are in the middle of sending // another packet. Return 0 to indicate that we can't send this data. // return(0); } // // Copy the data into the USB endpoint FIFO. // i32Retcode = MAP_USBEndpointDataPut(psInst->ui32USBBase, psInst->ui8INEndpoint, pi8Data, ui32Length); // // Did we copy the data successfully? // if(i32Retcode != -1) { // // Remember how many bytes we sent. // psInst->ui16LastTxSize += (uint16_t)ui32Length; // // If this is the last call for this packet, schedule transmission. // if(bLast) { // // Send the packet to the host if we have received all the data we // can expect for this packet. // psInst->iBulkTxState = eBulkStateWaitData; i32Retcode = MAP_USBEndpointDataSend(psInst->ui32USBBase, psInst->ui8INEndpoint, USB_TRANS_IN); } } // // Did an error occur while trying to send the data? // if(i32Retcode != -1) { // // No - tell the caller we sent all the bytes provided. // return(ui32Length); } else { // // Yes - tell the caller we could not send the data. // return(0); } }
还请注意我之前的问题-协议分析器在 PID 序列上出错、因为第一个描述符传输应该只是标头(16字节的标头数据)是整个标头(40字节的数据) 它充当了一种缓冲器超运行。 除了预期的16个字节之外、其他字节被解释为一个新的 PID、并且是错误的。 我想您可以在协议分析器屏幕截图中看到这一点、其中设置事务数据阶段正在请求16个字节的数据、我要发回40个数据。 希望这对某些人有所帮助。
Charles -告诉我您认为使用 usblib 和/或使用不同/多个端点发送数据的"正确"方式、如果我需要修改此发送函数并添加端点参数、或者如果有更干净的方法、我就会缺失。
尊敬的 Robert:
我正在尝试启动和运行所有端点,以便可以使用多个管道。 配置此器件以支持 Tiva 部件中所有8个 USB 端点后、如何告知驱动程序库使用特定端点进行传输? 请注意,下面的代码已硬编码,以使用 psInst 对象中的端点。
我不确定下面的说明是否能澄清您的问题。 似乎是基于说明、主机决定从哪个管道获取数据。 已配置的端点数量应该是已建立枚举的一部分。 我希望 USB 库以某种方式知道哪个端点根据从主机请求的管道/端点来发送数据。
18.6.1.4调度
传输事务的调度由确定、因此设备无法控制传输事务的调度
主机控制器。 USB 控制器可以随时建立传输事务。
USB 控制器等待主机的请求并在主机发出请求时产生中断信号
事务已完成或由于某种错误而终止。 如果主机控制器生成了
请求、当设备控制器未准备就绪时、USB 控制器向发送一个忙响应(NAK)
所有请求、直到它准备就绪。
另请注意,对于我先前的问题,协议分析器在 PID 序列中出错,因为第一个描述符传输应该只是标头(16字节的标头数据),而是整个标头(40字节的数据) 它充当了一种缓冲器超运行。 除了预期的16个字节之外、其他字节被解释为一个新的 PID、并且是错误的。 我想您可以在协议分析器屏幕截图中看到这一点、其中设置事务数据阶段正在请求16个字节的数据、我要发回40个数据。 希望这对某些人有所帮助。[/引号]谢谢! 您应该只返回16个字节作为标头、但实际上您返回40个字节。 很高兴您解决了枚举问题。 我当然会学到一些东西、这肯定会帮助其他人开发自定义的字符串描述符。
嗨、Charles、
感谢您的答复。 是的、这很棘手、并且调试 driverlib 已经是一种体验。 说到这里、您知道我在哪里可以找到如下所示的 ROM_functions 的定义吗? 我正在尝试调试更多的 driverlib 代码(在事务处理中无效)、这是我需要了解的关键函数。 它位于 USB 事件处理程序的 usbdenum.c 中。
MAP_USBIntStatusEndpoint(USB0_BASE);
尊敬的 Robert:
请参阅以下有关如何使用 MAP_和 ROM_的 说明。 基本上、如果 ROM 中提供了该函数、那么该函数将在 ROM 之外运行。 如果不是、它将从在您链接函数时存储在闪存中的 drivelib.lib 运行。 要调试此函数、我建议您 直接调用 USBIntStatusEndpoint、而不是 MAP_USBIntStatusEndpoint。 这样、您将能够单步执行 USBIntStatusEndpoint 的源代码。 如果你使用 MAP_USBIntStatusEndpoint、则它会跳转至 ROM、调试只能通过反汇编窗口完成、因此困难得多。
7.4 ROM 和 MAP TivaWare 前缀
为了更大限度地减少闪存空间、TM4C 微控制器将 TivaWare 的 DriverLib 加载至 ROM 存储器。 然而、ROM 中包含的 DriverLib 是一个旧版本、因此必须使用最新的 TivaWare DriverLib 通过闪存执行任何更新的函数或新函数。 为了更轻松地了解 ROM 中加载的哪些函数是最新的、以及必须从闪存执行哪些函数、TivaWare 包含一个映射文件、用于确定是否使用 ROM 函数或闪存函数。 在此设置中、每个 DriverLib 函数可能都有三个函数调用。 即泛型函数调用、ROM 前缀函数调用(函数以"ROM_"开始)和 MAP 前缀函数调用(函数以"MAP_"开始)。 对于所有 ROM 前缀函数调用、需要 rom.h 头文件、它将从 ROM 存储器映射中选择正确的函数来执行。 如果 ROM 中不存在该函数、则会出现编译器错误、指示该函数不可用。 在某些情况下、在 rom.h 中删除了关联勘误表的旧 ROM 函数以避免滥用、在这些情况下、应使用最新 TivaWare 中的 DriverLib 函数。 在其他情况下、ROM 函数可能根本不存在。 这增加了复杂性、不利于程序员使用。 为了简化这一过程、提供了函数调用的第三个选项- MAP 前缀。 所有 DriverLib 函数调用都根据是否存在 ROM 版本在 rom_map.h 头文件中定义了一个等效函数。 因此、通过使用 MAP 前缀、无需猜测何时使用 ROM 或闪存 DriverLib 函数、同时还可以最大限度地减少 DriverLib 的闪存占用。 在应用程序文件中包含 rom_map.h 后、只需在所有 DriverLib 函数中添加 MAP 前缀即可利用所有可用 ROM 函数的优势。
尊敬的 Charles:
非常感谢! 请参阅随附的图像。
我现在能够查看使用 libusb 成功完成的事务、并可以将数据输入事务传输到 Tiva 部件。 但是我不能得到一个在交易中工作。 我注意到我的数据包监听器的事务超时。 我还注意到、TI 驱动程序库没有在 USBTXIS 寄存器中看到 EP1的中断状态位。 RX 中断看起来运行正常、但是 TX 中断运行正常。 我已经在 usblib 一直到这些寄存器中跟踪了它。
在 usbdenum.c 中、以下代码(我认为)是中断处理程序、似乎会调用回调函数以从主机顺利接收数据、但在 IN 事务发生时不会执行任何操作。 我可以看到、当我从主机 EP1发送输出事务时、中断状态位被置位、因此"ui32Status"不是零调用端点处理程序的回调。 因此、我的数据包监听器看到主机的输入事务超时、但未引发 EP1接收数据的中断。 我还验证了寄存器 TXIE 和 RXIE 中的所有位都设置为1。
void USBDeviceIntHandlerInternal(uint32_t ui32Index, uint32_t ui32Status) { static uint32_t ui32SOFDivide = 0; void *pvInstance; uint32_t ui32DMAIntStatus; uint32_t ui32LPMStatus; // // If device initialization has not been performed then just disconnect // from the USB bus and return from the handler. // if(g_ppsDevInfo[0] == 0) { MAP_USBDevDisconnect(USB0_BASE); return; } pvInstance = g_psDCDInst[0].pvCBData; // // Received a reset from the host. // if(ui32Status & USB_INTCTRL_RESET) { USBDeviceEnumResetHandler(&g_psDCDInst[0]); } // // Suspend was signaled on the bus. // if(ui32Status & USB_INTCTRL_SUSPEND) { // // Call the SuspendHandler() if it was specified. // if(g_ppsDevInfo[0]->psCallbacks->pfnSuspendHandler) { g_ppsDevInfo[0]->psCallbacks->pfnSuspendHandler(pvInstance); } } // // Resume was signaled on the bus. // if(ui32Status & USB_INTCTRL_RESUME) { // // Call the ResumeHandler() if it was specified. // if(g_ppsDevInfo[0]->psCallbacks->pfnResumeHandler) { g_ppsDevInfo[0]->psCallbacks->pfnResumeHandler(pvInstance); } } // // USB device was disconnected. // if(ui32Status & USB_INTCTRL_DISCONNECT) { // // Call the DisconnectHandler() if it was specified. // if(g_ppsDevInfo[0]->psCallbacks->pfnDisconnectHandler) { g_ppsDevInfo[0]->psCallbacks->pfnDisconnectHandler(pvInstance); } } // // Start of Frame was received. // if(ui32Status & USB_INTCTRL_SOF) { // // Increment the global Start of Frame counter. // g_ui32USBSOFCount++; // // Increment our SOF divider. // ui32SOFDivide++; // // Handle resume signaling if required. // USBDeviceResumeTickHandler(&g_psDCDInst[0]); // // Have we counted enough SOFs to allow us to call the tick function? // if(ui32SOFDivide == USB_SOF_TICK_DIVIDE) { // // Yes - reset the divider and call the SOF tick handler. // ui32SOFDivide = 0; InternalUSBStartOfFrameTick(USB_SOF_TICK_DIVIDE); } } // // Handle LPM interrupts. // ui32LPMStatus = USBLPMIntStatus(USB0_BASE); // // The host LPM resume request has been acknowledged, allow the device // class to handle the sleep state. // if((g_psDCDInst[0].ui32LPMState == USBLIB_LPM_STATE_SLEEP) && ((ui32LPMStatus & (USB_INTLPM_ACK | USB_INTLPM_RESUME)) == USB_INTLPM_RESUME)) { // // Notify the class of the wake from LPM L1. // if(g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler) { g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler(pvInstance, USB_EVENT_LPM_RESUME, (void *)0); } // // Now back in the awake state. // g_psDCDInst[0].ui32LPMState = USBLIB_LPM_STATE_AWAKE; // // Enable receiving of LPM packet. // USBDevLPMEnable(USB0_BASE); } // // The host LPM sleep request has been acknowledged, allow the device // class to handle the sleep state. // else if((g_psDCDInst[0].ui32LPMState == USBLIB_LPM_STATE_AWAKE) && ((ui32LPMStatus & (USB_INTLPM_ACK | USB_INTLPM_RESUME)) == USB_INTLPM_ACK)) { if(g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler) { g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler(pvInstance, USB_EVENT_LPM_SLEEP, (void *)0); } // // Now back in the sleep state. // g_psDCDInst[0].ui32LPMState = USBLIB_LPM_STATE_SLEEP; } else if(ui32LPMStatus & USB_INTLPM_NYET) { // // The device has held off the sleep state because LPM // responses are disabled. // if(g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler) { g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler(pvInstance, USB_EVENT_LPM_ERROR, (void *)0); } } // // Get the controller interrupt status. // //ui32Status = MAP_USBIntStatusEndpoint(USB0_BASE); ui32Status = USBIntStatusEndpoint(USB0_BASE); // // Handle end point 0 interrupts. // if(ui32Status & USB_INTEP_0) { USBDeviceEnumHandler(&g_psDCDInst[0]); ui32Status &= ~USB_INTEP_0; } // // Check to see if any DMA transfers are pending // ui32DMAIntStatus = USBLibDMAIntStatus(g_psDCDInst[0].psDMAInstance); if(ui32DMAIntStatus) { // // Handle any DMA interrupt processing. // USBLibDMAIntHandler(g_psDCDInst[0].psDMAInstance, ui32DMAIntStatus); } // // Because there is no way to detect if a uDMA interrupt has occurred, // check for an endpoint callback and call it if it is available. // if((g_ppsDevInfo[0]->psCallbacks->pfnEndpointHandler) && ((ui32Status != 0) || (ui32DMAIntStatus != 0))) { g_ppsDevInfo[0]->psCallbacks->pfnEndpointHandler(pvInstance, ui32Status); } }
端点处理程序回调代码
// // Because there is no way to detect if a uDMA interrupt has occurred, // check for an endpoint callback and call it if it is available. // if((g_ppsDevInfo[0]->psCallbacks->pfnEndpointHandler) && ((ui32Status != 0) || (ui32DMAIntStatus != 0))) { g_ppsDevInfo[0]->psCallbacks->pfnEndpointHandler(pvInstance, ui32Status); }
由于某种原因,我无法上传屏幕截图...马上重试。
在 usbdenum.c 中,以下代码(我想)是似乎调用回调函数以接收主机确定的数据的中断处理程序,但在 IN 事务发生时不执行任何操作。 我可以看到、当我从主机 EP1发送输出事务时、中断状态位被置位、因此"ui32Status"不是零调用端点处理程序的回调。 因此我的数据包监听器看到来自主机的输入事务超时、但不会引发 EP1接收数据的中断
尊敬的 Robert:
我将建议您尝试使用标准描述符类型事务、看看这些寄存器位和状态是如何设置的、以及如何在中断处理程序中调用输入事务和输出事务的回调。 希望 通过这种方法、你可以比较标准和非标准字符串描述符之间 USB 库行为方式的细微差异。
嗨、Charles、
我可能对司机在交易中的操作有误解。 有关此批量 LaunchPad 示例的两个快捷问题。
1.是否应该通过 RX 端点中断提醒 Tiva 部件发生了输入请求、以便它可以提取数据发回?
2.是否应在主机发出输入事务之前将数据加载到环形缓冲区中?
尊敬的 Robert:
1. 是否应通过 RX 端点中断提醒 Tiva 部件发生了输入请求、以便它可以提取要发回的数据?
通常、输入和输出是相对于主机指定的。 因此、IN 意味着从 USB 主机读取/接收数据。 我假设在事务中、它应该对应于来自从器件的 TX 事务。
2. 是否应在主机发出 IN 事务之前将数据加载到环形缓冲区中?
尽管缓冲区是可选的、但 usb_dev_example 确实使用该缓冲区来提高效率。
#define bulk_buffer_size 256
//*****
//
//传输缓冲区(从 USB 的角度)。
//
//*****
uint8_t g_pui8USBTxBuffer[bulk_buffer_size];
tUSBBuffer g_sTxBuffer =
{
true、//这是传输缓冲器。
TxHandler、// pfnCallback
(void *)&g_sBulkDevice、//回调数据是我们的器件指针。
USBDBulkPacketWrite、// pfnTransfer
USBDBulkTxPacketAvailable、// pfnAvailable
(void *)&g_sBulkDevice、// pvHandle
g_pui8USBTxBuffer、// pi8Buffer
BULK_BUFFER_SIZE、// ui32BufferSize
};
我知道-此问题的核心是 Tiva 部件没有收到主机发出的入帐交易警报。 主机如何向器件通知输入事务、以便其检索数据以发送和加载这些缓冲区?
尊敬的 Robert:
不确定这是否有用。 我运行库存 USB_DEV_BULK 示例并捕获输入事务和输出事务的调用栈。 USB0DeviceIntHandler ()用于输入和输出事务。 HandleEndpoints()函数应转向 ProcessDataToHost()或 ProcessDataFromHost(),具体取决于是输入事务还是输出事务。
对于输出事务:
进行输入事务。
好的-这似乎是我的问题所在。 当从主机发送输入事务时,我在包括"USB0DeviceIntHandler ()"在内的任何处理程序中都没有看到任何中断活动。 如果我能找出任何东西、我会报告。
我还注意到、此示例似乎无法跟上主机的大量数据块事务。 流量捕获显示了一些数据包被 NAK 处理、因为设备无法跟上。
尊敬的 Robert:
当从主机发送输入事务时,我在包括"USB0DeviceIntHandler ()"在内的任何处理程序中看不到任何中断活动。 [/报价]我希望能提供更多的指导。 下面是我的一些评论和建议:
-您刚才说过您创建项目时启用了所有端点。 我建议您首先使用更简单的配置。 或许只有一个输入端点和一个输出端点。 这样、调试和比较参考示例就更容易了。
-运行您的项目并捕获所有 USB 寄存器设置并与库存 USB_DEV_BULK 示例进行比较。 是否有任何重大差异?
-库存示例 USB_DEV_BULK 只是根据主机的 OUT 和 IN 请求回传消息。 您的项目也在这样做吗? 或者您的主机是否仅向器件发送输入请求?
-我不知道为什么输入事务不会产生中断。 对我来说、当硬件条件满足时、中断应该由 USB 控制器产生。 USB 控制器等待主机的请求、并在事务处理结束或由于某种错误而终止时产生中断。
设备是否处于某种挂起状态? 下面是关于暂停的一些说明。
当 USB 总线上的3ms 无任何活动时、USB 控制器将自动进入
挂起模式。 如果 USB 中断使能(USBIE)中的挂起(SUSPEND)中断已经被使能
因此、此时会生成中断。 当 USB 控制器进入挂起模式、USB PHY 也会进入
挂起模式。 当检测到恢复信号时、USB 控制器退出挂起模式
并使 PHY 退出 SUSPEND 状态。 如果恢复中断被使能、那么就会产生一个中断信号。
设置 USB 中的 RESUME 位也可强制 USB 控制器退出挂起模式
电源(USBPOWER)寄存器。 当该位置位时、USB 控制器退出挂起模式、并且
驱动总线上的恢复信号。 RESUME 位必须在10ms (最大值
15ms)以结束恢复信令。- 我想知道器件是否检测到 SOF? 下面是有关 SOF 的一些说明。
当 USB 控制器运行在设备模式时、接收到帧起始包(SOF)
发出中断一次。 当 SOF 数据包被接收时、11位帧编号
将包中包含的内容写入 USB 帧值寄存器 USBFRAME 和 SOF 中
中断也会发出信号、由应用程序处理。 USB 控制器启动后
要接收 SOF 数据包、它预计每毫秒1次。 如果在1.00358之后没有收到 SOF 数据包
MS 中、数据包被假定已丢失、并且 USBFRAME 寄存器未更新。 。
USB 控制器继续、在这些脉冲接收到的 SOF 包时、会重新同步这些脉冲
再次成功接收包。-您是否看到设备检测到任何错误?
尊敬的 Charles:
我正在努力启动并运行示例项目、以执行您建议的操作、并使用工作示例查看所有流量。
据此、我注意到示例 bulk 项目中的自述文件显示有一个具有 Tivaware 的应用二进制文件、但我看不到它。 我确实看到了 Visual Studio 源代码,但如果有一个二进制文件会更好。 您在运行测试时是否使用了此二进制文件、如果使用、则它位于 Tivaware 目录的哪个位置。 按照自述文件中的以下信息执行操作似乎不会指向二进制文件。
自述文本:
Windows 命令行应用程序 USB_BULK_EXAMPLE 的示例、说明
此外还介绍了如何连接大容量器件以及如何与之进行通信。
应用程序二进制文件作为'Windows 端示例的一部分进行安装
安装 CD 或通过下载获取 USB 套件的软件包(SW-USB-WIN)
来自 http://www.ti.com/tivaware 。 包括项目文件、以供使用
使用 Microsoft VisualStudio 2008构建的示例。 源代码
可在目录中找到该应用的源代码
TivaWare-C 系列/tools/USB_BULK_example。
根据您的问题、我将在故障排除过程中对几个不同的方面进行测试。 我已尝试从主机发送 IN 事务而不发送任何其他流量、以查看它是否引发中断而无法执行。 当器件端 Tiva 部件加电时、我已经在主循环中载入了 TX 缓冲区、然后仍然无法发送输入事务。 我将尝试获取输出和输入事务的11位代码、看看我是否可以在那里获取输入事务、因为它应该可以通过 PID 识别。
我还将查找寄存器捕获的错误、因为我尚未注意到其中的任何错误。
谢谢你的支持我知道这是很难评论,但你一直很有帮助。 我还会再来的!
嗨、Charles、
我已经解决了我的问题、其中大部分是由于希望输入令牌能够提醒器件为主机加载数据、但事实并非如此。
我注意到批量设备结构(sPrivateData)具有"输入"和"输出"端点字段。 TI 希望客户如何处理这个具有3x 输入和3xOUT 端点的批量示例? 我们是否应该修改 sPrivateData 结构中的端点变量?
尊敬的 Robert:
读取以下 tBulkInstance、似乎对于批量器件、它目前仅支持一个输入和一个输出端点。 您可以自由地将其扩展到多个端点。 但老实说,我没有这种层次的细节经验 , 它将需要多复杂的改变。 是否有可能创建由多个批量器件组成的复合器件? 我想知道这是否可行呢?
在性能方面、您是否真的认为具有多个端点的批量设备的吞吐量比只有一对输入/输出端点的吞吐量更高? 如果没有其他更高优先级的传输、如中断传输、等时传输、则批量设备的一个端点应占用所有带宽。 我并不认为拥有3倍的输入/输出端点会产生更好的吞吐量。 您只是在端点之间进行交错。 请记住、从一个端点切换到另一个端点时需要产生的开销。 我倾向于认为、使用一个输入/输出选项是更好的性能/吞吐量选项。 您最终会受到 USB 2.0全速的最大理论限值的限制。 请参阅这篇文章、了解一些见解。 https://stackoverflow.com/questions/39926448/what-is-the-effective-maximum-payload-throughput-for-usb-in-full-speed
同时也是一个正面的,今天是我的公司的美国假期。 我的假期一直持续到周一下午。 我的答复可能会有延误。
//***************************************************************************** // // PRIVATE // // This structure defines the private instance data and state variables for the // Bulk only example device. The memory for this structure is inlcluded in // the sPrivateData field in the tUSBDBulkDevice structure passed on // USBDBulkInit(). // //***************************************************************************** typedef struct { // // Base address for the USB controller. // uint32_t ui32USBBase; // // The device info to interact with the lower level DCD code. // tDeviceInfo sDevInfo; // // The state of the bulk receive channel. // volatile tBulkState iBulkRxState; // // The state of the bulk transmit channel. // volatile tBulkState iBulkTxState; // // State of any pending operations that could not be handled immediately // upon receipt. // volatile uint16_t ui16DeferredOpFlags; // // Size of the last transmit. // uint16_t ui16LastTxSize; // // The connection status of the device. // volatile bool bConnected; // // The IN endpoint number, this is modified in composite devices. // uint8_t ui8INEndpoint; // // The OUT endpoint number, this is modified in composite devices. // uint8_t ui8OUTEndpoint; // // The bulk class interface number, this is modified in composite devices. // uint8_t ui8Interface; } tBulkInstance;
嗨、Charles、
"我知道你在胡说什么。" Usblib 只设置为与输入/输出的单个端点一起工作。 然而、您确实提出了一个很好的观点、也许我需要重新思考我的方法、因为吞吐量不会相对于批量传输的端点发生变化。 我已经这样在杂草与这个固件,我不认为这。 这种方法之所以起始、是因为我希望从逻辑上隔离不同的通信"路径"。 例如、EP1传输一种类型的数据、EP2另一种类型等。因此、它们对于主机软件在逻辑上是隔离的。
考虑过您所说的内容后-我可能会更好地考虑使用非批量接口进行进一步的逻辑分区,因此我在这里有一些东西需要考虑。 感谢您提出这个问题。
因为我怀疑 usblib 需要更新,这也会是一个痛苦。 我希望您度过了一个美好的假期,并感谢您的支持!