LPC11U67 USB CDC (VCOM lpcopen_2_06 based) BUG when receiving more than 64 bytes

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LPC11U67 USB CDC (VCOM lpcopen_2_06 based) BUG when receiving more than 64 bytes

1,282 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Belias on Tue Jul 22 05:53:54 MST 2014
I have spent the last 4 days on trying to figure this out. I searched all over the forums and didn*t find any answer.

I'm trying to implement the usbd_rom_cdc example from LPCOpen (lpcopen_2_06_lpcxpresso_nxp_lpcxpresso_11u68), Which uses the ROM API of the LPC11U67.

Despite the first example was working suprisingly fast, everything aftewards is a big struggle.

Additionally I think the example contains a lot of code which is unused, and i dont know where it originates. (in VCOM_bulk_out_hdlr everything which is not in the USB_EVT_OUT case, vcom_read_req etc.)

This example only supports packets that are smaller or equally long as the length of an Bulk EP, wich in this case is 64 bytes. In my scenario we need longer packets, and also flow control. So I started to adapt the code so that it supports longer packets in both directions. For the direction µC --> PC (Host) this was easy. Just calling vcom_write(..) until all bytes have been written (so my function vcom_puts(..) blocks until everything has been written, which is fine for me now).

But for receiving from the Host --> µC everything turns out to be way more complicated. At first I took a look at the usbd_rom_cdc_uartn example, because it included some form of flowcontrol. If I got it right, if I can't process an USB Packet currently then I just ignore it and count how many I missed. Then when I'm free to process further packets I just read the USB_CDC_OUT_EP? Now the questions start to arise. Do I have to read it once, and then I will get interrupts again? Can I read the amount of which I counted? How do I see that the Data is updated in the endpoint? I could not find ANY documentation on this.

The behaviour I got was: When not reading the USB_CDC_OUT_EP in the VCOM_bulk_out_hdlr the USB Stack will generate a huge number of VCOM_bulk_out_hdlr interrupts. It actually seems to do so endlessly. I'm not quite sure but I think it stopped after reading the Endpoint several times.

So this were way to many open questions for me. So I started with the Idea of just increasing the buffer to my needs so that no overflow can occur (I need 256 Byte pakets). So I will always be able to read the data out of the USB_CDC_OUT_EP in the interrupt handler. So everything should work fine. BUT IT DIDN'T.

Several hours later I figured out that printing the received data IN the interrupt handler made it work. Replacing the printf by a delay loop showed me that waiting in the interrupt handler solved the problem.
So my current VCOM_bulk_out_hdlr looks like this:
static ErrorCode_t VCOM_bulk_out_hdlr(USBD_HANDLE_T hUsb, void *data,
uint32_t event) {
VCOM_DATA_T *pVcom = (VCOM_DATA_T *) data;

switch (event) {
case USB_EVT_OUT:
if (pVcom->rx_count <= VCOM_RX_BUF_SZ - 64) { // The number of bytes which will be read is unknown, that is why  we reserve the maximum of 64 bytes at the end of the buffer
pVcom->rx_count += USBD_API->hw->ReadEP(hUsb, USB_CDC_OUT_EP, &pVcom->rx_buff[pVcom->rx_count]);
//        printf("USB RX: %d\n", pVcom->rx_count);
} 
for (int i = 0; i < 3000; ++i) {
__NOP();
}

break;

case USB_EVT_OUT_NAK:
printf("NAK\n");
///* queue free buffer for RX */
//if ((pVcom->rx_flags & (VCOM_RX_BUF_FULL | VCOM_RX_BUF_QUEUED)) == 0) {
//USBD_API->hw->ReadReqEP(hUsb, USB_CDC_OUT_EP, pVcom->rx_buff, VCOM_RX_BUF_SZ);
//pVcom->rx_flags |= VCOM_RX_BUF_QUEUED;
//}
break;

default:
printf("UNEXPECTED!");
while (1) {
__NOP();
}
break;
}
return LPC_OK;
}


If I send pakets smaller 512 Bytes ( which is the size of VCOM_RX_BUF_SZ) everything works ONLY with the for loop (I'm running on 48MHz).
If I don't include the for loop the code will hang almost always after sending three packets of 128 bytes (of course reading each of them before a new one arrives), from which at the last package only 64 bytes will arrive. Sometimes It is even less, and sometimes I also got a constant flow of interrupts, always with 64 bytes incomming, without an end.
To get it working again I need to unplug and replug the usb cable (then everything works again as in the beginning, failing after several packets). Just disconnecting the VCOM in the terminal is not enough.

I tested It, there are no interrupts generated anymore for VCOM_bulk_out_hdlr. Not only it isn't called by the ROM API, but also there will be no USB0_IRQs (from where the ROM API handler is called).
While this is the case the direction from the µC--> Host works totally fine.

So what is happening here? I'm running on custom hardware, but also deactivated the rest of the code (all Interrupts except SysTick) and everything that could theoretically interfere somehow.

What does the delay in the interrupt routine (which is indeed a teriible basically) alter? So why does it have an effect at all?

I definitely need help in this case! Any Idea is appreciated!
0 Kudos
Reply
2 Replies

997 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by David Perry on Sun Jan 17 17:17:38 MST 2016
This thread is old but maybe this will help somebody. Here are some simple changes that worked for me to increase the 64 byte limit to 512.

In cdc_com.c, replace this:

pVcom->rx_count =  USBD_API->hw->ReadEP(hUsb, USB_CDC_OUT_EP, pVcom->rx_buff);

with this:

if(pVcom->rx_count > (VCOM_RX_BUF_SZ - 64)) {
pVcom->rx_count = 0;  // Avoid buffer overflow (by discarding data)
}
pVcom->rx_count += USBD_API->hw->ReadEP(hUsb, USB_CDC_OUT_EP, &pVcom->rx_buff[pVcom->rx_count]);

And in vcom_bread, enter the critical section earlier so that pVcom->rx_count is not unexpectedly modified by the interrupt:

uint32_t vcom_bread(uint8_t *pBuf, uint32_t buf_len)
{
VCOM_DATA_T *pVcom = &g_vCOM;
uint16_t cnt = 0;
/* read from the default buffer if any data present */
if (pVcom->rx_count) {
/* enter critical section */
NVIC_DisableIRQ(USB0_IRQn);

cnt = (pVcom->rx_count < buf_len) ? pVcom->rx_count : buf_len;
memcpy(pBuf, pVcom->rx_buff, cnt);
pVcom->rx_rd_count += cnt;

if (pVcom->rx_rd_count >= pVcom->rx_count) {
pVcom->rx_flags &= ~VCOM_RX_BUF_FULL;
pVcom->rx_rd_count = pVcom->rx_count = 0;
}
/* exit critical section */
NVIC_EnableIRQ(USB0_IRQn);
}
return cnt;
}

It's not ideal because the buffer copy is done with interrupts blocked, but now it won't overrun the buffer until 512 bytes.

0 Kudos
Reply

997 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by nxpsupport on Wed Jul 23 15:33:00 MST 2014
Please make sure that ReadEP function is called everytime in VCOM_bulk_out_hdlr, because each OUT endpoint buffer has an active bit which is cleared by hardware upon receiving OUT data. The ReadEP function sets the active bit back to the corresponding buffer after reading the current data. Now if the active is not set the endpoint shall be stalled. But I am not sure how it worked with the delay loop.

To read data of size larger than 64 by splitting data into multiple packets of 64 data bytes you should be able to infer the length of each packet for not overrunning the space in your receive buffer. You could do this either by having fixed known sizes, or for variable lengths you could have a field in the first packet indicating the total transfer length. Below is a code snippet that would read 256 byte transfers (i.e. 4 packets of 64 bytes each) into a queue of buffers.

pVcom->rx_count += USBD_API->hw->ReadEP(hUsb, USB_CDC_OUT_EP, &pVcom->rx_buff[push_index][pVcom->rx_count]);
if(pVcom->rx_count == 256) {
pVcom->rx_count = 0;
push_index++;
if(push_index == QUEUE_SIZE) {
push_index = 0;
}
}

Hope this helps!
0 Kudos
Reply