We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
(Sorry for my limited English ability.)
I have been assigned to develop an USB based I/O board (software portion). The requirement of this I/O board has never been discussed and never been defined, and the hardware has not been made. So I am working on the LPC23xx evaluation boards. What I have been told is that, this USB based I/O board is made to work with a X86 platform running Linux.
I read some Tsuneo's (Tsuneo Chinzei) articles, and eventually decided to choose the LPCUSB for my LPC2378 evaluation board. The major reasons are: 1. Tsuneo said that it is the best open source project for LPC23xx. 2. Its Linux driver is ready. (The standard one in the Linux kernel.)
I built a Cygwin/YAGARTO GNU ARM toolchain environment to perform some simple tests for LPCUSB. And found that, the main_serial.c works. Then, I migrated the LPCUSB to the KEIL environment, and the main_serial.c still works. But during the migrations and the tests, I found that, or I guessed that, the LPCUSB USB stack is good; however, its examples are only for demonstrating purpose, so they are not well tested.
The major problem for me currently is that, the [static void USBFrameHandler(U16 wFrame)] function does not work properly, because the [fBulkInBusy] variable has never been reset to FALSE. Actually, the real circumstance is: If I send one byte from the X86 desktop to the LPC2378 board, the LPC2378 can send one byte back to the X86 desktop. If I do not send one byte from the X86 desktop to the LPC2378 board, then the LPC2378 board can not send anything to the X86 desktop. I am not so sure that, it is a bug that I have to fix, or it is not a bug, I just need to implement some more USB functions to complete the mechanism.
I don't actually know that, how it works originally. Is that the host sends data with a BulkOut, and this BulkOut invokes a BulkIn? Or the LPC2378 board sends data to the host when it is polled every milisecond by the host? What is the correct way to send data from the LPC2378 board to the host?
Is there any USB-CDC tutorial for a novice? Or could someone please kindly provide some advices?
Hi Tsuneo,
Many thanks for your exposition and help.
I am studying the precious information you provided.
Sorry for my limited technique ability.
Now, I notice that I am a little confused with some USB term.
Copied from [USB Made Simple]
If an IN endpoint is defined as using Bulk transfers, then the host will transfer data from it using IN transactions.
Interrupt transfers are regularly scheduled IN or OUT transactions, although the IN direction is the more common usage.
So there is NO "Bulk IN transactions" nor "Interrupt IN transactions"; they are the same "IN transactions".
Am I right?
CDC device driver polls bulk IN endpoint (Polling IN transactions) repeatedly.
Then a "raw IN EP" (not a "bulk IN EP", right?) responses to the host with DATA0/DATA1 Packet.
When the EP finishes to send the packet, a callback routine registered by USBHwRegisterEPIntHandler(BULK_IN_EP, BulkIn); is called.
Since it is a "raw IN EP", why it is called BULK_IN_EP? and why the function is called BulkIn? why the function is called after the DATA Packet had been sent? why not called before the DATA Packet had been sent? I mean that, what is the real role of this function?
The original code works, because BulkIn function is called after the DATA Packet had been sent, so the fBulkInBusy is set to FALSE. But I just can't understand the type of the "IN EP", and the naming/usage of the callback routine. Does every USB-CDC device follows this flow?
John,
why the function is called after the DATA Packet had been sent? why not called before the DATA Packet had been sent?
as stated above, IN is in terms of the host, thus data flowing from the device to the host. it is AFTER the packet has been sent because it has to be: FIRST, you write something in reply to the host, and then BulkIn gets called to give you the chance to deliver more data. that's how it works. BulkIn will never be called unless you first wrote something to the bus!
and I don't think your technique is an issue at all - quite the contrary. your questions indicate depth and an eager to learn, certainly in comparisons the common "do my homework" poster!
Then a "raw IN EP" (not a "bulk IN EP", right?) responds to the host with DATA0/DATA1 Packet.
I think this response is done by the SIE automagically?
I think the physical endpoints are some kind of hardware, equipped with EP_RAM and some USB functionality. But what is the logic endpoints?
Is the BulkIn() function something like the interrupt driven UART Tx procedure? called when the Tx buffer is empty?
Hi Tamir,
Thanks.
My technique is surely an issue. Especially I am an old and "forever junior" engineer. No big company wants to hire me. And the company which hired me, hires me to develop, not to learn. :)
what makes you so sure working for a big company is better? I have worked for a number of large organizations in the past and decided to stop with it more than a year ago, never to return unless I have to. Why? stupid managers all around you, bloated staff, the lack of freedom to do things your way, red-tape, procedures. there are of course advantages, but if you are in this because you like it, I think you'll be much better of in a smaller organization. believe me, what I'm doing now a thousand times for satisfying compared to anything I've done in the past.
As Tamir says, that would not necessarily be any different in a big company!
Any development project must, by definition, involve breaking some new ground - and must, therefore, include some "learning".
Your timescale estimates must include an allowance for this!
> So there is NO "Bulk IN transactions" nor "Interrupt IN transactions"; they are the same "IN transactions". Am I right?
Yah, you are right. Bulk and interrupt transfer apply the same IN and OUT transaction format. So, just seeing transaction on the line, we cannot distinguish them each other, bulk or interrupt. But the endpoint is assigned to bulk or interrupt on the endpoint descriptor. Therefore, we call them as bulk IN or interrupt OUT transaction.
The USB SIEs on NXP LPC family MCU classify each endpoint into interrupt/bulk/isoc (and default control) transfer type. Actually, these bulk and interrupt endpoints can be exchanged, because there is no difference on the handling of bulk and interrupt transaction of the device side. NXP calls double buffered endpoints as bulk, single buffer ones as interrupt.
Host controller processes interrupt transfer differently from bulk. For interrupt transfer, it reserves bandwidth (chance to occur the transfer) on enumeration. When a request for interrupt transfer comes from device driver, host controller schedules it in the periodical time table. Isoc transfer is also assigned to similar (or same) periodical time table. Control transfer is guaranteed for a fixed amount of bandwidth. For bulk transfers, the rest bandwidth is available. >> CDC device driver polls bulk IN endpoint (Polling IN transactions) repeatedly.
> Then a "raw IN EP" (not a "bulk IN EP", right?) responses to the host with DATA0/DATA1 Packet.
The firmware don't always to respond to each IN transaction. Just when the firmware has data to send, it puts the data to the IN EP. If there is no data on the IN EP, the device SIE returns NAK (Not ready) to the IN transaction.
USB don't have any mean to notify from device to host (at least until USB 3.0). Therefore, host polls device using IN transaction, repeatedly. This is called polling IN transaction, an orthodox method for device drivers, like HID, CDC, Printer, CCID, etc. > Since it is a "raw IN EP", why it is called BULK_IN_EP? and why the function is called BulkIn? why the function is called after the DATA Packet had been sent? why not called before the DATA Packet had been sent? I mean that, what is the real role of this function?
The same reason as above, the endpoint is assigned to bulk type.
Tsuneo
Ah, I didn't answer to the last question,
> why the function is called after the DATA Packet had been sent? why not called before the DATA Packet had been sent? I mean that, what is the real role of this function?
More precisely, the endpoint interrupt is generated when the device receives ACK from host on the IN transaction, not on the DATA0/1 packet.
Return to the meaning of your question, The FIFO implementation allows more than full-size (64 bytes) "packet" transfer (*1). VCOM_putchar() fills just one byte to the txfifo buffer. But, as usual putchar() does, VCOM_putchar() is expected to be called repeatedly, to pass strings. In this way, txfifo may hold greater data, before USBFrameHandler() starts a transfer. For greater size transfer than wMaxPacketSize (64 bytes) of the endpoint, it should be split into full-sized packets.
USBFrameHandler() starts the transfer by putting the first "packet". BulkIn() (SendNextBulkIn()) puts the second and latter "packets", when the last transaction finishes.
(*1) this "packet" is not strictly USB term, but we often use "packet" in this way. Sometimes, confusing.
-> As Tamir says, that would not necessarily be any different in a big company!
I had worked for a big global firm, where I met some real professionals, and co-worked with them. That was the only good thing. Oh, the pay was good too.
Before that, or after that, I haven't had any opportunity to co-work with real professionals.
To me, the biggest difference between small and large companies lays in the scope of your work (this may not always be true, of course). When you end up in a big company, you are not likely to be allowed to write something from scratch, really from scratch - using existing components will be required even if they suck (perfectly logical of course - time is money). One of the products firmware framework (plus some application) I am now working on was written almost entirely by me to my liking - from total scratch (except RL-ARM components of course). So, you need to know almost everything. However, when I worked for one of these large companies I begged to do more and get to know more, but my team leader would not allow me, because I had my responsibilities. that sucked, and was the main reason I left.
With your exposition and help, I think/guess I have gotten enough information for my stage-1.
I will re-read all the stuff I read before, trying to get a better understanding of the LPCUSB's CDC example.
Then, in my stage-2, I will try to slightly re-write the main_serial.c etc.
Many thanks.
I am just being told that the USB based I/O board should be capable to handle the data transmission every millisecond. The X86 Linux Application polls the USB based I/O board every millisecond, then the USB I/O board must respond right away.
I don't think this is a rational requirement. I think/guess that, the USB host controller chip on the X86 platform polls the USB device every millisecond, but such a polling is unintentional. I just checked and confirmed that, for Ubuntu 9.10 Desktop Linux, the CONFIG_HZ is 250 (tick = 4ms).
I don't know how to write a Linux testing application to poll the USB based I/O board every millisecond. And I don't know how to explain this to them.
> I am just being told that the USB based I/O board should be capable to handle the data transmission every millisecond.
This assumption is almost ture, but not 100%. You may see around 2 ms interval occasionally. Then, you need buffering so as not to drop the data. Also, the transfer size becomes greater, this assumption breaks. A packet-full size (64 bytes) is a good amount.
To run 1 ms interval using a generic device driver like libusb, you have to put multiple asynchronous calls in advance, without waiting completion of any call. When a call finishes, put another call, to keep the request queue populate.
==> \LPCUSB\target\examples\main_serial.c
/** Local function to handle incoming bulk data @param [in] bEP @param [in] bEPStatus */ static void BulkOut(U8 bEP, U8 bEPStatus) { int i, iLen; if (fifo_free(&rxfifo) < MAX_PACKET_SIZE) { // may not fit into fifo return; // Here!!! } // get data from USB into intermediate buffer iLen = USBHwEPRead(bEP, abBulkBuf, sizeof(abBulkBuf)); for (i = 0; i < iLen; i++) { // put into FIFO if (!fifo_put(&rxfifo, abBulkBuf[i])) { // overflow... :( ASSERT(FALSE); break; } } }
If there is no space in the rxfifo (rxdata), the ISR function BulkOut() returns.
In such a case, the EP_SLOW bit in USBDevIntClr Register had been set (cleared) by the major ISR function USBHwISR() already. But the bEP is still full with data, so the host application will not be able to write any data into this bEP (OUT-NAK), so the ISR function BulkOut() will not be triggered once again.
The data in the bEP will never be processed. It is a dead-end.
Test Only.