USB On The Teensy 3 From The Ground Up

When implementing USB on a microcontroller, most people are going to reach for V-USB if they’re using an AVR, one of Microchip’s USB libraries if a PIC is involved, or any number of the USB libraries for various ARM processors. [Kevin] had a different idea. As a challenge to himself, he wrote a USB device driver for the Teensy 3.1 microcontroller board, getting as close to the bare metal as he could get.

Writing a USB device driver first required a literature review. There are a few peculiarities in the Freescale K20 family of microcontrollers – the one found in the Teensy 3.1 – that dictate the need for a specific memory layout, using several clocks, and handling all the USB descriptors. [Kevin] started with the clocks, every last one of which must be enabled. The clock is generated by the Multipurpose Clock Generator from a 16MHz crystal, PLL’ed to the frequencies the USB module needs, and sent out over the System Integration Module.

Following the flowcharts and sequences found in the Freescale reference guide told [Kevin] exactly what needed to be done with the startup sequence, and offered a few suggestions on what needed to be done to set up all the interrupts. [Kevin] spent an incredible amount of time documenting, programming, and smashing his head against the keyboard for this tutorial, but he does give everyone a great opportunity to learn from his struggles.

While [Kevin] has a mostly complete USB device driver, his work is far from done. That’s alright, because this project wasn’t meant to be a full-featured driver; it’s still missing real error handling, strings in the configuration, and a real VID/PID. That’s alright, it’s still a great exercise in building something from scratch, especially something that very few people have built successfully.

Post navigation

13 thoughts on “USB On The Teensy 3 From The Ground Up”

Freescale provides a code generator and USB drivers, their stuff is overly complicated, unreadable and towards MQX. Freescale’s reference manuals coverage are spotty and doesn’t always tell you how their hardware modules work. They seem to expect users to reverse engineer that info from their code gen outputs or link in their code and not think about it. :(

His code seem to be nice, clean and readable. He put in quite a bit of effort for coding, debugging and most importantly documenting things.

All of the microprocessor vendors are like this: NXP, ST, TI, Microchip, etc.

It’s as if the software is some sort of mysterious extra add-on that they are not too concerned about.

They hire terrible programmers to write their example code and their libraries. Proper programming techniques are incompatible with their libraries, so one must either throw out their library or throw out good technique.

You read about the IoT world of software and how messed up it is with respect to security and user interface design and you realize that nobody in this business takes software seriously.

Totally agree… and it’s really not uncommon for hardware companies to neglect software. It’s sad, because a lot of really good hardware often goes by the wayside, because these companies adopt bad software practices and dedicate few resources to creating actual systems.

There’s a lot of untapped potential for a company that doesn’t take the mentality of “Well we made the chips and that was the hard part. Now lets haphazardly throw some software together and push this to market.” Even the best silicon can still make a crappy platform.

Those chips are fine if you only ever want Serial or FTDI’s bigbang interface, and if you can stomach using FTDI after their recent scandal.

But you can’t implement HID keyboard, mouse, joystick, and raw HID devices for custom projects. You can’t have class compliant USB MIDI either. Nor USB mass storage. You get all those if you use a native USB board like Teensy. (full disclosure, I’m the author of Teensyduino)

You also don’t get the ability to implement other USB stuff, like media transfer protocol and virtual ethernet, for low latency OSC (Open Sound Control) and E1.31 (many-universe DMX lighting control), and USB audio streaming. These don’t exist today, but I’m going to write them in 2015, because I can, and because Teensy 3.1 is programmable and expandable, and because those protocols made Arduino-style easy will open up possibilities for huge numbers of people to make truly awesome projects!

Hey Paul, that’s all cool, how about you mention the fact that all of USB stuff on Teensy is about as closed source as it gets? Maybe if it was open, this guy wouldn’t have to reinvent the wheel writing a usb stack from scratch.

@dong1e – You are mistaken. My USB stack for Teensy 3.1 is indeed open source. The author of this article mentions “In writing this, I must confess that I looked quite a lot at the Teensyduino code along with the V-USB driver code”.

If the Teensyduino USB code is “as closed source as it gets”, then how would you explain this:

Paul, if I follow things correctly, your USB MIDI implementation uses your baremetal USB stack on the Teensy, coded at a hardware specific level? I’m trying to do USB MIDI on the Arduino Due, and I’m struggling on where to start.

Nice writeup. This may be the very first document ever written that actually explains many of these details of using Freescale’s USB peripheral.

When I wrote Teensyduino’s USB code, nearly 3 years ago, there’s wasn’t any (horrible) Processor Expert code, and the 2 examples Freescale had published at the time were confusing at best. They also used a fixed BDT entry to memory buffer mapping, limiting each endpoint to buffering only 2 packets (rather than drawing from a larger memory pool as I do in Teensyduino, which allows good performance even with Arduino sketches that have a high latency due to various Arduino libraries that block). I know you didn’t do the other endpoints yet for bulk transfer, but if you do, allocating enough buffers for 1 ms of data (about 18 or 19) makes a dramatic difference for user-level code with high latency.

Of course, Freescale’s USB documentation leaves a lot to be desired. I spent a couple weeks filling in the gaps with guesswork while watching the results with a Total Phase Beagle-12 USB protocol analyzer. But I never wrote up a nice document like this. Everything I ever learned was only published in the form of source code, which few people have really ever dug into and fully understood, like you did.