10 CFD on Main Clock with NMI

To use Clock Failure Detection (CFD) on the Main Clock, write the CLKMAIN setting to the Clock Failure Detection Source (CFDSRC) bit field and ‘1’ to the Clock Failure Detection Enable (CFDEN) bit in the Main Clock Control C (CLKCTRL.MCLKCTRLC) register.

This register has Configuration Change Protection (CCP), so the ccp_write_io function in cpufunc.h should be used to ensure correct timing for the unlock of the register.

Figure 10-1. CLKCTRL.MCLKCTRLC – Enable CFD on Main Clock

	/* Enable Clock Failure Detection on main clock */
	ccp_write_io((uint8_t *) &CLKCTRL.MCLKCTRLC,
	             CLKCTRL_CFDSRC_CLKMAIN_gc | CLKCTRL_CFDEN_bm);

To enable the CFD interrupt as a Non-Maskable Interrupt (NMI), write ‘1’ to both the Interrupt Type (INTTYPE) bit and the Clock Failure Detection (CFD) bit in the Main Clock Interrupt Control (CLKCTRL.MCLKINTCTRL) register. This register also has CCP.

Figure 10-2. CLKCTRL.MCLKINTCTRL – Enable CFD interrupt as NMI

	/* Enable Non-Maskable Interrupt for CFD */
	ccp_write_io((uint8_t *) &CLKCTRL.MCLKINTCTRL, 
	             CLKCTRL_INTTYPE_bm | CLKCTRL_CFD_bm);

When the CFD interrupt has been configured as an NMI, the NMI interrupt vector will be called instead of the CFD vector when the interrupt is triggered.

When entering the Interrupt Service Routine (ISR) for the NMI, the interrupt source must be found if more than one NMI source is enabled. The CFD interrupt flag in the Main Clock Interrupt Flags (CLKCTRL.MCLKINTFLAGS) register is ‘1’ if the CFD was the trigger.

Figure 10-3. CLKCTRL.MCLKINTFLAGS – Check CFD interrupt flag

ISR(NMI_vect)
{
	/* Check which NMI has triggered*/
	if(CLKCTRL.MCLKINTFLAGS & CLKCTRL_CFD_bm)
	{
		/* This interrupt will trigger if the source for the main clock fails
		* and the CFD is able to switch to a different working clock.
		* In this case that means XOSCHF has failed and is replaced by OSCHF.
		* The main clock is therefore reduced to 4 MHz / 2 = 2 MHz.
		*/

		/* Toggle the LED forever */
		while(1)
		{
			LED0_toggle();
			_delay_ms(200); // 200 ms calculated from 16 MHz == 1600 ms
		}

	}
	else
	{
		/* A different NMI has been triggered */
	}

	/* Non-Maskable Interrupt bits can only be cleared by a reset */
}

A software reset can be triggered by writing a ‘1’ to the Software Reset (SWRST) bit in the Software Reset Register (RSTCTRL.SWRR). This register also has CCP.

Figure 10-4. RSTCTRL.SWRR – Trigger Software Reset

	/* Software Reset */
	ccp_write_io((uint8_t *) &RSTCTRL.SWRR, RSTCTRL_SWRST_bm);

The code for this example is available in the CFD-on-main-clock-with-NMI folder in these github repositories: