Jumping To The Bootloader

Normally when you want to load new code onto your Teensy, you can just press the pushbutton to reboot and run the bootloader. However, if you install a Teensy inside an enclosure where the pushbutton is not easily accessible, you may want a way for your code to jump directly to the bootloader, as if there button were pressed.

Shutting Off Peripherals and Interrupts

Before jumping to the bootloader, you MUST completely disable all interrupt sources and any peripherals you have activated. If the watchdog timer has been activated, it must be turned off.

The USB must be disabled. When the UDCON register is written to 1, the attach resistor which signals the presence of a USB device is disconnected. Within 5 ms, the PC should recognize that no device is connected (even though the cable is still plugged in). A 5 ms delay is advisable after disabling the USB. When the bootloader begins, it will enable and reconnect the USB, so it is important to give the PC time to recognize the disconnect.

The Actual Jump

Jumping to the bootloader requires only a single ASM instruction.
asm volatile("jmp 0x7E00");  // Teensy 2.0

Each board has a different beginning address for the bootloader.

ModelChipJump Address (byte address)
Teensy 1.0AT90USB1620x3E00
Teensy 2.0ATMEGA32U40x7E00
Teensy++ 1.0AT90USB6460xFC00
Teensy++ 2.0AT90USB12860x1FC00

Example Code

This example disables the USB, timers, A/D converter and I/O pin drivers. If other peripherals or the watchdog timer have been activated, they must also be disabled before the jump instruction.

cli();
// disable watchdog, if enabled
// disable all peripherals
UDCON = 1;
USBCON = (1<<FRZCLK);  // disable USB
UCSR1B = 0;
_delay_ms(5);
#if defined(__AVR_AT90USB162__)                // Teensy 1.0
    EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0;
    TIMSK0 = 0; TIMSK1 = 0; UCSR1B = 0;
    DDRB = 0; DDRC = 0; DDRD = 0;
    PORTB = 0; PORTC = 0; PORTD = 0;
    asm volatile("jmp 0x3E00");
#elif defined(__AVR_ATmega32U4__)              // Teensy 2.0
    EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; ADCSRA = 0;
    TIMSK0 = 0; TIMSK1 = 0; TIMSK3 = 0; TIMSK4 = 0; UCSR1B = 0; TWCR = 0;
    DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0; TWCR = 0;
    PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0;
    asm volatile("jmp 0x7E00");
#elif defined(__AVR_AT90USB646__)              // Teensy++ 1.0
    EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; ADCSRA = 0;
    TIMSK0 = 0; TIMSK1 = 0; TIMSK2 = 0; TIMSK3 = 0; UCSR1B = 0; TWCR = 0;
    DDRA = 0; DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0;
    PORTA = 0; PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0;
    asm volatile("jmp 0xFC00");
#elif defined(__AVR_AT90USB1286__)             // Teensy++ 2.0
    EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; ADCSRA = 0;
    TIMSK0 = 0; TIMSK1 = 0; TIMSK2 = 0; TIMSK3 = 0; UCSR1B = 0; TWCR = 0;
    DDRA = 0; DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0;
    PORTA = 0; PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0;
    asm volatile("jmp 0x1FC00");
#endif 

Triggering Ideas

When to run this code and perform the jump depends on the design of your application. A simple approach might be to parse commands received via a virtual serial device.

It is also possible to integrate this jump inside the endpoint 0 interrupt routine, if your design uses endpoint 0 messages. Triggering inside an endpoint 0 interrupt handler has the advantage that it will still be listening (if interrupts are enabled), even when the main program gets "stuck", such as an infinite loop or other state where it isn't parsing input commands.

If you are designing a full device driver, or have direct access by the libusb or winusb libraries, you can send a custom endpoint 0 message.

If you are using a virtual serial device, this code can be embedded inside the endpoint 0 message handlers for baud rate changes, handshake signals, and so on.

If you are using HID, it is possible to use the "set feature" message. You must add the code inside the interrupt routine, and also add the feature report specification the HID report descriptor bytes, so the operating system's HID driver knows your device can accept that type of message.

TODO: write HID FEATURE example and win32 code to trigger it....