Content originally posted in LPCWare by pierre on Sat Sep 20 07:54:09 MST 2014
Forget about USB rom drivers or lpcusblib, they have rather nasty bugs/race conditions, plus the code is impossible to read, it has globals accessed from everywhere, etc.
Everything you need is in the Huge User Manual. However, it is not explained in the clearest way possible... I had to read some bits several times.
Basically you setup your Device Queue Head (see manual) where each endpoint has a pointer to a Transfer Descriptor (DTD) linked list, which will be executed in order. Each of those contain pointers to a buffer where the data will be stored/read.
Once you prime the endpoint it executes its DTD list in-order, and it will fire a Transfer Complete interrupt after each transfer... then you read the DTDs, retire the used ones, and and add more of them. The last part is subtle as you got to handle the case where the hardware is writing a finished DTD while the ISR is reading the same DTD, or the hardware is stopping after its last transfer, while at the same time the ISR is queuing a new transfer. Some registers have to be tickled in the right way, and the Manual does explain very well how to do that.
Regarding performance, you can get maximum USB bandwidth with very little CPU use on the LPC, since the USB controller can transfer 16kbytes packets and handles all the low level stuff. So you get an IRQ every 16kB, or even less if you queue more DTDs. That's a few % cpu use at full bandwidth, unless the CPU copies all data "manually", of course.
You got to remember that USB is a protocol for dumb devices.
Sending is simple : you queue a DTD and the hardware will send it when the host requests it.
Receiving is a bit more subtle. You only know the host wants to send something when it does try to send it. In that case, if you got an endpoint primed with its DTD and buffer ready, the data is transferred. Otherwise, the endpoint will NAK, you'll get an interrupt, and the host will retry later, hopefully by that time the endpoint will be primed.
You cannot ask the host to send, or tell it you are ready to receive, because USB is a protocol for dumb devices. The only way the device can talk is to reply to something. Even so called "interrupt" transfers are really the host polling the device 1000x a second and asking "any news ?"...
So if you want high throughput, always have your endpoint primed for reception, with several DTDs queue'd. If it runs out of DTDs, it will NAK, and wait for the host retry delay.
The most painful thing is to write those damn descriptors.