UINT32 UVC_EP1_In(void *p) { UINT8 nump = 0; UINT8 numWait = 0; UINT32 remaindNump = 0; //最后一包数据的长度 static UINT8 needTransfer = DEF_UVC_BURST_MAX; //最大是4包数据 nump = USB30_IN_Nump(ENDP_1); //读出当前已经发出的数据包 if (nump >= DEF_UVC_BURST_MAX) { printf("nump %d err\r\n", nump); numWait = 0; USB30_IN_ClearIT(ENDP_1); return 2; } else { numWait = DEF_UVC_BURST_MAX - nump; //计算当前需要发送的数据包 } if (Iso_Flag_PackHasVideo) //有有效数据包 { remaindNump = 1024; if ((DEF_UVC_BURST_MAX == numWait) || (needTransfer == numWait)) //数据包接收完成 { nump = DEF_UVC_BURST_MAX; Iso_PackSeqNum += DEF_UVC_BURST_MAX; /*计算当前USB微帧的发送包数量*/ if (Iso_PackSeqNum >= Iso_PackTotalNum) //全部数据包已经发完,一般是45K { UVC_BufInfo.isFull[isFullIndex] = 0; Iso_PackSeqNum = 0; Iso_Flag_PackHasVideo = 0; needTransfer = DEF_UVC_BURST_MAX; UVC_BufInfo.RemainCount--; isStop = 1; } else if (Iso_PackSeqNum + DEF_UVC_BURST_MAX >= Iso_PackTotalNum) { nump = Iso_PackTotalNum - Iso_PackSeqNum; remaindNump = Iso_LastPackLen; } if (Iso_PackSeqNum < Iso_PackTotalNum) { Iso_Tx_DataAddr += (DEF_UVC_BURST_MAX << 10); //地址每次递增4k } needTransfer = nump; } else { if (numWait < DEF_UVC_BURST_MAX) //如果4包数据没有发完,则重新计算需要发送的包数 { nump = DEF_UVC_BURST_MAX - numWait; needTransfer = nump; } else { nump = needTransfer; } } USBSS->UEP1_TX_DMA = Iso_Tx_DataAddr; USB30_IN_ClearIT(ENDP_1); if (isStop == 1) { USB30_IN_Set(ENDP_1, ENABLE, NRDY, 0, 0); //整包数据完成,发送NRDY } else { USB30_IN_Set(ENDP_1, ENABLE, ACK, nump, remaindNump); USB30_Send_ERDY(ENDP_1 | IN, nump); } } return 0; } //该函数在main中运行 void UVC_Start_transform(UINT8 idx) { UINT32 packNum = DEF_UVC_BURST_MAX; UINT32 lastLen = 1024; if (isStop == 1) { Iso_Flag_PackHasVideo = 0x01; Iso_PackTotalNum = UVC_BufInfo.Length[idx] >> 10; //计算包个数 Iso_LastPackLen = UVC_BufInfo.Length[idx] & 0x3FF; //计算最后一包数据的长度 UVC_BufInfo.Length[idx] = 0; Iso_Tx_DataAddr = (UINT32)UVC_USBDMA_Addr[idx]; //切换地址 if (Iso_LastPackLen) { Iso_PackTotalNum++; } else { Iso_LastPackLen = 1024; } if (Iso_PackTotalNum <= DEF_UVC_BURST_MAX) //总共不足4包 { packNum = Iso_PackTotalNum; lastLen = Iso_LastPackLen; } if ((packNum == 0) || (lastLen == 0)) { printf("start err\r\n"); isStop = 1; return; } HSPI_Signal_RxReady(); //拉GPIO isStop = 0; inEp1_IRQ = 0; USBSS->UEP1_TX_DMA = Iso_Tx_DataAddr; //DMA地址偏移 需重重置 USB30_IN_Set(ENDP_1, ENABLE, ACK, packNum, lastLen); USB30_Send_ERDY(ENDP_1 | IN, packNum); UVC_dotTick = UVC_dotTick / 2; if ((UVC_dotTick < 33) && (UVC_dotTick != 0)) //每帧图像间延迟 { mDelaymS(33 - UVC_dotTick); UVC_dotTick = 0; } } }
可以在所有调用以下两个函数的地方,在之前将包数量和长度打印出来监控下,是否有意外的情况。
以及是否存在连续调用的情况,这是不被允许的。
或者调用之后,还没有进入IN_Callback就意外调用IN_ClearIT,导致芯片状态异常
其次,要注意数据本身是否复合相关协议对格式、收发数量和速度的要求,可能无法上传是因为上位机主动停止了。
可以借助bus hound来看下是否有错误信息,可能可以根据bus hound显示的信息在网上找到一些相关的信息。
测试该类问题的时候,应当想办法规避上位机问题,确保上位机一直在请求数据,使用libusb直接加载该设备是比较合适的方法。
我这个是摄像头设备,读数据是从PC上直接打开摄像头测试的,所以上位机应该是没问题。
在发送NRDY时包数和数据长度是0没关系吧?其他的我再看看
摄像头属于UVC类,是比较复杂的class,数据流有一定的格式要求,存在时间戳、帧号等概念,都要保证不能出错。
应答状态为NRDY时,包数量和长度不被关心。
需要注意当前端点配置,是被配置位同步传输端点还是批量传输端点:
void USB30_ISO_Setendp(UINT8 num,FunctionalState Status );
1、这个是我抓的log,程序连续运行到6406秒以后USB数据就没有中断触发了,主机在请求RESET,在这以前没有出现过重复调用IN_set、数据长度为0的情况。
2、IN_ClearIT只有在UVC_EP1_In函数中调用,这个是中断的回调函数,也只会在中断触发后运行。
3、初始化时没有调用USB30_ISO_Setendp,这样就是BULK传输端点了吧。而且在描述符里设置的也是BULK传输。
4、如果这个问题避免不了,有没有什么接口可以自动恢复通讯?
根据USB3.0 SPEC,主机在某种情况下会出现放弃ERDY的情况,可以根据tERDYTimeout,超时后,通过重新发送ERDY来尝试恢复通讯。
这个超时判断和重发ERDY用什么接口呢?
可以自行设计,这个超时不是非常严格500毫秒。
1、我试了超时600ms重发ERDY,无法恢复通讯。然后试着在超时后加了RESET USB才能恢复,这样就类似于USB断线重连,这样实际使用中也不能这样。是否有其他办法呢?
2、我在用bushound抓数据时还发现偶尔数据会乱掉,但是很快自动就正常了,如下图。出现USB无法发出数据和这个是否有关呢?
已解决
现在通讯中断的问题解决了,但是还是会出现丢数据的情况,如25楼中贴出来的bushound抓到的数据。这个在部分PC上出现的概率更高。我通过测试(发送特定有规律的数据),出现这种情况是某次传输没有将4k数据发完,这样就造成数据错乱。请问,这个要怎么规避
看截图,似乎是多个长度460800byte的传输,怎么大数据量的传输多半还是代码逻辑上的问题。重点监控IN_CALLBACK进入次数吧