This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Some questions about LPCUSB with LPC2378

(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?

Parents
  • John,

    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

    assuming you are developing a device, it is incorrect to initiate a transaction from the device. the host must take the initiative first.

    Note that "Out" and "In" in the device source files refer to the host - thus, "BulkOut" serves data coming into the device from the host, while "BulkIn" serves data transmitted to the host.

    USB CDC tutorial provided by Keil's latest MDK can be found at this relative path:

    \Keil\ARM\Boards\Keil\MCB2300\USBCDC

Reply
  • John,

    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

    assuming you are developing a device, it is incorrect to initiate a transaction from the device. the host must take the initiative first.

    Note that "Out" and "In" in the device source files refer to the host - thus, "BulkOut" serves data coming into the device from the host, while "BulkIn" serves data transmitted to the host.

    USB CDC tutorial provided by Keil's latest MDK can be found at this relative path:

    \Keil\ARM\Boards\Keil\MCB2300\USBCDC

Children
  • Hi Tamir,

    Thanks.

    I checked the

    \Keil\ARM\Boards\Keil\MCB2300\USBCDC

    and confirmed that, it is a sample project.

    Do you know any USB-CDC documentation which is simple enough for a novice?

    What is the correct way to send data from the USB-CDC device to the host? Only when it is polled every milisecond by the host?

  • the device is only addressed when the host sends data to it. it is not polled every x milliseocnds unlike MSDs, for instance. operation is simple: device is mapped to a serial port on the host. you only need to change the contents of BulkIn, BulkOut and make sure the baudrate, data bits and stop bits on both sides agree. also, you need to install the provided .inf file.

  • > What is the correct way to send data from the USB-CDC device to the host? Only when it is polled every milisecond by the host?

    CDC device driver polls bulk IN endpoint (Polling IN transactions) repeatedly.
    It's true for the default CDC driver on most of OS, Windows, Linux, Mac OSX, etc.
    It's a difference from a generic device driver.

    While the bus is not busy, you'll see 30-40 times of IN-NAKs on the bulk IN endpoint in a 1ms USB frame. You have chances to start IN transfer on each IN transaction. But once you finishes the IN transfer (end with short packet), you don't see any IN-NAK until next frame. This behavior makes 1 transfer per 1 frame feature.



    > 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.

    The bulk IN endpoint is independent from bulk OUT endpoint.
    LPCUSB source also doesn't relate them each other.
    So, it's caused by your custom code.
    Post your code, which was added by you for above process.

    Tsuneo

  • Hi Tsuneo,

    Many thanks for your exposition and help.

    I downloaded the source code from

    lpcusb.svn.sourceforge.net/.../trunk
    

    in January.

    And did some modifications to migrate them to the KEIL environment. (I hope that these modifications didn't bring any side-effect to the LPCUSB.)

    After the migration, the code works -> If I send one byte from the X86 desktop to the LPC2378 board, the LPC2378 can send one byte back to the X86 desktop.

    The below is the original code, it works:

    static void USBFrameHandler(U16 wFrame)
    {
            if (!fBulkInBusy && (fifo_avail(&txfifo) != 0)) {
                    // send first packet
                    SendNextBulkIn(BULK_IN_EP, TRUE);
            }
    }
    
    int main(void)
    {
            int c;
    
            /* Omitted */
    
            // echo any character received (do USB stuff in interrupt)
            while (1) {
                    c = VCOM_getchar();
                    if (c != EOF) {
                            // show on console
                            if ((c == 9) || (c == 10) || (c == 13) || ((c >= 32) && (c <= 126))) {
                                    DBG("%c", c);
                            }
                            else {
                                    DBG(".");
                            }
                            VCOM_putchar(c+1);
                    }
            }
    
            // return 0;
    }
    

    The below is the code I further modified, It does not work -> 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.:

    int main(void)
    {
            int c = 'A', next;
    
            /* Omitted */
    
            // echo any character received (do USB stuff in interrupt)
            while (1) {
                    next = (txfifo.head + 1) % VCOM_FIFO_SIZE;
                    if (next != txfifo.tail) {
                            VCOM_putchar(c);
                    }
            }
    
            // return 0;
    }
    

    If I also modify the below portion, then it seems work.

    static void USBFrameHandler(U16 wFrame)
    {
            // if (!fBulkInBusy && (fifo_avail(&txfifo) != 0)) {
            if ((fifo_avail(&txfifo) != 0)) {
                    // send first packet
                    SendNextBulkIn(BULK_IN_EP, TRUE);
            }
    }
    

    I checked the whole project, and found that the [fBulkInBusy] variable has never been reset to FALSE.

  • Some related code that I did not modify.

    static void SendNextBulkIn(U8 bEP, BOOL fFirstPacket)
    {
            int iLen;
    
            // this transfer is done
            fBulkInBusy = FALSE;
    
            // first packet?
            if (fFirstPacket) {
                    fChainDone = FALSE;
            }
    
            // last packet?
            if (fChainDone) {
                    return;
            }
    
            // get up to MAX_PACKET_SIZE bytes from transmit FIFO into intermediate buffer
            for (iLen = 0; iLen < MAX_PACKET_SIZE; iLen++) {
                    if (!fifo_get(&txfifo, &abBulkBuf[iLen])) {
                            break;
                    }
            }
    
            // send over USB
            USBHwEPWrite(bEP, abBulkBuf, iLen);
            fBulkInBusy = TRUE;
    
            // was this a short packet?
            if (iLen < MAX_PACKET_SIZE) {
                    fChainDone = TRUE;
            }
    }
    
    
    static void BulkIn(U8 bEP, U8 bEPStatus)
    {
            SendNextBulkIn(bEP, FALSE);
    }
    
    

  • I found 3 [ fBulkInBusy = FALSE; ] totally.

    1. fBulkInBusy will soon be set to TRUE.

    static void SendNextBulkIn(U8 bEP, BOOL fFirstPacket)
    {
            int iLen;
    
            // this transfer is done
            fBulkInBusy = FALSE;
    
            /* Omitted */
    
            // send over USB
            USBHwEPWrite(bEP, abBulkBuf, iLen);
            fBulkInBusy = TRUE;
    
            // was this a short packet?
            if (iLen < MAX_PACKET_SIZE) {
                    fChainDone = TRUE;
            }
    }
    

    2. VCOM_init() is called only once.

    void VCOM_init(void)
    {
            fifo_init(&txfifo, txdata);
            fifo_init(&rxfifo, rxdata);
            fBulkInBusy = FALSE;
            fChainDone = TRUE;
    }
    

    3.

    /**
            USB device status handler
    
            Resets state machine when a USB reset is received.
     */
    static void USBDevIntHandler(U8 bDevStatus)
    {
            if ((bDevStatus & DEV_STATUS_RESET) != 0) {
                    fBulkInBusy = FALSE;
            }
    }
    

    I don't know how to implement a proper USB function to reset the fBulkInBusy to FALSE.

  • You are fiddling the example in half understanding.
    Then, it doesn't work as you intend.
    Before fiddling the example, realize the background structure well.

    The CDC example implements FIFOs (ring buffers) upon raw IN/OUT endpoints (EPs).

    For raw IN/OUT EPs,
    1) raw IN EP,
    - USBHwEPWrite() writes to a data packet (up to 64 bytes) into the EP buffer.
    - When the EP finishes to send the packet, a callback routine registered by
    USBHwRegisterEPIntHandler(BULK_IN_EP, [i]BulkIn[/i]);
    is called.

    2) raw OUT EP,
    - When the EP finishes to receive a packet from host, a callback routine registered by
    USBHwRegisterEPIntHandler(BULK_OUT_EP, [i]BulkOut[/i]);
    is called.
    - Firmware reads out the packet using USBHwEPRead()

    That's all.
    For the data exchange of short-size packet, you'll use the raw EP directly.

    For the FIFOs,
    3) As of the device --> host direction,
    - VCOM_putchar() puts a byte of data into the txfifo
    - USBFrameHandler() (registered by USBHwRegisterFrameHandler(USBFrameHandler);) polls txfifo at every 1ms SOF timing.
    If any data on the txfifo, USBFrameHandler() puts a packet (up to 64 bytes) into the IN EP.
    - When the IN EP finishes to send the packet, BulkIn() is called (registered by USBHwRegisterEPIntHandler(BULK_IN_EP,)). And then, SendNextBulkIn() is called.
    If any other data still remains on txfifo, SendNextBulkIn() puts another packet to the IN EP.
    OR, if the last packet ends up with full-size packet (64 bytes), SendNextBulkIn() puts ZLP (Zero-Length Packet) to the IN EP, to terminate the transfer.

    4) As of the host --> device direction,
    - When the OUT EP finishes to receive a packet, BulkOut() (registered by USBHwRegisterEPIntHandler(BULK_OUT_EP, BulkOut);) is called.
    - BulkOut() unloads the packet from the OUT EP to rxfifo
    - VCOM_getchar() reads out one byte of data from rxfifo, if any.

    Lastly, the LPCUSB example detects bus reset using USBDevIntHandler() and USBHwRegisterDevIntHandler(USBDevIntHandler);

    /**
        USB device status handler
    
        Resets state machine when a USB reset is received.
     */
    static void USBDevIntHandler(U8 bDevStatus)
    {
        if ((bDevStatus & DEV_STATUS_RESET) != 0) {
            fBulkInBusy = FALSE;
        }
    }
    

    In above bus reset handler, a user flag, fBulkInBusy, is initialized.
    This scheme also works in usual enumeration. But in good USB practice, this kind of initialization is placed in Set_Configuration handler.

    As for the USB term, Transfer - Transaction - Packet, read this post.
    www.cygnal.org/.../001627.html

    Tsuneo

  • 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