XMC4500. USB-device. Zero-length packets in isochronous IN-endpoint - how to prevent?

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
rst
Level 3
Level 3
First solution authored Welcome!
I need a fast real-time data stream from device (USB-device) to PC (USB-host). I use a large isochronous endpoint (packet length <= 1023 bytes).
The data is transmitted, but very often in frame stream on the host side I get of zero-length packets (ZLP). Although I do not send ZLP from my device (I send all packets of a fixed length).
Because of this, the flow rate decreases.
How to transfer data into USB-core so that ZLP-s are absent?

I write the data for transfer like this:
#define USBCFG_DMA 0

#define B0 (1u << 0)
#define B1 (1u << 1)
#define B2 (1u << 2)
...
#define B31 (1u << 31)

//Writes number of bytes in to the USB IN endpoint.
u32 UsbHwEpWrite(uint epa, void const *buf, u32 len)
{
UsbEp *ep = &udev.ep[epa & USB_EP_NUM_MSK];
if (ep->state != ep->S_CFG) return 0;
ep->xferBuf = (u8 *)buf;
ep->xferTotal = len;
ep->xferLen = ep->xferCnt = 0;
ep->state = ep->S_IN;
StartWriteXfer(ep);
return ep->xferTotal;
}

//Start a new in transfer.
//Based on the transfer values of the endpoint the in endpoint registers will be programmed to start a new in transfer
//ep - Endpoint to start the transfer
static void StartWriteXfer(UsbEp *ep)
{
uint i, n, e = ep->address & USB_EP_NUM_MSK; //for isoc.ep ep->address == 0x81
HwRegsUSB::T_DEP volatile *pdep = &USB.DIEP;
n = maxTransferSize[__USAT(e, 1)]; //for isoc.ep n == 65472
if (ep->xferTotal - (i = ep->xferLen) < n) ep->xferLen = i = ep->xferTotal;
else ep->xferLen = i += n;
u32 j = 1 << 19; //number of packets. do not used, since i != 0 for isoc.ep
if (i) {
i -= ep->xferCnt;
if (e) j = divCeil(i, ep->maxPacketSize) << 19; //#define divCeil(a, b) (((a) + (b) - 1) / (b))
j |= i; //for isoc.ep for example: j == 0x803FF
if (USBCFG_DMA) { //USBCFG_DMA==0
u8 *s = ep->xferBuf + ep->xferCnt; //source address
if (((u32)s & 3) || !IN_RANGE((u32)s, RAM_regionB_start, RAM_regionC_start + RAM_regionC_size - 1)) { //do not used, since the isochronous point data placed is in RAM
memcpy(ep0buf.data, s, i);
s = ep0buf.data;
}
pdep->DMA = (u32)s; //USB.DIEPDMAx
} else USB.DIEPEMPMSK |= 1 << e; //enable fifo empty interrupt
}
//Program size of transfer and enable endpoint
pdep->TSIZ = j; //USB.DIEPTSIZx
pdep->CTL |= B26 | B31; //USB.DIEPCTLx. set bits CNAK and EPEna
}


When data transfer to USB-host is completed, an interrupt occurs, which is processed by this code:

//Handles all interrupts for all in endpoints
//The interrupt handler first checks, which endpoint has caused the interrupt and then
//determines, which interrupt should be handled.
static void HndIEPInt()
{
int i, e;
u32 c, ie, ia = USB.DAINT & B16 - 1;
while ((e = 31 - __CLZ(ia)) >= 0) {
ia -= 1 << e;
UsbEp *ep = &udev.ep;
HwRegsUSB::T_DEP volatile *pdep = &USB.DIEP;
ie = pdep->INT; //USB.DIEPINTx
ie &= (USB.DIEPEMPMSK >> e & 1) << 7 | DIEPMSK; //enum {DIEPMSK = B0 | B1 | B2 | B3};
if (!USBCFG_DMA) if (ie & B7) {
//Write data to an endpoint fifo
//The data from the ep->xferBuf gets copied in to the tx fifo of the endpoint until the buffer has been read
//completely or the tx fifo is full. The transfer values are not updated.
if (c = ep->xferLen - ep->xferCnt) { //calculate the length and the amount of dwords to copy based on the fifo status
//add the unaligned bytes to the word count to compare with the fifo space
if (c < (i = (pdep->TXFSTS & B16 - 1) << 2)) i = c; //pdep->TXFSTS == USB.DTXFSTSx
u32 volatile *fifo = FIFO_PTR(ep->txFifoNum); //#define FIFO_PTR(epn) (u32 *)((u8 *)&USB + 0x1000 + (uint)(epn) * 0x1000)
u8 *s = ep->xferBuf + (c = ep->xferCnt);
for (ep->xferCnt = c + i; (i -= 4) >= 0; s += 4) *fifo = *(u32p8 *)s; //copy data dword wise
if (i & 3) {
c = 0;
if ((i += 2) >= 0) {
c = *(u16p8 *)s;
s += 2;
}
if (i) c |= *s++ << __USAT(i << 4, 5);
*fifo = c;
}
}
}
if (ie & B0) {
if (USBCFG_DMA) if (!(pdep->TSIZ & B29 - 1)) ep->xferCnt = ep->xferLen; //pdep->TSIZ == USB.DIEPTSIZx
if (ep->xferTotal == ep->xferLen) { //always true
pdep->CTL = pdep->CTL & (B11 - 1 | B15 | 3 << 18 | B20 | 15 << 22) | B27; //USB.DIEPCTLx. set bit SNAK. I also tried not to set this bit after transfer was completed - but nothing changes
ep->state = ep->S_CFG;
if (!USBCFG_DMA) USB.DIEPEMPMSK &= ~(1 << e); //Mask fifo empty interrupt
UsbEventEndpoint(USB_EP_IN(e), USB_EVT_IN); //Inside this function, a call of UsbHwEpWrite() is made to write the next data frame
} else StartWriteXfer(ep); //start next step of transfer - never called for isoc.ep
}
if (USBCFG_DMA) if (ie & B2) trap(TRAP_USB | USB_ERR_AHB << 16, pdep->DMA);
pdep->INT = ie; //USB.DIEPINTx
}
}


remark:

struct UsbEp {
enum { //endpoint's states
S_VOID, //do not configured
S_CFG, //is configured
S_IN, //IN-transaction in progress
S_OUT //OUT-transaction in progress
};
union {
struct {
u8 type:2; //The endpoint type
u8 isStalled:1; //Sets if the selected USB endpoint is stalled.
u8 sendZLP:1; //If set, a zero length packet will be send at the end of the transfer
};
u8 attr;
};
u8 volatile state; //one from S_...
u8 address; //The endpoint address including: bits0...3 - endpoint number; bit7 - direction
u8 txFifoNum; //Endpoint transmit Fifo Number
u16 maxPacketSize; //The maximum size of packet for USB endpoint (due to FS Speed device only 64 Byte)
u16 xferLen; //The length of the current transfer
u16 xferCnt; //Bytes transfered of the current USB data transfer
u16 xferTotal; //The length of total data in buffer
u8 *xferBuf; //The buffer of the current transfer
};

typedef __packed u16 u16p8;
typedef __packed s16 s16p8;
typedef __packed u32 u32p8;
typedef __packed s32 s32p8;
typedef __packed u64 u64p8;
typedef __packed s64 s64p8;
typedef __packed u32 u32p16;
typedef __packed s32 s32p16;
typedef __packed u64 u64p16;
typedef __packed s64 s64p16;
typedef __packed u64 u64p32;
typedef __packed s64 s64p32;
0 Likes
1 Reply
rst
Level 3
Level 3
First solution authored Welcome!
Question canceled. I studied the manual more thoroughly and independently solved this problem.
For those members, who will be interested, I give a solution:
In function StartWriteXfer() needs modify these lines:
pdep->TSIZ = j;
pdep->CTL |= B26 | B31;

to:
pdep->TSIZ = j;
j = pdep->CTL;
pdep->CTL = j | B26 | B31 | B29 >> (j >> 16 & 1);

and after this action the extra ZLP no longer appear.
0 Likes