|Shopping Cart Download Website|
|You are here: Teensy How-To Tips Jump Bootloader|
Jumping To The BootloaderNormally 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 InterruptsBefore 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 JumpJumping 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.
Example CodeThis 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 IdeasWhen 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....