4
    Example Three

Create a new example directory and set it up as before in example one. Next edit usbconfig.h and change the values to the following.

  • Set USB_CFG_HAVE_INTRIN_ENDPOINT to 1,
  • set USB_CFG_DEVICE_CLASS to 0,
  • set USB_CFG_INTERFACE_CLASS to 3,
  • set USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH to 34, and
  • set USB_CFG_IMPLEMENT_FN_WRITE to 1.

The source code for example three is given in the download.

The report descriptor now includes an output report as well as an input report. Lastly, compile and burn the firmware as done with the two previous examples. The firmware creates a loop back like HID device. The download also contains a simple host application, simple.c, to read and write this device.

As this host application is running, the debug output from the firmware should resemble the following figure below.

A word of caution, the first byte of the write buffer for the write() command in the host application is the output report’s “Report ID”. A report descriptor provides for different types and sizes of reports through the use of a byte sized Report ID field. This byte is sent as the first byte of a report. If the first byte is zero, however, the write command does not send the first byte, instead the byte is skipped and only the remaining data in the buffer is transfered to the device.

    Control Transfers

No more than 90% of any frame can be allocated for interrupt transfers. Any time remaining is allocated to control transfers and after that bulk transfers. See the figure below for an illustration on how control transfers work.

Since control transfers are not limited by a service interval or a data limit, their maximum data rate can be substantially more than an interrupt transfer. Bulk transfers are even faster because they lack the overhead associated with control transfers. Low-speed devices, however, are not allowed to use bulk transfers, nor does HID support them. We can use a low level library, such as libusb, to get around this restriction though.

My experiments reveal that an 8 byte control transfer has a maximum data rate of around 14,000 bytes per second. The rate doubles to 28,000 bytes per second for a 64 byte control transfer. The upper limit appears to be around 32,000 bytes per second.

    Handling OUT Transactions

The figure below illustrates the two ways V-USB handles OUT transactions. One way is for control transfers and it involves writing a usbFunctionWrite() routine. The other is for interrupt and bulk transfers and it involves writing a usbFunctionWriteOut() routine.

The usbFunctionWrite() routine is called by V-USB for any data phase OUT transactions addressed to control endpoint zero. The routine should return a one when the data phase is complete. Otherwise it should return a zero. Returning a one causes a ZLP to be queued for the upcoming status phase of the control transfer. However if a one is returned more than once during the same data phase the control transfer can fail. This is because doing so might toggle the packet ID for the upcoming status phase to DATA1 which is invalid.

The usbFunctionWriteOut() routine on the other hand does not need to return anything. V-USB simply relays the data from any OUT transaction not directed to control endpoint zero to this routine. The endpoint number can be found in the variable, usbRxToken.

    Interrupt OUT Endpoints

A HID interface can support interrupt transfers for output reports. Since control transfers have some overhead, interrupt transfers are preferred for streaming data. Plus by using both an interrupt IN endpoint and an interrupt OUT endpoint their two data rates can be equalized for better balance. Without an interrupt OUT endpoint the host must rely on the SET_REPORT request to transfer data. Unfortunately V-USB has no option for adding to the interface descriptor the endpoint descriptor required to tell the host about the interrupt OUT endpoint.

To get around this I have included a patch for it in order to make the next and last example clearer. The patch, usbdrv.patch, is located in the download package.

    Example Four

Create a new example directory and set it up as we have done before. But before copying and editing usbconfig.h, we must first patch the usbdrv directory. Run “patch -p0 < usbdrv.patch” in our directory to patch it. The new patched version of usbconfig-prototype.h in usbdrv is then copied over as usbconfig.h.

Edit this usbconfig.h after the preliminaries have been done and change the values to the following. The source code for example four is again in the download.

  • Set USB_CFG_HAVE_INTRIN_ENDPOINT to 1,
  • set USB_CFG_DEVICE_CLASS to 0,
  • set USB_CFG_INTERFACE_CLASS to 3,
  • set USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH to 34,
  • set USB_CFG_IMPLEMENT_FN_WRITEOUT to 1, and
  • set USB_CFG_HAVE_INTROUT_ENDPOINT to 1.

Using the simple host application from the last example, the debug output should resemble the following figure below.

    Conclusion

Bit-banging data communication interfaces might not be considered optimal especially an interface like USB running at 1.5Mhz. However it was not until recently that on-chip USARTs became both ubiquitous and inexpensive on MCUs. Before then experimenters had to rely on software based solutions to fill the gap. It is a mark of versatility and resourcefulness that led experimenters to take on the challenge, making do with what was available to accomplish their design.

It probably will not be very long before USB interfaces make it onto the chips that hobbyists hold dear like the ATmega328p. At the moment innovative support chips such as the FT232RL USB-to-serial IC are very popular. While they provide an excellent way to interface to USB they hide away all the interesting details behind their serial ports. Even the very creative folks at Arduino have taken steps to ween themselves away from these support chips, replacing them with an open source LUFA ATmega8U2 version, albeit with the same functionality, in their UNO and Mega2560 boards.

For low speed USB communications the future is definitely in the direction of USB microcontroller based solutions. USB serial interface engine chips, such as the Phillips PDIUSBD11 which uses a I2C serial interface and the National Semiconductor USBN9603 which uses a Microwire serial interface, are becoming a thing of the past. The only other options are USB devices that use a high-speed parallel interface to connect to the MCU. This is not a solution that most experimenters will embrace.

My wish is that someone will eventually release an open source library dedicated to the HID interface supporting all the latest experimenter class USB microcontrollers that will come our way soon. In the meantime hopefully the contributors to the Arduino project will enhance the firmware of their ATmega8U2 USB-to-serial IC to provide an USB-to-I2C feature as well, one that could provide for one or more HID based pipes off of it.

    Footnotes

[*] Copyright 2011 George Magiros. Use of source code subject to the GPLv3. See http://gplv3.fsf.org.

    Downloads
All Examples (256)