My good friend Bdale Garbee and I are working on a new rocket flight computer called TeleMetrum. It's using a TI CC1111 microcontroller, which contains a digital RF transceiver along with a tiny microprocessor based on the universally loved Intel 8051.
The CC1111 has the usual array of microcontroller I/O ports: A to D converters, GPIO pins, SPI, I2C, I2S and regular serial ports. It also has a USB device controller, which is why we selected it over the otherwise identical CC1110.
I hadn't ever written a USB device controller before, and my USB experience had been limited to writing a debug interface driver using libusb for this project, which should be the subject of another blog posting someday. USB appears to have been designed by mean people; just getting a simple two-way bytestream involves a huge pile of code.
Starting with FreeRTOS, I found a compatible USB stack written for the LPC2148 processor, lpcusb. Fortunately, that stack is fairly cleanly written, with a narrow interface between the stack and the device. I figured I could replace the LPC2148 bits with CC1111 bits and have it running in short order. Of course, nothing is as simple as it should be.
After about three weeks, I managed to get packets flowing from host to device and started to debug the USB setup stuff. All of my difficulties here relate to the slightly brain damaged way USB signals the end of Setup data flowing from device to host (IN data). This is done by sending a packet which is strictly less than the maximum size advertised by the device, in my case 32 bytes (that's all the CC1111 can handle). If the data to be sent is a multiple of this max size, you send a zero-length packet afterwards.
The first bug was that my code simply delivered a zero length packet every time it was done sending data, in response to the 'a packet has been delivered' interrupt. It should have been obvious, but this ended up flooding the USB link with zero length packets. Once that was fixed, I had the first few setup packets working correctly. Next, I had to fix the code that would chunk up larger setup replies into multiple packets. With that done, I had the initial setup working correctly and the device appeared in the lsusb output.
While debugging this, I had noticed that my ISR was getting called 'a lot', and I found out that none of the USB interrupt status bits were on. I guessed that this meant the master USB interrupt bit was stuck on. Which confused me, as the other interrupt bits I'd played with on the CC1111 all automatically cleared themselves when the ISR was invoked. Hurray for inconsistent hardware, but it turns out that this is not true for all of the interrupts, only some of them. With the interrupts turned back off, I've now got a device which correctly responds to the USB setup and then sits idle:
idVendor 0xfffe
idProduct 0x000a
bcdDevice 1.00
iManufacturer 1 altusmetrum.org
iProduct 2 TeleMetrum
iSerial 3 tele-0
The next task is to figure out how to send NAK packets back when the host asks for data and I have none to send. That may make it possible to send data back and forth, at which point I can write a simple command interpreter for the CC1111 so we can poke at it via USB.
All of this code is in my freertos git repository, I'll see if the freertos or lpcusb people are interested in the code once it's working.