LPCUSB

From Bertrik's wiki

Jump to: navigation, search

Back to main page

This page is the homepage for an open-source USB stack for the built-in USB controller in LPC214x microcontrollers. See also the lpcusb project page at sflogo.php?group_id=160384&type=1&.jpg.

Source code documentation is here.

The LPC2148 USB controller appears to have much in common with the Philips PDIUSBD12.

Contents

[edit] Design

Basic design ideas:

  • Layered approach:
    • USB hardware layer that talks to the actual hardware
    • USB core layer that knows how to handle basic USB structures and can handle control transfers and standard requests (through endpoint 0)
    • USB application layer. This could be a class-driver for mass-storage, HID, etc.
  • Interrupt driven, but also able to run in polled mode by repeatedly calling the ISR.
  • Software layers are relatively independent of each other. Lower layers communicate with upper layers through registered callbacks. This makes it easy to extend the functionality. For example, an USB application that uses vendor-specific transfers can simply register a callback for vendor-specific transfers, so no modification of the core layer is required.

In order to keep things simple, the following limitations apply:

  • No support for DMA transfers (can be added later)
  • No support for isochronous transfers (yet), currently only control, bulk and interrupt
  • Just one configuration (more than one is very rare in current USB devices)

[edit] Hardware requirements

The software assumes that the following hardware is present:

  • LPC2148 microcontroller (I'm using an Embedded Artists LPC2148 quickstart board + prototype board) running on a 12 MHz crystal.
  • P0.23 (VBUS) sees a high level when the USB Vcc is present (see LPC2148 data sheet, section 10.1)
  • P0.31 (CONNECT) controls a pull-up resistor to D+, such that a low-level activates the pull-up (see LPC2148 data sheet, section 10.1)

Make sure that the peripheral clock runs at 18 MHz minimum, see the user manual, section 14.6.2.

Notes for the Embedded Artists board (may also apply to other boards):

  • Remove the LED jumper on P0.23, because it acts as a pull-up that interferes with USB VBUS detection.
  • Watch out that the LPC2148 gets its power supply from only one place (for example, either from the motherboard or from the USB plug, but not both).

[edit] Building and running the software

Preparations:

Building it:

  • Enter directory 'target' and type 'make' to build the core stack plus all application examples.

Running it:

  • Download one of the generated hex files to your LPC214x (for example by using the Philips LPC2000 flash utility).
  • In the terminal program, connect to the serial port.
  • Connect the USB cable.
  • Watch the messages in the terminal program, you should see some debugging information about requests received (for example 'S' means a setup request).

[edit] USB application examples using LPCUSB

[edit] USB virtual COM port

An very simple USB virtual COM port example is included in the sources that simply echoes any data sent to the device.

On Windows XP, the USB virtual COM port simply shows up as a COMx device. The device uses the usbser.sys driver and just needs an .inf file (available in sources in directory target/examples). The usbser.sys driver file is not available in Windows by default and needs to be extracted from a Windows .cab file, for example:

C:\WINDOWS\Driver Cache\i386>expand sp2.cab -f:usbser.sys .

Apparently, there is a newer version of this driver for Windows XP, described in the Microsoft Knowledge base under KB918365, but it is not officially released yet.

On Linux, the USB serial port is handled by the cdc_acm kernel module (automatically loaded when the device is plugged in on most distros) and shows up as /dev/ttyACMx.

[edit] Custom/benchmark device

The included custom device example shows how to create a simple application that uses custom 'vendor' requests. The implemented vendor request simply dumps parts of the LPC214x memory space to the USB port, and is used to measure the throughput performance of the LPCUSB stack and hardware.

The host side software is not part of the software release yet, but is available from SVN. To build it:

  • Install the libusb package
    • Windows: install libusb-win32
    • Linux: install packages libusb and libusb-dev.
  • Checkout the sources from SVN as indicated under Building and running the software and enter the 'host/benchmark' directory
  • Build it:
    • Windows: run 'make'.
    • Linux: edit the Makefile to set the libusb include path and link path, then run 'make'.

[edit] Mass storage

The sources include an USB mass storage application example. Data is stored on an SD/MMC card connected to the SPI0 port.

Features:

  • Works under Windows XP and Linux 2.6
  • Practical read/write throughput: about 250 kB/s
  • Passes chapter 9 and MSC tests of the USBCV R1.3beta USB compliance verifier tool.
  • Memory footprint: 9 kB flash (non-debug version)

[edit] Design

The USB mass storage stack consists of several protocols layered on top, each in a separate source file:

  • Startup code and USB glue is implemented in main_msc.c
  • USB layer is provided by the LPCUSB stack.
  • BOT (bulk-only-transport) is implemented in msc_bot.c
  • SCSI is implemented in msc_scsi.c. The current implementation only supports the commands indicated as mandatory in the MSC Compliance test specification: TEST UNIT READY, REQUEST SENSE, INQUIRY, READ CAPACITY(10), READ(10) and WRITE(10). The SCSI interface consists of just three function calls: SCSIReset to initialise the SCSI layer, SCSIHandleCmd to check the command data and SCSIHandleData to handle a block of data.
  • The SD and SPI layers are borrowed from EFSL.

[edit] Links

Specifications used in developing USB mass storage (see http://t10.org)

  • [SAM-2] or SCSI Architecture Model version 2, revision 24
  • [SPC-3] or SCSI Primary Commands version 3, revision 23
  • [SBC-2] or SCSI Block Commands version 2, revision 16

Other links:

[edit] Ethernet

Ethernet is even more versatile than USB, so it would be great to have some kind of ethernet-over-USB. Used in combination with a small networking stack like uIP or lwIP, this would allow us to do things like add a simple webserver (e.g. for configuration) to an embedded device, or even something like a SMB server to serve files.

Unfortunately, the situation around ethernet-over-USB appears to be complex. For example, there are already multiple ways of doing ethernet-over-USB and it is not quite clear what eventually becomes the standard way:

  • Microsoft Windows uses RNDIS. Although Windows should be able to recognise an RNDIS compatible USB-ethernet adapter automatically, it appears that RNDIS still needs a device specific driver for it. As far as I know, Windows does not support any other ethernet-over-USB standard.
  • There is the standard CDC (communications device class) ethernet device class. Linux apparently supports this.
  • Then there is the relatively new CDC-EEM (ethernet emulation model).
  • Finally there appear to be various ways for framing ethernet packets, see GNU/Linux usbnet driver.

[edit] DMA support

DMA transfers are not supported yet. DMA transfer uses a model that also seems very useful for non-DMA transfers, so I plan to create a transfer layer that can (almost transparently) support LPC214x with and without DMA support, through the same API. The goal is to make it easier to transfer data for the application running on top of the USB stack.

[edit] Generic USB data transfer API

I'm thinking of an API that looks roughly like this:

  • void USBTransfer(U8 bEP, U8 *pbBuf, int iLen, TFnXferCB *pfnResult);

where

  • bEP = endpoint id, data direction is derived from bit 7
  • pbBuf = buffer address
  • iLen = number of bytes to send/receive
  • pfnResult = Completion callback that informs the caller about the result

This is a kind-of asynchronous call, meaning that it starts a transfer and then returns almost immediately. The caller must keep the data at pbBuf valid until pfnResult is called to inform the caller about the result of the transfer.

TFnXferCB is a function prototype, defined as typedef void (TFnXferCB)(U8 bEP, U8 bStatus); where bEP is the endpoint id and bStatus is a bitmap of status bits (to be defined, probably similar to what hardware presents, e.g. transfer OK, overrun, underrun etc.)

[edit] DMA implementation

In a LPC214x microcontroller supporting DMA, this function maps quite well on the DMA interface presented by the hardware.

Additional functions may be needed to allocate a piece of the USB RAM for a specific endpoint, something like

  • U8 *USBAllocDMABuffer(int iSize);

which is called at USB initialisation and returns a pointer to a DMA buffer of size iSize. The function reserves enough free space to allow for DMA descriptors.

[edit] Non-DMA implementation

If DMA is not supported (or one chooses not to use it), the transfer function can be implemented in software, basically emulating the DMA hardware. In this case, the function installs its own endpoint interrupt handler and handles data packetizing until the complete buffer is transfered. Also, things like double buffering (to improve throughput) can be handled here.

[edit] Benchmarks

This file shows the throughput of the LPCUSB custom/benchmark application for various types of USB host controller chips and for varying read/write block sizes.

Explanation of the curves:

  • via ehci hub: host controller is a VIA USB EHCI chip, device is connected through an USB2.0 high-speed capable hub
  • via uhci uhb: host controller is a VIA USB UHCI chip, device is connected through an USB2.0 high-speed capable hub
  • via uhci: host controller is a VIA USB UHCI chip, device is connected directly to the host.
  • nec ehci hub: host controller is a NEC USB EHCI chip, device is connected through an USB2.0 high-speed capable hub
  • nec ohci: host controller is a NEC USB OHCI chip, device is connected directly to the host.

It turns out that the throughput increases as block size is increased. This can be explained by the way an USB device read or write is handled: one read or write is scheduled in one USB frame (which takes 1 ms for full-speed USB). Therefore there is a simple upper limit for the throughput when using a specific block size: when reading/writing X bytes at once, the throughput is at maximum 1000*X bytes/second.

Also interesting to observe is the difference between the curves for the various USB host controller connections. The NEC OHCI chipset can actually achieve the throughput limit until a blocksize of 512 bytes. The VIA UHCI chipset achieves only half the throughput limit.

The curves for the cases where a hub is used are intriguing, somehow they can defeat the theoretical limit. For example, communication through the high-speed hub to the NEC EHCI controller takes place at about 250 kB/s for a block size of only 64 bytes. A possible explanation is that high-speed USB uses shorter frames, so the turn-around time for one IO operation is much smaller. Although the hub increases throughput for small block sizes, the ultimate throughput for very large blocksizes is lower than the case where a USB full-speed host controller is used.

[edit] Questions and answers

[edit] Combined USB functions

Q: Is it possible to combine mass storage with a virtual COM port?
A: Apparently not in a standard way. Normally, different USB classes can be combined by specifying each function in its own interface descriptor. Unfortunately, the USB CDC class requires that its function is specified in the global device descriptor (not in the interfaces), leaving no room for other functions. Attempts to implement this anyway (by specifying the CDC functionality on the interface level instead of the device level) easily result in blue screens on Windows XP and therefore are very tedious to debug.

Q: Is it possible to combine mass storage with a custom device?
A: Yes, this is possible by putting your custom endpoints in a separate interface. Windows XP then recognises the device as a composite device, consisting of one regular USB mass storage device and one custom device. The USB mass storage can be used as usual, the custom device can be used with (for example) libusb at the same time. NOTE: Class requests (like the 'get number of LUNs' and 'reset' commands for MSC) are now sent to recipient 'interface' instead of recipient 'device' by the host OS driver. Therefore the code has to be modified so these class requests are forwarded to the proper interface.

Q: Is it possible to create more than one virtual COM port in one device?
A: Currently unknown.

[edit] Hardware

Q: Can LPCUSB run on a Keil board?
A: Yes. TODO

Q: Can LPCUSB run on a IAR/Olimex board?
A: Yes, probably. TODO

Q: Can LPCUSB run on a LPC23xx?
A: Yes, success has been reported with this on the LPC2000 mailing list but it requires some changes which have not been integrated in the code yet.

[edit] Problems

Q: The microcontroller gets stuck somewhere in the LPCUSB stack (usually in Wait4DevInt)?
A: Probably caused by too slow peripheral clock, it needs to run at 18 MHz minimum, see the user manual.

[edit] Miscellaneous

Q: Can LPCUSB be run using interrupts (instead of being polled)?
A: Yes, this has been tested for the virtual COM port, but has not been integrated yet.

Q: What version of GCC does it work with?
A: LPCUSB has been tested with version 3.4.3 and 4.1.0 of the gnuarm for windows toolchain.

[edit] Porting

Q: Can it be ported to IAR Embedded Workbench?
A: Yes, it can, zgabriele ported it to version 4.04: ' I successfully ported LPCUSB (2006-11-24) under IAR Embedded worbench 4.40A.I found that compiling in the ARM mode at the maximum optimization level don't work. I found that removing the "Turns off type-based alias analysis" of the function "USBHandleControlTransfer" it works again.'

[edit] External links

Tools

Websites

Other mass storage implementations:

Personal tools