Blog Posts

Teensyduino 1.42 – What’s New

Today PJRC is releasing Teensyduino version 1.42.

Here’s a detailed look at 1.42’s many new features and improvements.

I’d like to thank everyone who contributed & beta tested, especially Defragster!

The 1.42 installers are available now at the downloads page.

Arduino IDE Ports Menu & Serial Monitor

Since 2009 Teensy has supported non-Serial USB types (selected in the Tools > USB Type menu), but Arduino’s Ports menu has worked only with Serial devices.  1.42 extends the Ports menu with a new “Teensy” section capable of showing every USB type Teensy implements.

In this screenshot, 3 Teensy boards are connected, but only 1 is programmed to be Serial.

Teensyduino’s non-Serial modes include a HID interface to emulate serial, so you can still use Serial.print() to the Arduino Serial Monitor.  Arduino’s “Serial ports” list is still present, where you would select “(emulated serial)” to use those other boards.  The new “Teensy” ports list allows you clearly see which boards are really connected and to precisely choose the one you want.

Selections from this new Teensy Ports list are based on the physical USB port, plus any USB hubs.  This info is shown in the lower right corner.

Here “usb2/2-1/2-1.2/2-1.2.2” is a Linux syntax meaning port 2 of a hub plugged into port 1 of the 2nd USB controller.  Similar codes are used on Windows and Macintosh.  These physical location codes allow Arduino to target exactly the board you’ve selected, even when it changes USB type.

Physical location allows Teensy auto-reboot to know exactly which Teensy you wish to upload.  Previously (and still if you don’t select from the Teensy ports list) attempting to reboot required searching and trying to reboot whatever boards were found.  If your Teensy isn’t responding to USB (interrupts disabled, deep sleep, etc) the auto-reboot process won’t search for other boards.  It ends quickly.  Of course no software on a PC or Mac can get your Teensy to report if it’s not communicating on the USB, which is why every Teensy is made with a button to force entering programming mode.

When you open the Arduino Serial Monitor with a board selected in the Teensy Ports list, a special version of the serial monitor customized to Teensy is used.

You can easily tell it apart from the normal serial monitor because it lacks the baud rate drop-down selection.  Teensy USB always communicates at full USB speed, not the serial baud rate.

This new serial monitor has many features you can’t easily see.  If you unplug your Teensy while it’s open, the USB disconnect is automatically detected.  Likewise, reconnecting (on the same physical USB port) is automatically detected.  Access to the hardware is done by a native “teensy_serialmon” helper program, rather than a Java serial library, which is meant to solve rare but difficult communication problems some people encounter.

If you want to use the old way, via the Java serial library, just select from the “Serial ports” part of the Ports menu.  Look for the baud rate drop-down list to confirm you’re using the traditional serial monitor.

256K RAM Usable on Teensy 3.5

Some NXP/Freescale’s documentation for the MK64FX512 chip used on Teensy 3.5 says 192K of RAM.  Other documentation says 256K.  We have recently confirmed all these chips really do have 256K RAM.

Teensyduino 1.42 enables access to all 256K, except the last 8 bytes.  Teensy Loader looks at the initial stack address to deduce which board you selected when compiling.  Future versions may improve how the intended board is communicated.  With 1.42, you get to 65528 more bytes of RAM for variables on Teensy 3.5.

USB Touchscreen Emulation

Many modern PCs have touchscreens, which is distinctly different then simply a mouse, because they can track the position of up to 10 fingers.

Starting with 1.42, Teensy can emulate a 10-finger tracking touchscreen.  This quick video demo shows how it works.

After installing 1.42, click File > Examples > Teensy > USB_Touchscreen > TenFingerCircle to open the example used in this video.

Windows, Linux and some Android systems support multi-touch screens.  Unfortunately no Apple Macintosh computers recognize USB multi-touch devices yet.

Audio Library New Features

Many great new features have been added to the Teensy Audio Library for 1.42.  For this article I had wanted to shoot video demos, but the time required would mean holding up the 1.42 release.  For now, here’s a quick summary with photos.  I hope to bring you another detailed article next week with more details.

Audio: FreeVerb

FreeVerb was added, in both mono and stereo.  It implements the high quality reverb algorithm as published by “Jezar” at Dreamport.  FreeVerb’s quality is better than the reverb effect contributed a couple years ago by Joao Rossi FIlho.  However, it uses more memory.  The stereo FreeVerb requires Teensy 3.5 or 3.6, due to RAM usage.

FreeVerb has 2 tunable parameters for “room size” and “damping” to give you control over the effect.  Of course, you can also add a mixer using the FreeVerb “wet” output with the original “dry” signal to tune how strongly the reverb effect is heard.

Audio: Granular Pitch Shift & Freeze

John-Mike of Bleep Labs contributed a granular processing effect.  In the freeze mode it repeatedly replays a short segment of the sound you recently heard.

Pitch shift mode captures grains continuously and plays them back windows overlapping, interpolated to a different speed.  When the parameters are set well, it results in a pretty good real-time pitch shift.  Of course you can also set the parameters not-so-well if you wish to hear a very grainy output!

Audio: Waveforms

The generic waveform synthesis was greatly improve for 1.42, inspired by Bleep Labs, though in the end new code was written.

Variable Triangle has been added to the waveform synthesis, which allows you to change continuously from a sawtooth to triangle.

A long-standing bug with the phase(angle) function has been fixed in 1.42.  You can now create 2 or more waveforms and control their relative phase shift.  Of course all of the waveforms work down to nearly zero, so any can be used as low frequency oscillators (LFOs) to control or sequence effects or other synthesis, now with full control over relative phase timing.

A new modulated waveform synthesis object has also been added.

Previously only Sine_FM offered modulation.  It was limited to one octave of frequency change, and the modulating signal varied the waveform period, which isn’t the proper “volt per octave” model.

The new waveform modulation allows you modulate the frequency of any of the 9 waveforms.  Up to 12 octaves are available (the range is configurable), allowing you to modulate even a sub-sonic LFO all the way up to the top of human hearing range!  The modulating signal uses proper exponential “volt per octave” scaling.

You can also configure the modulation to affect the waveform phase, for 8 of the 9 waveforms.  The amount of phase shifting is also configurable, up to 9000 degrees (±25 full waveform cycles).

Waveform modulation also has a 2nd input, to allow any signal to modulate the Pulse duty cycle and Variable Triangle waveform shape.

Audio: Pulse Density Modulated (PDM) Input

Some very low cost microphones have a special 1-wire pulse density output signal, which must be low-pass filtered to recover the audio signal.

Teensy now supports these PDM signals.  The low-pass filter is implemented as a single 512-tap FIR filter, which should have much better passband performance than the Cascaded Integrator Comb (CIC) filters typically used.  But this does come at a computation cost, approximately 39% CPU usage when running Teensy at 96 MHz.

Audio: Other Improvements

Support for the WM8731 codec chip has been improved, with the ability to properly select between its microphone vs line input.

The envelope effect now offers status functions to tell if it’s active, and whether its sustain period as ended.  When you use multiple envelopes to create (polyphonic) notes from oscillators or continuous other sound sources, these can help you better select which envelope is not currently “busy” to avoid truncating in-progress sounds.

The WAV file player and wav2sketch utility have been updated to handle unusual WAV files containing junk sections or other metadata before the format section.  Now you can use these WAV files without having to convert them.

A simple “amp” object has been added, meant for switch signals, or amplifying or attenuating.

While this functionality has long been available by using only 1 channel of a mixer, consistent feedback from users has shown that placing a mixer into a graphical design simply does not feel right.  Of course, the amp implements the cases of gain=0 and gain=1.0 by skipping all the math for efficient switching of signals.

Updates have also been made to audio library documentation in the design tool (right side panel).  As the audio library continue to grow, documenting its many features well is becoming ever more important.

Compile Speedup

1.42 has changes to speed up fully recompiling your project.  When Arduino prints “Build options changed, rebuilding all”, you’ll still have to wait, but hopefully not nearly as long.

The main improvement comes from removing “#include <algorithm> from wiring.h”.  Version 1.41 brought a greatly improved map() function which automatically detects if the variable you’re translating is an integer or floating point number.  This magic depends on C++14 features, so wiring.h had this and other includes added.

It turns out nearly all of the extra compile time is due to that one #include <algorithm> header.  It has been removed, and map() still works, still automatically detecting integer versus floating point.

1.42 also changes the build process to use a pre-compiled header for Arduino.h (which in turn includes wiring.h).  This offers some additional speed improvement, but only about a 20% reduction for most programs.

Teensy Loader Improvements

Teensy Loader has a little-known feature to show you detailed information.  Few people know of this because it’s hidden in a place nobody would look, the Help menu.

The Verbose Information window now shows events from the helper programs Teensyduino uses from Arduino, as well as the events from within Teensy Loader itself.

The events are now timestamped with millisecond resolution.  Normally this level of detailed logging isn’t needed, but when “strange” USB problems occur, a log of all the events from every software component can really help.

Teensy Loader has only a few dialog boxes.  The most important ones, alerting you to problems like the wrong board, have long been non-modal to prevent blocking event logging and responding to Arduino.  Help > About and File > Open are also non-modal, completely eliminating modal behavior.

Teensy Loader’s internal graphics handling and memory management were improved.

Miscellaneous Improvements and Fixes

USB Host support on Teensy 3.6 received small improvements.  KurtE updated the Joystick support.  A bug impacting certain hubs was also fixed.

The Serial boolean, used to check if the Arduino Serial Monitor is open, has been improved.

Teensy’s support for X-Plane flight simulator received a fix to FlightSimFloat on Teensy 3.5 and 3.6.  The Flightsim+Joystick USB type was also updated, fixing a problem where it would not be recognized by the TeensyControls X-plane plugin

A small speedup to analogWrite for DAC pins was made.

The startup delay in Teensyduino’s initialization code was reduced from 400 to 300 ms, and changes were made to begin USB enumeration sooner.  While instant startup might seem highly desirable, too-fast startup tends to cause compatibility issues with many Arduino libraries, which do not properly wait for external hardware – because all Arduino boards have slow startup.

A bug in DMAChannel.h transferSize() affecting Teensy LC was fixed.

USB Keyboard KEY_MEDIA_RANDOM_PLAY was fixed.

EthernetClient received a fix for forced connection close.

A subtle timing problem in OctoWS2811 affecting Teensy 3.5 was fixed.

When compiling on 32 bit Teensy boards, “narrowing conversion” is now treated as only a compiler warning, not an error, as has always been done on 8 bit boards.  This allows some poorly written libraries to “just work” even though their code is a bit sloppy.

Ethernet.init(cspin) is now documented in all the Ethernet examples, and on the Ethernet page.  This function is an Adafruit extension which PJRC adopted for Teensy’s version of the Ethernet library, but until now it wasn’t actually documented.

Libraries ADC, OneWire, PS2Keyboard, SerialFlash, Time, TimeAlarms (included in the Teensyduino installer) were updated.

The Macintosh version is now 64 bits software, as required by the newest High Sierra and future versions of MacOS.

Arduino Versions Supported

Support for Arduino 1.8.2 and 1.8.3 and 1.8.4 has been dropped.  The new ports menu and serial monitor are only implemented on Arduino 1.8.5.

Teensyduino is continuing to support 3 old versions of Arduino.  Arduino 1.8.1 was the last version before major changes in Arduino’s “arduino-builder” program.  Arduuino 1.6.5-r5 was the last version before “arduino-builder”, where the entire build process is controlled by the Java code in the Arduino IDE.  Arduino 1.0.6 was the last version of the very old 1.0 series.

Arduino appears to have entered a period of slower release.  Through 2016-2017, Arduino made 12 stable releases.  Since releasing 1.8.5 in September 2017, they started a 1.9 beta but haven’t made any non-beta releases.  If this trend continue, we may explore supporting specific 1.9 beta versions.

At the recent San Mateo Maker Faire, Massimo Banzi announced a developer summit.  PJRC will be participating.  My personal hope is we can move the entire Arduino ecosystem forward with contributions like EventResponder.  I plan to write more detailed articles about this effort as it develops.

Recently work was also done to support Linux 64 ARM (Aarch64), testing on nVidia Jetson TX2.  While Aarch64 support is still considered “experimental” and not part of the stable 1.42 release, if you’re interested in running on Linux 64 bit ARM, please see the last 1.42 beta for an Aarch64 build that’s essentially the same as this 1.42 release.

rudRemote, Radio Control for Multiple Vehicles

Rudolph thought it would be nice to have one radio transmitter that could talk to all of his creations, so he created rudRemote.

rudRemote uses a NRF24L01+ radio module and a Teensy 3.2 to interface the controls.  Almost any type of housing can be used.  In this case Rudolph used a 40-year-old transmitter that he found and added an OLED display.  The controller uses CRTP (Crazy RealTime Protocol) so he could use it to fly his CrazyFile quadcopter.

This HackADay article also talks about the project.

Code for the project can be found on GitHub.

Glitch Delay Eurorack Module

Scott Pitkethly (aka unicornpower on the forum & cutlasses on his blog) designed Glitch Delay, a DIY Eurorack module that fits nicely inside a lunchbox.

The effect consists of a standard delay line, or delay buffer, with multiple read heads that each read the audio in a different way. There is a feedback path, so the effected signal can be feedback into its self.

There are 2 types of read head:

Loop heads – These heads loop small sections of audio. There are 3 of these. One that plays the audio an octave lower, one at the original octave, and one an octave higher. The size of each of these loops can be adjusted (size dial), as can the amount the loops move each time the loop starts again (jitter dial)

Reverse head – This head plays the buffer in reverse at the original octave.

The top white button allows you to set a tap tempo. This forces the looping heads to jump to a new position on every beat.

The bottom white button is the ‘freeze’. This freezes the write head. No new audio will be written into the buffer, the old audio will remain. This essentially ‘locks-down’ the audio, so it can be tweaked without the buffer changing.

A bit more information can also be found on his blog.

Code and schematics for the project can be found on GitHub.

Horten Fyr Art Installation

Rasmus Hildonen and Skomakerstuen created a beautiful LED installation at the Horten Lighthouse in Norway.

The installation features over 3,000 WS2812b LEDs in 16 windows.  The LEDs react to an ultrasonic distance sensor (HRXL-MaxSonar-WR) under the pier that is used to measure tides and wave heights.  A Teensy 3.2 and a OCTO28 Adapter along with the FastLED Library are used to control the LEDs.

This video shows some of the behind the scenes work.

There is some additional discussion about the project here and here.

USB Hub Bug Hunting & Lessons Learned

This tale begins with a customer reporting this cute little 2 port USB hub wasn’t working with Teensy 3.6.

In this article I’ll show how a protocol analyzer is used, how my instincts turned out to be very wrong, and along the way dive into arcane USB details you probably won’t see explained anywhere else.

First Instincts: Fail

This article is being written from the perspective of hindsight.  You can much of the process as it actually happened on the forum thread where this problem was reported.

Most of my first troubleshooting instincts revolved around these 2 questions:

1: This hub works on Linux, Mac & Windows.  What are they doing that Teensy isn’t?

2: All other hubs work on Teensy, so what’s different about this particular hub?

I believe most people would start with these 2 questions.  In the end, both turned out to involve very wrong assumptions that were ultimately a distraction from finding the real problem.

Protocol Analyzer: Fail (Software Crash)

Usually the first step in debugging USB problems is to hook up the protocol analyzer.  I have this one, the Beagle480 from Total Phase.

The way this works is pretty simple.  On the left side where I drew the 2 blue arrows, your USB communication passes through.  To whatever you’re monitoring, it just looks like a longer cable.  But it makes a copy of all communication happening in either direction and sends it to the port on the right (green arrow).

If you’re just making a USB device that plugs into your PC, usually you can run software to capture the data from your PC’s driver.  But when you’re making an embedded USB host like Teensy 3.6’s 2nd USB port, this is the only way to actually see what’s really happening.  The downside is the cost.  Total Phase sells this one for $1200.

Here’s the test setup, with the Beagle480 inserted between the Teensy 3.6 and 2-port hub.  Usually quite a clutter of cables is involved, but this little hub plugs right into the side of the Beagle480.  The hub has 1 keyboard connected.  A Mac laptop is watching the USB communication, and a Linux desktop is running the Arduino IDE to upload code to Teensy.

Despite the high price, it’s not made of magic.  Inside it has a fairly small buffer memory.  It has to send a copy everything on that right side port, which can be problematic during periods of sustained fast data flow.

This case turned out to be one of those problematic situations.  Unfortunately Total Phase’s software does not handle this situation well.  The Linux version simply crashes.

The Mac version is better.  It hangs with the infamous Mac beachball.  A Force quit is needed.

Fortunately having the software lock up means at least something can be seen on the screen, even if I could not scroll up to look at any other data.   Whatever was going wrong with this hub was causing a SETUP token to be repeatedly tried.  The timestamps show it’s happening about every 8 to 12 microseconds!

What’s Different About This Hub?

With the protocol analyzer not helping much, I decided to first focus on question #2.  All the other hubs work.  What’s different about this one?

In USBHost_t36.h on line 59 is an option to turn on lots of verbose debug printing.  But it doesn’t print much about the USB descriptors.  I decided this was a good time to fix that (rather than just plug into a PC and run a program to view), optimistically hoping the info would show some stark difference between this bad hub and all the good ones.  I spent a few hours adding this code to print descriptor info.

Here is the configuration descriptor for this 2 port hub.

Configuration Descriptor:
  09 02 29 00 01 01 00 E0 01 
    NumInterfaces = 1
    ConfigurationValue = 1
  09 04 00 00 01 09 00 01 00 
    Interface = 0
    Number of endpoints = 1
    Class/Subclass/Protocol = 9(Hub) / 0 / 1(Single-TT)
  07 05 81 03 01 00 0C 
    Endpoint = 1 IN
    Type = Interrupt
    Max Size = 1
    Polling Interval = 12
  09 04 00 01 01 09 00 02 00 
    Interface = 0
    Number of endpoints = 1
    Class/Subclass/Protocol = 9(Hub) / 0 / 2(Multi-TT)
  07 05 81 03 01 00 0C 
    Endpoint = 1 IN
    Type = Interrupt
    Max Size = 1
    Polling Interval = 12

Turns out this little 2 port hub is a Multi-TT type.  I’ll talk more about the transaction translators soon.  Most hubs have only a single TT, but 2 of my test hubs are Multi-TT.  Here’s one of their descriptors.

Configuration Descriptor:
  09 02 29 00 01 01 00 E0 32 
    NumInterfaces = 1
    ConfigurationValue = 1
  09 04 00 00 01 09 00 01 00 
    Interface = 0
    Number of endpoints = 1
    Class/Subclass/Protocol = 9(Hub) / 0 / 1(Single-TT)
  07 05 81 03 01 00 0C 
    Endpoint = 1 IN
    Type = Interrupt
    Max Size = 1
    Polling Interval = 12
  09 04 00 01 01 09 00 02 00 
    Interface = 0
    Number of endpoints = 1
 Class/Subclass/Protocol = 9(Hub) / 0 / 2(Multi-TT)
  07 05 81 03 01 00 0C 
    Endpoint = 1 IN
    Type = Interrupt
    Max Size = 1
    Polling Interval = 12

Turns out they’re an exact binary match, except for the last byte in the configuration descriptor.  I can’t emphasize enough how this process involves looking stuff up in the USB 2.0 PDF, in this case page 266.  This byte is just the hub telling the PC how much power is might draw.  The 2 port hub say 01, meaning only 2 mA which can’t be right.  But I know nothing in code on Teensy (which I wrote) ever look at this byte.  Maybe someday we’ll detect power issues, but for now it’s ignored.

Hubs also have a special descriptor for their capability.  It’s documented on page 417-418 of the USB 2.0 PDF.  Here the 3 different hubs did differ.

2 port (not working)
09 29 02 09 00 32 01 00 FF


4 port, ioGear, GUH274 (works)
09 29 04 89 00 32 64 00 FF


4 port, no-name blue color, UH-BSC4-US (works)
09 29 04 E0 00 32 64 00 FF

While I now know these differences are meaningless, and really only the 3rd byte (the number of ports) matters, at the moment I found these it seemed like this just had to explain the problem.  That 2 port hub is different.

Turns out the 4th byte describes features like which LEDs, over-current protection and other features the hub has.  The 7th byte says how long we’re supposed to wait between enabling power and accessing a port, but the bad 2-port hub says only 1 ms, much less than the other 2 working hubs.

What Would Linux Do?

With nothing apparently different between the hub, I returned to the idea that Teensy must be missing some initialization or other step that hasn’t mattered for all the other hubs, but does for this one.

It’s easy to look at what Linux does, since nothing goes wrong that crashes the Beagle480’s software.  Here’s most of the hub’s enumeration process on Linux:

I quickly noticed Linux is sending this Set Interface control transfer.  I did testing with all 7 of the hubs I have.  This doesn’t happen on the USB 1.1 hub or the 3 that are Single-TT, but Linux does do it for all the Multi-TT hubs.

Adding this to the USBHost_t36 library took time.  The hub driver was only looking at the first 16 bytes, to see if the interface and endpoint are compatible.  I had to rewrite the hub driver to look for all the possible interfaces.  Then when it was able to detect if 2 or more exist and parse which is the best to use, I added code to actually send that Set Interface command.

It didn’t make any difference.  I’d poured a few more hours into coding, which in the long term was probably worthwhile, but still no closer to solving the problem!

Working Around Data Capture Software Lockup

I seriously considered resigning this problem to my low-priority bug list, since all the other hubs work.  I considered restructuring the enumeration code to even more closely match Linux’s approach.  I looked over the descriptor data yet again, hoping to find the answer, but no.

Clearly I needed to get around the problem with Total Phase’s software locking up.  At first I just added a “while (1) ; // die here” right before calling new_Device() to start the enumeration process after the hub detects the keyboard.  But that didn’t reveal anything useful.

I needed to let the problem happen, but then shut off the USB port before the Beagle480’s buffer could overfill (or whatever other problem causes the Total Phase software to lock up).  Ultimately I did this:

    if (state == PORT_RECOVERY) {
        port_doing_reset = 0;
        print("PORT_RECOVERY, port=", port);
        println(", addr=", device->address);
static IntervalTimer stoptimer;
stoptimer.begin(panic, 1000);
        // begin enumeration process
        uint8_t speed = port_doing_reset_speed;
        devicelist[port-1] = new_Device(speed, device->address, port);

When I put in terrible debug code, I like to give it very odd indenting so I’ll see it when/if I later forget to remove it!  In this case, a hardware timer is started to generate an interrupt in 1 millisecond, which calls panic().

The panic() function looked like this:

void panic()
{
    GPIOE_PCOR = (1<<6); // turn off USB host power
    Serial.printf("Panic stop\n");
    while (1) ;
}

With this I was finally able to see the beginning of the problem.

Somehow the bad 2 port hub was indeed working, at least for the first control transfer which reads 8 bytes of the device descriptor.  But then everything goes wrong and nothing works.  The host controller keeps trying to resend the Set Address command… at least for 1 millisecond, until the panic() function abruptly shuts off the USB port’s power.

Realizing What’s Really Wrong

I wish I could tell you there’s a reliable process to go from actually being able to observe the problem to understanding it.  There isn’t!

In this case I spent several hours re-reading the USB 2.0 spec, staring at that screenful of info, and basically just guessing.  For a while I considered there might be some sort of electrical interference problem unique to this hub, somehow triggered by completing that first transfer.

It literally took hours to focus on this 1 byte which turned out to be the cause of so much trouble.

To understand this byte, you need to know about USB split transactions, which are probably among the most arcane of USB features.  This next part will be quite technical, so perhaps skip to the end if you’re not in the mood for intensely low-level USB protocol details.

Split transfers are used only between USB hosts and hubs.  If you’re making a USB device, you’ll never see this type of USB communication.  They’re used only when a host at 480 Mbit speed needs to communicate with devices at 12 or 1.5 Mbit.

When USB 3.x is used, all the 5+ Gbps communication happens on dedicated wires.  The cables & hubs have a redundant 480 Mbit path. When you use a 12 or 1.5 Mbit device on a USB 3.0 hub, that slow communicate is done using split transactions on the 480 Mbit line.  Slow speeds are never translated to the 5 Gbps lines.

USB has a lot of very specific terminology.  From smallest to largest, communication is in Tokens, then Transactions, then Transfers.  I’m not going into details here, other than it’s important to remember we’re dealing with 3 starts-with-T junks of data.  Tokens are the smallest.  Transactions are bigger, made up of Tokens.  Transfers are the largest, maybe of of Transactions & Tokens.

These SPLIT tokens communicate with a Transaction Translator (TT) inside the hub.  When the host wants to send a transaction at 12 or 1.5 Mbit, it sends a SPLIT-START token to the hub’s TT.  If the TT isn’t busy, it acknowledges and begins working on the slow communication.  That sequence of tokens is a transaction, also called split start (maybe confusing if you don’t know the context).  Later the host sends SPLIT-COMPLETE.  If the TT has finished, it replies with ACK, and if it’s still busy it replies with NYET.

Here’s the diagram from the USB 2.0 spec on page 202 showing this for OUT transactions.  Sadly there isn’t a diagram specifically for SETUP transactions, but my guess is this should be pretty close.  Did I mention this sort of work is all about guesswork and re-reading technical specs over and over?

Total Phase’s software is very nice.  It normally shows you one Transfer per line.  If you want to see the Transactions that made up that transfer, you click the little triangle to expand.  Likewise if you want to drill down to see the actual tokens, you can.  Here’s the first successful transfer.  Hopefully you can see this more-or-less corresponds to Figure 8-9, except SETUP instead of OUT.  Of course, we only see the stuff from circles #1 & #3 since we’re connected between Teensy and the hub.

In this case, we can see Teensy sent 58 SPLIT-COMPLETE tokens while the hub slowly send that SETUP token at 1.5 Mbit to the keyboard, where the hub replied with NYET to the first 57, then finally an ACK.

This is important, because the process of figuring out why something isn’t working almost always involves first looking at the deeper details of what it’s supposed to do when it does work.

With that in mind, here’s an expanded view of the first Set Address transfer that fails.

Remember the software is showing us nesting of Tokens inside Transactions inside Transfers.  So the 3rd line is actually the first real data.  This badness begins with the bytes 78 01 82 B4.

Earlier 78 01 82 10 was considered ok.  But 78 01 82 B4.  So what does that last byte mean?  Again the answer is digging into the nitty gritty details in the USB 2.0 document.  It happens to be on the same page as that diagram above.

Even though going from 10 to B4 looks like a big change, in fact the top 5 bits are a CRC check which is expected to be totally different if any of the checked bits changed.  So really this means just one of the “ET” bits flipped from a 0 to 1.

The meaning of those ET bits is documented a few pages later.

These bits were 00 for Control, but somehow became 10 for Bulk.  That’s definitely not right!  We’re sending a SETUP token, and that token has the endpoint number encoded within (also documented elsewhere in chapter 8….)

Where This 1 Bit Went So Wrong

The good news is this last part, going from an understanding of what’s wrong to finding the mistake causing it, is relatively easy.  It took only about an hour.  While I didn’t know exactly what controlled those ET bits, I was pretty sure it was something in the EHCI controller’s QH or qTD data structures.

USB controllers implementing EHCI are very unlike traditional embedded peripherals, where a handful of fixed register addresses control everything.  EHCI gets almost everything from main system RAM, where it’s regularly using DMA to read linked lists and binary tree data structures to find out what work you want it to perform.

For each pipe/endpoint each USB device, you create a QH structure (except complexities for isochronous… but trying to keep this simple).  Here’s the EHCI diagram for the QH structure.

The first field is used for the linked lists or inverted tree pointers.  The next two are where you actually configure how the hardware will communicate.  The big yellow area is written by the hardware, coping other info there temporarily as it works on the Transfers you want.  (Yes, the hardware deals in Transfers and automatically does the Transactions & Tokens)

While there’s a lot of small fields in those 64 bits, the “C” bit turned out to be the key.  The EHCI spec documents it as:

Control Endpoint Flag (C). If the QH.EPS field indicates the endpoint is not a high-speed device, and the endpoint is an control endpoint, then software must set this bit to a one. Otherwise it should always set this bit to a zero.

Fortunately the code uses C structs to with distinctive names to represent the QH and other EHCI data structures.  A quick grep of all the code turned up everything that accesses this field.  Only a few of them actually write to it.

Ultimately the bug turned out to be just this 1 line function called pipe_set_maxlen()

pipe->qh.capabilities[0] = (pipe->qh.capabilities[0] & 0x8000FFFF) | (maxlen << 16);

Of course if you look at the 2nd line in the QH documentation, this is incorrectly zeroing too many bits, destroying the C bit and part of the RL field.  It should be:

pipe->qh.capabilities[0] = (pipe->qh.capabilities[0] & 0xF800FFFF) | (maxlen << 16);

Indeed applying this fix made the “bad” 2-port hub work quite nicely.

Headslap Moment

After I found the fix, I looked one last time at the question “All other hubs work on Teensy, so what’s different about this particular hub?”

Here’s the communication with that keyboard through one of the “good” hubs!

Turns out the code has been sending improper SPLIT-START tokens all along, but every hub I own and the numerous hubs other people have used all were able to work anyway!  The different was this particular hub was actually rejecting the bad tokens.

In all this time, developing this library earlier this year and all this recent work to track down this bug, apparently I never actually connected the protocol analyzer and watched the “good” hubs really communicating with low speed devices!  Could have saved a *lot* of work.  Now I know, in hindsight.

Pinball Machine

Ben Heck of element14 built a portable pinball machine.

In the first video (part 1) Ben discusses the design of the project and puts the electronics together, including writing the code.  In the second video (part 2) he finished building the portable pinball machine.

Code for the project has been published on GitHub.

Anjuna Logo Top Hat

Tim Lukasik made an awesome LED top hat.

The hat was made to wear to Above & Beyond’s Common Ground tour.  Tim used 722 neopixels glued onto a thin foam backing which was then attached to a top hat.  A Teensy 3.2 and a battery pack for power were wired into it.  He programmed the LEDs to display 13 different designs featuring the band’s iconic event logos that rotate in order.

The code for the project has been published on GitHub.