Teensy 3.0 is a 32 bit ARM-based, breadboard compatible development board that you can program using the Arduino IDE. Teensy 3.0 runs sketches much after than 8 bit Arduino boards, has higher performance peripherals, and is available at Adafruit now.
I’m Paul Stoffregen, creator of the Teensy board and software. Phil Torrone asked me to share some of the details of making Teensy 3.0 here on the Adafruit blog.
Click “read more” for details of the latest software update, real-time clock support, touch sensing, bugs recently fixed, development on the XBee, SdFat and FastSPI libraries, and some discussion of issues porting Arduino libraries to run on a 32 bits processor, and remaining challenges to be addressed in the next software updates.
Software Update, Beta #6
As of today, the sixth Teensy 3.0 beta test software is now available. Here are the links.
http://www.pjrc.com/teensy/beta/arduino-1.0.1-teensy3-beta6-linux32.tar.gz
http://www.pjrc.com/teensy/beta/arduino-1.0.1-teensy3-beta6-linux64.tar.gz
http://www.pjrc.com/teensy/beta/arduino-1.0.1-teensy3-beta6-macos.zip
http://www.pjrc.com/teensy/beta/arduino-1.0.1-teensy3-beta6-windows.zip
This version adds a few important features and fixes several bugs.
Real-Time Clock Support
This software release provides support for Teensy 3.0’s on-chip RTC. To use it, you’ll need to solder a 32.768 crystal to the board. A 3 volt coin cell is optional, but without the battery the RTC will lose track of the date and time when power is turned off.
The recommended crystal is Digikey 300-8303-ND. Any similar 32.768 kHz, 12.5 pF crystal should work. The crystal can be placed on either side of the board, but I highly recommend soldering from the bottom side, to avoid accidentally touching or solder bridging to any of the tiny top-side parts.
To access the clock, use these 3 functions:
Teensy3Clock.get()
Teensy3Clock.set(atime_t)
Teensy3Clock.compensate(num)
The set() function sets the time, using a 32 bit unsigned integer. The common convention is the number of seconds elapsed since 1970. The RTC simply increments the number every second. The get() function gives you the current time.
Once the RTC is running, uploading new code does not reset it. You can click Upload in Arduino over and over, without losing the current time. If a battery is connected, the time will be maintained while power is off.
The compensate() function lets to adjust the speed of the clock slightly. If it’s running too fast, call compensate() with a negative number to slow it down a bit. The adjustment is approximately the ppm/8. If you use compensate(-40), the clock is slowed by 5 ppm (parts per million).
If you know how your crystal response to temperature, you could measure the temperature with analogRead(38) and use the results to change the crystal compensation as the temperature changes!
These 3 functions alone aren’t very usually very interesting. To handle time and date in hours, minutes, seconds, days, months and years, Michael’s Margolis’s Arduino Time library is very helpful. The get() function is intended to be used with Time’s setSyncProvider(). In File > Examples > Time > TimeTeensyRTC is a simple example.
Over the last few days, Michael and I have talked about how to port his Time to ARM. The Time library included in this software release is a preliminary port. It should work well, but please be aware future versions may change slightly.
Touch Sensing
The touchRead(pin) function is finally implemented. It works similarly to analogRead(pin). Just call the function with one of the pins that has touch sensing, and it will return a 16 bit number representing the capacitance on that pin, in 1/50th pico-Farad units. If the pin has 40 pF, you’ll get 2000.
The measurement is the capacitive coupling to ground. For human touch interfaces, the best results are when the person’s body is physically connected to ground. With hand-held devices, usually the enclosure has exposed metal for this purpose. It still works reasonably if Teensy’s ground is connected to earth ground, usually through the USB cable. But running from a battery (called “floating”) or connected to a laptop computer running from its battery doesn’t work well.
Of course, it’s possible to implement capacitive sensing using ordinary pins. The CapSense library does it quite well. But Teensy 3.0’s built-in hardware gives you 3 things you can’t get from software-based capacitive sensing.
1: Sensitivity: By default, touchRead() gives 0.02 pF sensitivity.
2: Speed: The measurement time depends on the capacitance. A worst-case measurement takes about 5 ms. Typical capacitances used for human touch typically read much faster. Even when cycling through all the touch sensitive pins, you get excellent sensitivity at very responsive speeds.
3: Stability: The measurement works by comparing the pin’s capacitance to an on-chip reference capacitor. If the power supply voltage, charging and discharging currents or other electrical factors change, their effect on the measurement are largely canceled out by using the same on-chip hardware to measure both the pin and the reference capacitor.
The common use is to replace buttons and sliders with touch-sensitive controls. But with 0.02 pF sensitivity, perhaps touchRead() can be used for other very interesting projects?
Bugs Fixed Since Beta #5
Reproducible bugs reports really, really help. Most of the bugs fixed in this release are due to bug reports I received.
The SPI.setClockDivider() but reported by John and Thomas is fixed.
Many missing files were added, and minor edits made, so Ethernet now compiles. So far, Ethernet is untested on Teensy 3.0. If you do try Ethernet, please keep in mind the Wiznet W5100 chip draws more than the 100 mA current Teensy 3.0 can provide on the 3.3 volt pin. An external regulator or source of power for the W5100 is needed.
I added wiring_private.h, prog_uchar() and other things needed for Adafruit’s ST7735 and Adafruit_GFX libraries, as reported by Joh Lummel. Sadly, there is still a remaining linker error. If anyone has insight into this problem, please contact me.
I added the missing digitalPinToTimer() function needed by the alternate LiquidCrystal library, as reported by Kgrider. This library is still untested, but it now compiles and looks like it should work.
A file naming bug preventing the EEPROM library from working on non-Linux systems was fixed. Thanks to Alex Ferro for reporting this.
XBee Library
I sent a small XBee library patch to Andrew Rapp. It was failing to compile because it assumed “Serial” is of type “HardwareSerial”. On Teensy, Serial is a USB virtual serial. The same is true on Arduino Leonardo. The patch allows XBee to work on both boards.
Andrew and I exchanged a few messages. Many people have wanted to use the XBee library with the software serial library. Even though it’s not very useful for Teensy 3.0 (which has 3 real hardware serial ports), I created a patch for XBee to allow use of software serial, or any Arduino Stream compatible library.
Andrew published the code as a 0.4 beta test. If you’re using an XBee on a regular Arduino board, please give a try and send Andrew some feedback.
http://code.google.com/p/xbee-arduino/
SdFat Library
Beta 4 was the first version to support the SD library (not impacted by the SPI.setClockDivider bug).
Since beta 4, I’ve exchanged several messages with Bill Greiman, author of the SdFat library. Bill has been working on a newer, faster versions. The one that comes with Arduino is fairly old. He sent me a beta test version, and a patch to make it compile in the 32 bit environment.
Teensy 3.0’s SPI port is capable of 24 Mbit/sec speed. That’s 3 times faster than the AVR-based Arduino. It also has a small FIFO and DMA transfer capability. My initial support for the SD library involved emulating the AVR SPI port. I spent a couple days fiddling with native routines, which admittedly were probably only a start at a truly native solution.
Bill optimized the native code and put it in his faster library. Here is a benchmark result he posted.
http://arduino.cc/forum/index.php/topic,121568.msg959812.html#msg959812
I should mention this is probably not even “beta” code at this point. It’s also worth keeping in mind the speeds vary quite a bit from one SD card to another.
Still, it’s pretty exciting seeing such impressive read and write speeds. This could really open up a lot of amazing project possibilities.
FastSPI Library
I also spent some time talking with Daniel Garcia (author of the FastSPI library) and Mark Kriegsman, about driving huge numbers of RGB LEDs. Last weekend they worked with a couple Teensy 3.0 boards, and got them driving LEDs. Here’s a photo Mark sent of the first light, on October 14, 2012:
Later that day, Dan had a strip of 160 LDP8806 LEDs refreshing at 1300 frames/sec. Since then, I’ve heard he has some preliminary support for Teensy 3.0 in the library. I believe some pretty exciting LED driving projects are likely to happen!
I’m personally very interested to try using combinations of DMA channels to synthesize the special waveforms needed by some of 1-wire the addressable RGB LED driver chips. That’s a complex topic that could fill a huge posting all by itself. For now, it’s taking every ounce of self-restraint I have to resist the RGB LED urge….
Issues Running 8 Bit AVR Code on 32 Bit ARM
Since I started working on Teensy 3.0 over a year ago, and especially in the recent months as more people have heard about the project, I’ve regularly been asked what types of problems arise when adapting 8 bit Arduino code to a 32 bit platform.
So far, there are been remarkably few issues involving the different size of int, pointers and other basic data types. Most code is really quite well written. The relatively small number of issues are usually caught by the compiler. But one turned up today….
Jonathan Sorensen reported a problem that turned out to be code that compiles without error, works on 8 bit AVR, but fails when run on 32 bit ARM. It’s a library from Pololu for their LSM303-based Inertial Measurement Unit.
Here is the code. a.x is a float, and xha and xla are bytes from an accelometer. Can you spot the bug, without peeking at the explanation below?
a.x = (xha << 8 | xla) >> 4;
The 2 bytes, xha and xla, were read using the Wire library. Together they form a 2’s complement number. This code reassembles the 2 bytes, divides by 16, and then assigns the result to the float.
Both AVR and ARM combine the two halves to the correct 16 bit result. The hidden gotcha is the right shift operation. You might expect this to simply shift the bits to the right, but what goes into those “empty” most significant bits? It’s not necessarily zeros. On AVR, it’s handled as a signed integer, so if the number is negative, the left-most 1 bit is replicated to the 4 empty bits.
On ARM, it’s also handled as a signed integer. But instead of an integer with a range of -32786 to +32767, it’s an integer with range -2147483648 to +2147483647, which happens to be using a small 0 to 65535 portion of that huge 32 bit range. When the sensor reads positive, the result is correct. But when negative, the result is a wrong positive number.
Even without the right shift, the same problem would occur while converting the integer to a float (and does indeed happen elsewhere in this library). To the compiler, the unsigned logical or only becomes a signed integer if the result exactly matches the size of the result. A type cast is needed, so the desired 16 bit signed integer is always used.
a.x = (int16_t)(xha << 8 | xla) >> 4;
Normally, even code that packs bytes into large variables works fine. But depending on sign extension from an intermediate result with implicit type is a hidden gotcha, which probably isn’t obvious to many programmers.
Missing Features, Coming Soon….
At the top of my to-do list are 2 of the missing USB Types: Keyboard/Mouse/Joystick and MIDI. Teensy 2.0 supports these, and several others. In time, I’ll do them all on Teensy 3.0, plus a few more I have planned.
Also extremely urgent is an AVR-like ISR() macro and interrupt vector table scheme. Since beta 2, attachInterrupt() has worked. But if you want to use any of the interrupts directly, the only way to do so is editing the table in mk20dx128.c. Soon I’m going to rework that file and the linker script, to support a system that look and works very much like what everyone is used to on AVR.
While testing SdFat, Bill Greiman discovered problems with malloc/free. He sent me a very detailed, very helpful description. It turns out the newlib library malloc was designed for and defaults to settings that make sense for a large memory system. It requests memory in 4K blocks, which is very large for such a small microcontroller. I’m going to try porting or reimplementing the avr-libc malloc/realloc/free system, with minor modification for 32 bit word alignment.
The other item on my urgent to-do list, which probably needs some explanation, is Arduino reset emulation. Traditionally, Arduino has used a 2 chip design, where the USB to serial converter is a dedicated chip. When you open the Arduino Serial Monitor, the change on the DTR signal resets the AVR chip, causing your program to restart a fraction of a second after the serial monitor window appears. It’s a very convenient feature (though some people might prefer their board never automatically reset). Lots of Arduino sketches and library example depend upon this automatic reset behavior.
For years, Teensy 2.0 has emulated this 2-chip reset behavior, using only a single chip. It’s tricky. Interrupt-based code parses a restart request, which is sent as the serial monitor window is opening (Teensy doesn’t use DTR, so it the auto-reset isn’t easily happen by accident while using non-Arduino software).
Microsoft’s driver has terrible bugs which are triggered if the device instantly resets before the request is acknowledged, or before a couple more followup requests also complete successfully, so a software-based timer is started. When the timer elapses, a reset is simulated by shutting down all the on-chip peripherals and jumping back to location 0. But the USB port is left operating, as if nothing happened. Then the USB initialization code checks to see if the USB is already running. If it is, an alternate initialization is done. If not, the USB is initialized. All the critical USB state variables are located in a special “noinit” memory section, so the C runtime startup code doesn’t overwrite them. When actual cold USB startup is done, they all have to be written, even if they’re zeros.
If that sounds like an overly complex implementation for a minor feature, perhaps it is? But years of experience with Teensy 2.0 shown it really makes a big difference in usability. It’s also really convenient. I’m going to implement it in Teensy 3.0 soon.
I hope you’ll have a great time using Teensy 3.0.
-Paul
Hi Paul,
Thanks for taking the initiative on this, Teensy 3.0 is so affordable and your support of Teensy solid enough that I’v taken the plunge and am using it as my ARM sandbox.
With the expanded capabilities provided by the K20X128, I’m going to stop (at least for the short term) my search for a suitably capable ARM platform on which to design projects.
Thanks for advancing all of our options with an awesome solution and keep up the great work!
— Joe
I was sad to see the lack MIDI. I bought the Teensy 3.0 for use in a Midi device. The fact that you have worked so hard makes up for it!