Software Startup

Teensy LC, 3.x, 4.x follow these software startup steps.
  1. Minimal hardware initialization
  2. Call startup_early_hook()
  3. Initialize variables
  4. Configure clocks
  5. Initialize non-USB hardware
  6. Call startup_middle_hook()
  7. Delay 20 ms
  8. Initialize USB
  9. Delay 280 ms
  10. Call startup_late_hook()
  11. Run C++ constructors
  12. Call setup()
  13. Repeatedly call loop()

Hooks

Three hook functions allow your program to customize Teensy's software startup.

Early startup_early_hook() is meant for configuring a watchdog timer or other hardware initialization which must be performed as soon as possible. Because variables are not yet initialized and almost no hardware is configured, the early hook is difficult to use. Usually very low-level code is required.

Middle startup_middle_hook() provides a relatively easy way to run startup code still quite early in the startup sequence. Most Arduino functions work. Arduino libraries with constexpr constructors can be used, including hardware serial (Serial1, Serial2, etc), Wire and SPI, but libraries with regular C++ constructors are not yet initialized.

Late startup_late_hook() lets you perform initialization just before the C++ constructors are run. Because USB is started before this late hook, you can use "while (!Serial) ;" and "Serial.print()". Often creating a middle hook function is easiest with startup_late_hook(), and then change to startup_middle_hook while Serial.print() is no longer needed.

Delays

Teensy's startup code waits 20 ms before initializing USB. This supports the scenario where Teensy rapidly reboots while USB is conncted. The hardware reset disconnects USB, but the USB ports on PCs and USB hubs require several milliseconds to recognize a USB disconnect.

After USB is started, another 280 ms delay is meant to support external hardware which requires startup time. Many devices, especially MEMS motion sensor chips, require time to start up. Most Arduino libraries do not properly handle fast startup. Without this delay, Teensy can appear to not have properly stored your program in non-volatile flash memory, because it functions properly after uploading (with external hardware already running) but then "does nothing" when you power cycle (with external hardware needing initialization).

While not technically part of the startup sequence, Serial.begin() can add 0.75 to 2.0 seconds additional delay. Many example program are written for Arduino Uno or Mega, where a dedicated USB-Serial chip resets the main processor when the Arduino Serial Monitor is opened. These programs often call Serial.print() immediately. For compatibility with these programs, Teensy's Serial.begin() waits up to 2 seconds for the Arduino Serial Monitor to connect, but aborts the wait after 0.75 is no USB communication is heard.

Reduced Startup Delay

extern "C" void startup_middle_hook(void);
extern "C" volatile uint32_t systick_millis_count;

void startup_middle_hook(void) {
  // force millis() to be 300 to skip startup delays
  systick_millis_count = 300;
}

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  digitalToggle(13);
  delay(1000);
}

Watchdog on Teensy 4.0, 4.1, MicroMod

https://forum.pjrc.com/threads/59257-WDT_T4-Watchdog-Library-for-Teensy-4

Watchdog on Teensy 3.2, 3.5, 3.6

#define WATCHDOG_MILLISECONDS 5000

extern "C" void startup_early_hook(void);

void startup_early_hook(void) {
  WDOG_UNLOCK = WDOG_UNLOCK_SEQ1;
  WDOG_UNLOCK = WDOG_UNLOCK_SEQ2;
  asm("nop");
  asm("nop");
  asm("nop");
  asm("nop");
  WDOG_TOVALH = WATCHDOG_MILLISECONDS >> 16;
  WDOG_TOVALL = WATCHDOG_MILLISECONDS & 0xFFFF;
  WDOG_PRESC = 0;
  WDOG_STCTRLH = WDOG_STCTRLH_WDOGEN;
  for (int i=0; i<8192; i++) asm("nop");
}

void watchdog_reset() {
  noInterrupts();
  WDOG_REFRESH = 0xA602;
  WDOG_REFRESH = 0xB480;
  interrupts();
}

void setup() {
  Serial.begin(9600);
}

void loop() {
  watchdog_reset();
  delay(100);
  static unsigned int count=0;
  Serial.println(count++);
}

Watchdog on Teensy LC

extern "C" void startup_early_hook(void);

void startup_early_hook(void) {
  SIM_COPC = 0x0C;   // 1024 ms
  //SIM_COPC = 0x08; //  256 ms
  //SIM_COPC = 0x04; //   32 ms
}

void watchdog_reset() {
  SIM_SRVCOP = 0x55;
  SIM_SRVCOP = 0xAA;
}

void setup() {
}

void loop() {
  watchdog_reset();
}

Measuring Startup on Teensy 4.1

extern "C" void startup_early_hook(void);
extern "C" void startup_middle_hook(void);
extern "C" void startup_late_hook(void);

FLASHMEM void startup_early_hook(void) {
  // pin 0 = AD_B0_03 = GPIO6 bit 3
  IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_03 = 5;
  IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B0_03 = IOMUXC_PAD_DSE(7);
  IOMUXC_GPR_GPR26 = 0xFFFFFFFF;
  GPIO6_GDIR |= (1<<3);  // pinMode(0, OUTPUT);
  GPIO6_DR_SET = (1<<3); // digitalWrite(0, HIGH);
}

void startup_middle_hook(void) {
  pinMode(1, OUTPUT);
  digitalWrite(1, HIGH);
}

FLASHMEM void startup_late_hook(void) {
  pinMode(2, OUTPUT);
  digitalWrite(2, HIGH);
}

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  digitalToggle(13);
  delay(1000);
}

Measuring Startup on Teensy 3.2

extern "C" void startup_early_hook(void);
extern "C" void startup_middle_hook(void);
extern "C" void startup_late_hook(void);

void startup_early_hook(void) {
  WDOG_STCTRLH = WDOG_STCTRLH_ALLOWUPDATE;
  SIM_SCGC5 = 0x00043F82; // clocks active to all GPI
  PORTB_PCR16 = PORT_PCR_MUX(1) | PORT_PCR_DSE | PORT_PCR_SRE;
  GPIOB_PDDR |= (1<<16); // pinMode(0, OUTPUT);
  GPIOB_PSOR = (1<<16);  // digitalWrite(0, HIGH);
}

void startup_middle_hook(void) {
  pinMode(1, OUTPUT);
  digitalWrite(1, HIGH);
}

void startup_late_hook(void) {
  pinMode(2, OUTPUT);
  digitalWrite(2, HIGH);
}

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  digitalToggle(13);
  delay(1000);
}