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)
- 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. - Wait until the external
high-frequency crystal is stable by
adding:
while(!(CLKCTRL.MCLKSTATUS & CLKCTRL_EXTS_bm)) { ; }
- 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. - 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) { ; }
- 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); } }