Teensy as Benito, at 57600 baud

By: paul

2010-05-30 11:10:13

At the last Monday meetup, Scott mentioned my Teensy-as-Benito code wasn't working with the '328-based Arduino and Dorkboards, which now run at 57600 baud. So this weekend I dusted off my Dorkboard to investigate. Read on for details, and updated code with a workaround.... The UART (serial port) in these AVR chips generates its baud rate from the main clock, which is usually a very accurate crystal or pretty accurate ceramic resonator. However, it can only divide that clock by integer multiples of 8 or 16. With a 16 MHz clock, you can get close, but not exactly the correct baud rates. Some small error is ok. Usually about 2% to 2.5% is the maximum error before reliability problems occur. The Arduino bootloader is actually using 58824 baud, which is 16e6 / (17 * 16). That's +2.124% error. The UART code in Teensyduino, when 57600 baud is requested, uses 57143, which is 16e6 / (35 * 8). That's -0.794% error. Either can communicate with a FTDI chip or PC, which generates a very accurate 57600 baud rate, but when used together, the total error is too much because Arduino's is 2.1% too fast and Teensy's is 0.8% too slow. As a workaround, I just added a check for when the PC requests 57600 baud, and instead the baud rate is set to 58824, to exactly match the Arduino bootloader's actual baud rate. You can download the updated code. I also added code to blink the LED, and I included pre-compiled copies for Teensy 2.0, Teensy 1.0, and Don's Benito, so you can just load the appropriate code onto your board using the Teensy Loader or DFU programmer. Of course, you can customize it as easily as tweaking any Arduino-based project, because it's built with Teensyduino! Here is the source code:
unsigned long baud = 19200;
HardwareSerial Uart = HardwareSerial();
const int reset_pin = 4;
const int led_pin = 11;  // 11=Teensy 2.0, 6=Teensy 1.0, 16=Benito
const int led_on = HIGH;
const int led_off = LOW;

void setup()
{
	pinMode(led_pin, OUTPUT);
	digitalWrite(led_pin, led_off);
	digitalWrite(reset_pin, HIGH);
	pinMode(reset_pin, OUTPUT);
	Serial.begin(baud);	// USB, communication to PC or Mac
        Uart.begin(baud);	// UART, communication to Dorkboard
}

long led_on_time=0;

void loop()
{
	unsigned char c, dtr;
	static unsigned char prev_dtr = 0;

	if (Serial.available()) {
		c = Serial.read();
		Uart.write(c);
		digitalWrite(led_pin, led_on);
		led_on_time = millis();
		return;
	}
	if (Uart.available()) {
		c = Uart.read();
		Serial.write(c);
		digitalWrite(led_pin, led_on);
		led_on_time = millis();
		return;
	}
	dtr = Serial.dtr();
	if (dtr && !prev_dtr) {
		digitalWrite(reset_pin, LOW);
		delayMicroseconds(250);
		digitalWrite(reset_pin, HIGH);
	}
	prev_dtr = dtr;
	if (millis() - led_on_time > 3) {
		digitalWrite(led_pin, led_off);
	}
	if (Serial.baud() != baud) {
		baud = Serial.baud();
		if (baud == 57600) {
			// This ugly hack is necessary for talking
			// to the arduino bootloader, which actually
			// communicates at 58824 baud (+2.1% error).
			// Teensyduino will configure the UART for
			// the closest baud rate, which is 57143
			// baud (-0.8% error).  Serial communication
			// can tolerate about 2.5% error, so the
			// combined error is too large.  Simply
			// setting the baud rate to the same as
			// arduino's actual baud rate works.
			Uart.begin(58824);
		} else {
			Uart.begin(baud);
		}
	}
}
Back to archive index