I have a project that requires reading an external IMU gyroscope data at regular interval and sending the data over to an Android phone.
I am using a teensy 2.0 board to query the IMU via I2C and send it over USB using raw HID. I am using RawHID variable which is declared in usb_api.h of usb_rawhid of teensyduino.
I have read that a full-speed USB using interrupt transfer can have a 1ms maximum latency and would like to achieve this 1ms maximum latency. I am not sure what to look for to achieve this maximum latency and would like to please know about pointers. My ultimate goal is to receive the gyroscope data every 2 ms (500 Hz).
A few things that I am aware that may be of an issue:
1) I have changed the RAWHID_TX_SIZE to be 6 bytes (I only need 6 bytes for gyroscope value), and the RAWHID_TX_INTERVAL is set to 1 ms (fastest). An OUT endpoint is currently specified in the interface that I do not need, I am not sure if removing it can improve latency.
2) Android recognizes teensy as "hiddev USB HID v1.11 Device". I am not sure if this is full raw HID or if it is trying to parse it. Teensy is using raw HID as specified above.
3) In Android, a specific thread is trying to queue() on UsbRequest followed by requestWait(). The processing when the data arrive is very fast (ie: store it in a global variable), but I am at the mercy of the thread scheduler.
So those are some pointers I am aware of (and not completely sure how they affect maximum latency). I would love to please hear people's feedback and maybe pointing in new directions on how to improve my maximum USB latency. Finding information on reducing USB latency of interrupt transfer is scare.
For USB, it's all polling. Every 1 ms you have a "frame" consisting of none or more transfer descriptors, where each transfer descriptor tells the USB controller which USB device to poll.
In general, the USB controller begins the frame with transfer descriptors for interrupt transfers. What this means is that with a single interrupt transfer descriptor you're (almost) guaranteed to get polled once per ms. If your device has an interrupt to send it returns it when polled; so you're going to get 1 ms latency as your worst case.
It is possible to ask the USB controller to poll the device less often (e.g. isochronous transfers). It's also possible to ask the USB controller to poll the device multiple times in the same 1ms frame; however because it typically does interrupt transfer descriptors first you'd expect to get polled twice at almost the same time with an "almost 1 ms" gap between, so that doesn't help worst case latency.
Mostly, as far as I can tell, for a "< 2 ms" requirement the USB specs/protocols, the USB controller, the kernel's USB controller driver and the kernel's USB HID driver are not a problem at all. The problem is getting the data from the USB HID driver to a user-space process/thread in a timely manner.
Unfortunately, Linux/Andriod is not a real-time OS. It provides no guarantees. You will be at the mercy of the thread scheduler (and maybe also the JVM's garbage collector). There is probably nothing you can do about it.
I'd recommend finding out why you need to receive the gyroscope data every 2 ms in the first place. For a simple example, can you add a timestamp to the gyroscope data, and let the receiving thread "reconstruct history" from those timestamps such that the receiving thread doesn't need low latency at all?