Serial.print is not stable when using timer interrupt

Hello.
When using arduino nano 33 IOT and doing serial.print while toggling the LEDs with the timer interrupt, in very rare cases, the result of serial.print may be corrupted. (This does not happen when timer processing is not used.)

Is there any special processing required when using interrupts?

#include <Arduino.h>
#include "Adafruit_ZeroTimer.h"

// This example can have just about any frequency for the callback
// automatically calculated!
float freq = 500000; //

// timer tester
Adafruit_ZeroTimer zerotimer = Adafruit_ZeroTimer(3);

void TC3_Handler() {
  Adafruit_ZeroTimer::timerHandler(3);
}

// the timer callback
volatile bool togglepin = false;
void TimerCallback0(void)
{
  digitalWrite(LED_BUILTIN, togglepin);
  togglepin = !togglepin;
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);

  Serial.begin(115200);
  while (!Serial) {
    delay(10);
  }
  
  Serial.println("Timer callback tester");

  Serial.print("Desired freq (Hz):");
  Serial.println(freq);

  // Set up the flexible divider/compare
  uint16_t divider  = 1;
  uint16_t compare = 0;
  tc_clock_prescaler prescaler = TC_CLOCK_PRESCALER_DIV1;

  if ((freq < 24000000) && (freq > 800)) {
    divider = 1;
    prescaler = TC_CLOCK_PRESCALER_DIV1;
    compare = 48000000/freq;
  } else if (freq > 400) {
    divider = 2;
    prescaler = TC_CLOCK_PRESCALER_DIV2;
    compare = (48000000/2)/freq;
  } else if (freq > 200) {
    divider = 4;
    prescaler = TC_CLOCK_PRESCALER_DIV4;
    compare = (48000000/4)/freq;
  } else if (freq > 100) {
    divider = 8;
    prescaler = TC_CLOCK_PRESCALER_DIV8;
    compare = (48000000/8)/freq;
  } else if (freq > 50) {
    divider = 16;
    prescaler = TC_CLOCK_PRESCALER_DIV16;
    compare = (48000000/16)/freq;
  } else if (freq > 12) {
    divider = 64;
    prescaler = TC_CLOCK_PRESCALER_DIV64;
    compare = (48000000/64)/freq;
  } else if (freq > 3) {
    divider = 256;
    prescaler = TC_CLOCK_PRESCALER_DIV256;
    compare = (48000000/256)/freq;
  } else if (freq >= 0.75) {
    divider = 1024;
    prescaler = TC_CLOCK_PRESCALER_DIV1024;
    compare = (48000000/1024)/freq;
  } else {
    Serial.println("Invalid frequency");
    while (1) delay(10);
  }
  Serial.print("Divider:"); Serial.println(divider);
  Serial.print("Compare:"); Serial.println(compare);
  Serial.print("Final freq:"); Serial.println((int)(48000000/compare));

  zerotimer.enable(false);
  zerotimer.configure(prescaler,       // prescaler
          TC_COUNTER_SIZE_16BIT,       // bit width of timer/counter
          TC_WAVE_GENERATION_MATCH_PWM // frequency or PWM mode
          );

  zerotimer.setCompare(0, compare);
  zerotimer.setCallback(true, TC_CALLBACK_CC_CHANNEL0, TimerCallback0);
  zerotimer.enable(true);
}

int i=0;
int j=65535;
void loop() {
  Serial.print(i++);
  Serial.print(" ");
  Serial.println(j-i);

  //delay(1);
}

There are the following patterns of data corruption.

  1. New-line code is not sent.
    455640 -390106
    455641 -390107455642 -390108
    455643 -390109

  2. Spaces are not printed
    473543 -408009
    473544-408010
    473545 -408011

You try to fire interrupt 500.000 times in a second. Calling the Interrupt handler is a relatively slow operation, entering to interrupt can be tens or handreds controller ticks . Therefore, firing the irq so frequently, controller just do not have time for doing anything else rather than interrupts.

2 Likes

Use the interrupt to set a flag and do your processing externally. A little more difficult to write but much more reliable. If you run the serial faster you are also less likely to get so many errors.

That is once every 2 microseconds!

Thanks for the reply.

The 500000 was an overstated value.
I would like to use a longer period as the response time of the interrupt cycle seems to be slow.

*It is a little unforgivable that it doesn't appear programmatic, though.

Thank you.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.