3.1 Configure the Main Clock to Use XOSCHF

Todo:
  • Edit the CLOCK_XOSCHF_crystal_init() function so that the main clock uses the XOSCHF as a clock source
Info:
Almost all the registers in the Clock Controller (CLKCTRL) peripheral are under Configuration Change Protection (CCP), which means that for write to this register, a certain key must first be written to the CPU.CCP register, followed by a write to the protected bits within four CPU instructions. To make sure this criterion is met when writing to the protected registers, the function ccp_write_io() is used, which can be found in the avr/cpufunc.h file. The function takes two arguments: A uint8_t pointer and the values to be written to the register. The register macros provided by the io.h file are not uint8_t pointers, so typecasting is therefore needed. A useful template showing how to use the function is:
ccp_write_io((uint8_t *) &<register>, new_value)
  1. Configure the XOSCHF to use an external crystal, have a start-up time of four thousand cycles, a frequency range of 16 MHz, to always run, and enable XOSCHF. All this can be done by adding the following to CLOCK_XOSCHF_crystal_init():
    ccp_write_io((uint8_t *) &CLKCTRL.XOSCHFCTRLA, CLKCTRL_RUNSTDBY_bm
    	             | CLKCTRL_CSUTHF_4K_gc
    	             | CLKCTRL_FRQRANGE_16M_gc
    	             | CLKCTRL_SELHF_CRYSTAL_gc
    	             | CLKCTRL_ENABLE_bm);
    
    Info: The frequency range setting determines the maximum frequency for the crystal that is supported. The larger the range, the higher the current consumption.
    Info: The XOSCHF is set to always run so we can verify that the clock source is stable before being used. This setting will later be turned off to save power.
  2. Wait until the external high-frequency crystal is stable by adding:
    while(!(CLKCTRL.MCLKSTATUS & CLKCTRL_EXTS_bm))
    	{
    		;
    	}
    
  3. Set the main clock source to be XOSCHF and enable the clock output pin by writing:
    ccp_write_io((uint8_t *) &CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_EXTCLK_gc
    	             | CLKCTRL_CLKOUT_bm);
    
    Info: By enabling the clock out pin, it is possible to observe the main clock frequency on the CLKCOUT (PA7) pin.
  4. Wait for the main clock to switch successfully by polling the Main Clock Oscillator Change (SOSC) bit field in Main Clock Status (CLKCTRL.MCLKSTATUS) register:
    while(CLKCTRL.MCLKSTATUS & CLKCTRL_SOSC_bm)
    	{
    		;
    	}
    
  5. To save power, clear the Run Standby (RUNSTDBY) bit, so the oscillator is not running when it is not needed:
    ccp_write_io((uint8_t *) &CLKCTRL.XOSCHFCTRLA, CLKCTRL.XOSCHFCTRLA & ~CLKCTRL_RUNSTDBY_bm);
Result: After adding all the code to configure XOSCHF, the CLOCK_XOSCHF_crystal_init() function will look like this:
void CLOCK_XOSCHF_crystal_init(void)
{
    /* Enable crystal oscillator
     * with frequency range 16MHz and 4K cycles start-up time
	 */
	ccp_write_io((uint8_t *) &CLKCTRL.XOSCHFCTRLA, CLKCTRL_RUNSTDBY_bm
	             | CLKCTRL_CSUTHF_4K_gc
	             | CLKCTRL_FRQRANGE_16M_gc
	             | CLKCTRL_SELHF_CRYSTAL_gc
	             | CLKCTRL_ENABLE_bm);

	/* Confirm crystal oscillator start-up */
	while(!(CLKCTRL.MCLKSTATUS & CLKCTRL_EXTS_bm))
	{
		;
	}

    //PORTA.DIRSET = PIN7_bm;
	/* Set the main clock to use XOSCHF as source, and enable the CLKOUT pin */
	ccp_write_io((uint8_t *) &CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_EXTCLK_gc
	             | CLKCTRL_CLKOUT_bm);

	/* Wait for system oscillator changing to complete */
	while(CLKCTRL.MCLKSTATUS & CLKCTRL_SOSC_bm)
	{
		;
	}

	/* Clear RUNSTDBY for power save when not in use */
	ccp_write_io((uint8_t *) &CLKCTRL.XOSCHFCTRLA, CLKCTRL.XOSCHFCTRLA & ~CLKCTRL_RUNSTDBY_bm);

	/* Change complete and the main clock is 16 MHz */
}
Info: The main function will be as follows, containing a call to the CLOCK_XOSCHF_crystal_init() function.
int main(void)
{
        CLOCK_XOSCHF_crystal_init();
        CLOCK_CFD_CLKMAIN_init();
        LED0_init();
        SW0_init();

        /* Enable global interrupts */
        sei();

        /* Replace with your application code */
        while(1)
        {
            LED0_toggle();
            _delay_ms(200);
    }
}