4 Configure OSCHF to Run at 1 MHz and Activate/Deactivate the Auto-Tune Feature

The following code example starts the internal high-frequency oscillator at the desired frequency and sets up an interrupt on a rising edge of the signal coming from the button. When the interrupt is triggered, the auto-tune feature is enabled, the microcontroller waits for a second and then deactivates it in order to check the value the auto-tune feature stores in the tuning register.

#define F_CPU                                   1000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define DELAY_TIME                              1000
#define PRESSED                                 1
#define NOT_PRESSED                             0
#define PULL_UP_ENABLE                          0x08
#define BUTTON_PIN                              PIN7_bm

void CLK_init(void);
void PORT_init(void);

uint8_t volatile button_event = NOT_PRESSED;

int main(void)
{
    cli();
    CLK_init();
    PORT_init();
    sei();

    while (1) 
    {
        if(button_event == PRESSED) 
        {
            cli();
            _PROTECTED_WRITE (CLKCTRL.OSCHFCTRLA,(CLKCTRL_FREQSEL_1M_gc|CLKCTRL_AUTOTUNE_bm));
            _delay_ms(DELAY_TIME);
            _PROTECTED_WRITE (CLKCTRL.OSCHFCTRLA, CLKCTRL_FREQSEL_1M_gc);
            button_event = NOT_PRESSED;
            sei();
        }
    }
}

void CLK_init(void)
{
    _PROTECTED_WRITE (CLKCTRL.OSCHFCTRLA, CLKCTRL_FREQSEL_1M_gc);
    _PROTECTED_WRITE (CLKCTRL.XOSC32KCTRLA, CLKCTRL_ENABLE_bm);
    _PROTECTED_WRITE (CLKCTRL.MCLKCTRLA, (CLKCTRL_CLKSEL_OSCHF_gc | CLKCTRL_CLKOUT_bm)); 
}

void PORT_init(void)
{
    PORTC.DIRCLR = BUTTON_PIN;
    PORTC.INTFLAGS = BUTTON_PIN;
    PORTC.PIN7CTRL = PORT_ISC_RISING_gc | PULL_UP_ENABLE;
}

ISR(PORTC_PORT_vect)
{
    button_event = PRESSED;
    PORTC.INTFLAGS = BUTTON_PIN;
}

Tip: The full code example is also available in Appendix.

The PORT_init() function configures Port C Pin 7 (PC7) as an input, enables the internal pull-up and the PORTC interrupt for external GPIO events such as pressing a button.

The CLK_init() function configures all the registers that select the main clock for 1 MHz operation, enables the 32.768 kHz external crystal oscillator, and enables the output of the main clock on CLKOUT pin (PA7).

The Interrupt Service Routine (ISR) for the button sets a flag that is polled in the main loop.

The sei() command enables global interrupts and cli() disables them. This is done to prevent unwanted interrupts from disrupting the _PROTECTED_WRITE macro.

The main loop enables the auto-tune feature for one second, then disables it. This ensures there is enough time for it to correct the error and that the value is stored in the tuning register at the end. Depending on the noise present at the interface between the 32.768 kHz external crystal oscillator and the microcontroller, the tuning time can be higher than one second, but in practice, this time is sufficient under most normal conditions. If the signal is too distorted, as in the case when a finger is placed on the traces or the pins of the microcontroller, the auto-tune feature will not be able to run properly.

The following pictures were obtained using the oscilloscope; Figure 4-1 with normal start-up frequency and Figure 4-2, after the auto-tune feature is enabled.

The line of the code for the correct frequency is:
_PROTECTED_WRITE (CLKCTRL.OSCHFCTRLA, CLKCTRL_FREQSEL_1M_gc);

The line for starting the auto-tune feature is:

_PROTECTED_WRITE (CLKCTRL.OSCHFCTRLA, ((CLKCTRL_FREQSEL_1M_gc)|(CLKCTRL_AUTOTUNE_bm)));
Figure 4-1. 1 MHz Clock Without Auto-Tune
Figure 4-2. 1 MHz Clock With Auto-Tune

The value in the OSCHFTUNE register at the end of the auto-tune process is 0x06.

As the figures show, the error has been corrected and the value is much closer to the required 1 MHz than it was before.