Confused about Clock Failure Detection feature in the Atmega328PB

I am exploring the extra features of the Atmega328PB compared to the Atmega328P. Currently I am getting confused about how Clock Failure Detection (CFD) is supposed to work.

This is what I did:

Hardware:
I made a breakout board for the AT328PB with an external 16MHz crystal and I put some non-sm capacitors on it, so that I can crash the crystal resonance by touching the leads with a small screwdriver. That crashing works fine. The 5 different colored leds are used for monitoring what's going on in the sketch.

Software:
I use Minicore to have support for the AT328PB
I set the fuses of the AT328PB like this:

And use this sketch:

#define RED     PIN_PD2
#define BLUE    PIN_PD3
#define YELLOW  PIN_PD4
#define WHITE   PIN_PD5
#define GREEN   PIN_PD6

void setup() {
  pinMode(RED, OUTPUT);
  pinMode(BLUE, OUTPUT);
  pinMode(YELLOW, OUTPUT);
  pinMode(WHITE, OUTPUT);
  pinMode(GREEN, OUTPUT);
  XFDCSR = _BV(XFDIE); // Activate Clock Failure Detection Interrupts
  digitalWrite(GREEN, HIGH); // toggle green led to show when running through setup
  delay(200);
  digitalWrite(GREEN, LOW);
  digitalWrite(YELLOW, HIGH);// just a led to indicate setup had been done.
}

void loop() {
  if (XFDCSR & _BV(XFDIF)) { // If clock failure has been detected
    digitalWrite(RED, HIGH);
    delay(100); // this will indicate how fast the clock is running after CFD
    digitalWrite(RED, LOW);
    delay(100);
  }
  else { // normal routine
    digitalWrite(BLUE, HIGH);
    delay(100);
    digitalWrite(BLUE, LOW);
    delay(100);
  }
}

// Interruptserviceroutine Clock Failure Detection
ISR (CFD_vect) {
  static uint8_t i = 0;
  if (i < 10) { // or it will go on forever and hold up the rest of the program almost completely
    i++;
    digitalWrite(WHITE, HIGH);
    delay(500); // does not work within an ISR, so is way too short
    digitalWrite(WHITE, LOW);
    delay(500); // does not work within an ISR, so is way too short
  }
}

The Clock Failure Detection (CFD) itself is working. I can see after touching the capacitor, that the red led starts blinking. The processor runs at 1MHz then instead of 16MHz.

My confusion is about the following:

  1. After CFD the ISR keeps firing forever and there is no way to turn it off. According the datasheet only a reset can clear the ISR flag. It cannot be cleared manually.
  2. The program resets. I can see that my setup is done again (at 1MHz speed) but the chip does not reset. Leds stay on, ports stay in output mode.
  3. Sometimes the ISR keeps firing after CFD without confirmation that Setup or Loop is active. only the white led keeps blinking then.

So for now I can use the mechanism of checking the XFDIF bit to let the program know the clock has crashed, but I have no clue what state the MCU is in after a clock failure. I don't get the purpose of the unstoppable ISR.

[EDIT] I also tried a sketch without the Arduino API's with the same result

#define RED     PD2
#define BLUE    PD3
#define YELLOW  PD4
#define WHITE   PD5
#define GREEN   PD6
#include <avr/delay.h>

int main (void) {
  pinMode(LED_BUILTIN, OUTPUT);
  DDRD |= _BV(PD2) | _BV(PD3) | _BV(PD4) | _BV(PD5) | _BV(PD6);
  XFDCSR |= _BV(XFDIE);
  PORTD |= _BV(GREEN);
  _delay_ms(200);
  PORTD &= ~_BV(GREEN);
  PORTD = _BV(YELLOW);
  sei();
  while (1) {
    if (XFDCSR & _BV(XFDIF)) {
      cli(); // the only way to stop the CFD_vect ISR is to stop all interrupts.
      PORTD ^= _BV(RED);
      _delay_ms(100);
    }
    else {
      PORTD ^= _BV(BLUE);
      _delay_ms(100);
    }
  }
}

// Interruptserviceroutine
ISR (CFD_vect) {
  static uint8_t i = 0;
  if (i < 20) { // or it will go on forever as this ISR keeps firing
    i++;
      PORTD ^= _BV(WHITE);
      _delay_ms(100);
  }
}

Huh. Weird. I guess it's a sort of "The hardware has failed, but I can limp along and tell you about it. But otherwise this is not recoverable."
There's an App Note, but it doesn't say anything useful...

"limping" is even an overstatement. If I just let the CFD_vect ISR toggle a pin and measure the frequency it is firing every 40 us.

Without the ISR my main loop after CFD can toggle a pin every 10us.
With an empty CFD_vect ISR firing on repeat, the main loop is toggling every 160us

So imagine the ISR needs to actually do something and/or my main loop needs to do something, the main loop comes virtually to a halt. Hence my confusion what is the thought behind offering this ISR?
I saw the App Note and the associated demonstration software is only checking the XFDIF bit, which works fine.

Until someone can educate me, I will stay away from the CFD_vect ISR.

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