[Solved] No upload possible after uploading a faulty sketch.

The faulty sketch is based of an working example from Markus Bader at

I experimented with timer registers and interrupt vectors. A logical error must have occurred. After uploading the modified sketch, nothing works.

  • All subsequent uploads will not work (processing.app.SerialException: Error opening serial port 'COM12').
  • After a reset, the serial monitor still shows the result of the last correct upload.

The "bad" sketch contains the following important points:

  • Line 8: Declaration of a pointer to a timer
  • Line 28: Calculation of the timer address
  • Line 93: Calculating the interrupt vector

Is there a way to make the Nano 33 IOT working again?

Here is the modified sketch:

/*===========================================================================
 This sketch illustrates how to set a timer on an SAMD21 based Arduino boards 
 (Feather M0, Arduino Zero - and Nano 33 IOT - should work)
 ----------------------------------------------------------------------------
 TC4 with CC0 = 48000 -> exactly 1 ms interrupt
 ============================================================================*/

Tc* TCn;            // Pointer to TC3 | TC4 | TC5 
#define TIMn 4      // Number of used timer from above (3|4|5)

#define LED_PIN 13  // Blinking with change at each interrupt
bool state = 0;     // Actual state of the LED_PIN

char buffer[64];		// Buffer for sprintf

void setup() {
  pinMode(LED_PIN,OUTPUT); // LED pin, toogled at each interrupt
  Serial.begin(38400);
  while(!Serial) delay(10); 
  Serial.println("Testing SAMD21 at Nano 33 IOT: Timer 3...5");
	//---- Print the addresses of the 3 possible timer ----
  sprintf(buffer,"TC3=0x%08lXU",(long)TC3);
  Serial.println(buffer);
  sprintf(buffer,"TC4=0x%08lXU",(long)TC3 + 0x400);
  Serial.println(buffer);
  sprintf(buffer,"TC5=0x%08lXU",(long)TC3 + 0x800);
  Serial.println(buffer);
  TCn = TC3 + (TIMn-3) * 0x400; //---- calculated pointer to used timer
  sprintf(buffer,"TCn=0x%08lXU",(long)TCn);
  Serial.println(buffer);				// Show it at Serial Monitor
  
  tcConfigure(); 			// Configure the timer 
  tcStartCounter(); 	// Starts the timer
}

void loop() {
  //tcDisable(); // This function can be used, to stop/pause the timer
  //tcReset();   // This function should be called if you stop the timer
}

//==== Interup service at all 3 interrupts ====
void TCx_Handler() {

  if(state == true) {
    digitalWrite(LED_PIN,HIGH);
  } else {
    digitalWrite(LED_PIN,LOW);
  }
  state = !state;
  // END OF YOUR CODE
  TCn->COUNT16.INTFLAG.bit.MC0 = 1; // Don't change this
}

//==== This function gets called by the interrupt =====
void TC3_Handler (void) {
  TCx_Handler(); 
}
void TC4_Handler (void) {
  TCx_Handler(); 
}
void TC5_Handler (void) {
  TCx_Handler(); 
}

/*---------------------------------------------------------------------- 
 TIMER SPECIFIC FUNCTIONS FOLLOW
 you shouldn't change these unless you know what you're doing
	----------------------------------------------------------------------*/

//===== Configures the TC to generate output events at the sample frequency.
//      Configures the TC in Frequency Generation mode, with an event output once
//      each time the audio sample frequency period expires.
void tcConfigure() {
 // Enable GCLK for TC4 and TC5 (timer counter input clock)
 GCLK->CLKCTRL.reg = (uint16_t)(GCLK_CLKCTRL_CLKEN 
																| GCLK_CLKCTRL_GEN_GCLK0 
																| GCLK_CLKCTRL_ID(GCM_TC4_TC5));
 while (GCLK->STATUS.bit.SYNCBUSY);

 tcReset(); //reset TCn

 // Set Timer counter Mode to 16 bits
 TCn->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;
 // Set TC5 mode as match frequency
 TCn->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ;
 // Set prescaler and enable TC5
 TCn->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1 | TC_CTRLA_ENABLE;
 // Based off of the system clock and the user defined sample rate
 TCn->COUNT16.CC[0].reg = 48000; //---- Wert für genau 1 ms ----;
 while (tcIsSyncing());
  
 //---- Configure interrupt request ----
 IRQn TCx_IRQ = (IRQn)(TC3_IRQn + TIMn -3);	//---- Calculate vector
 Serial.print("TCx_IRQ="); Serial.println((int)TCx_IRQ);	// Debug only
 NVIC_DisableIRQ(TCx_IRQ);
 NVIC_ClearPendingIRQ(TCx_IRQ);
 NVIC_SetPriority(TCx_IRQ, 0);
 NVIC_EnableIRQ(TCx_IRQ);

 // Enable the TC5 interrupt request
 TCn->COUNT16.INTENSET.bit.MC0 = 1;
 while (tcIsSyncing()); //wait until TC5 is done syncing 
} 

//====Function that is used to check if TC5 is done syncing
//    returns true when it is done syncing
bool tcIsSyncing() {
  return TCn->COUNT16.STATUS.reg & TC_STATUS_SYNCBUSY;
}

//==== This function enables TC5 and waits for it to be ready
void tcStartCounter() {
  TCn->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE; //set the CTRLA register
  while (tcIsSyncing()); //wait until snyc'd
}

//==== Reset TC5 ===== 
void tcReset() {
  TCn->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
  while (tcIsSyncing());
  while (TCn->COUNT16.CTRLA.bit.SWRST);
}

//==== Disable TC5 =====
void tcDisable() {
  TCn->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE;
  while (tcIsSyncing());
}

The solution for my problem is very simple - I found a post in the Nano33 BLE forum with the hint:

  • Press the reset button twice.
    Now my Nano 33 IOT accepts correct sketches again. And I must have a look to my errors...