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:
- 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.
- 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.
- 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);
}
}