Expand description

External multipurpose crystal oscillator controller

Overview

The xosc module provides access to the two external crystal oscillator controllers (XOSCs) within the OSCCTRL peripheral.

Each XOSC peripheral can operate in two Modes. It can accept an external clock or can interface with an crystal oscillator. In both cases, the clock must be in the 8-48 MHz range.

When used with an external clock, only one GPIO Pin is required, but when used with a crystal oscillator, two GPIO Pins are required. The XIn Pin is used in both Modes, while the XOut Pin is only used in CrystalMode.

When operating in CrystalMode, the XOSC peripheral provides several configuration options to increase stability or reduce power consumption of the crystal.

The XOSC peripheral can also detect failure of the clock or crystal; and if failure occurs, it can automatically switch to a safe, backup clock derived from the DFLL.

Creating and configuring an Xosc proceeds according to the principles outlined in the clock module documentation. It is best shown with an example.

Example

Let’s start by using clock_system_at_reset to access the HAL clocking structs. We’ll also need access to the GPIO Pins.

use atsamd_hal::{
    clock::v2::{
        clock_system_at_reset,
        xosc::{CrystalCurrent, SafeClockDiv, StartUpDelay, Xosc},
    },
    gpio::Pins,
    pac::Peripherals,
    time::U32Ext,
};
let mut pac = Peripherals::take().unwrap();
let pins = Pins::new(pac.PORT);
let (buses, clocks, tokens) = clock_system_at_reset(
    pac.OSCCTRL,
    pac.OSC32KCTRL,
    pac.GCLK,
    pac.MCLK,
    &mut pac.NVMCTRL,
);

Next, we can create and configure the Xosc in one long chain of methods, using the provided builder API. The final call to Xosc::enable yields an EnabledXosc that can act as a clock Source for other clocks in the tree.

let mut xosc = Xosc::from_crystal(tokens.xosc0, pins.pa14, pins.pa15, 20.mhz())
    .current(CrystalCurrent::Medium)
    .loop_control(true)
    .low_buf_gain(true)
    .start_up_delay(StartUpDelay::Delay488us)
    .enable();

We start by calling Xosc::from_crystal, and we provide the corresponding XIn and XOut Pins, as well as the nominal crystal frequency. We then set the CrystalCurrent level to Medium. The default current level for a 20 MHz signal is actually High, but we opt for a lower current under the assumption that our crystal’s capacitive load is small. Next, we turn on automatic loop control, which should save power, but we also set LOWBUFGAIN to 1. Counterintuitively, this actually increases the crystal amplitude, which increases power consumption, but it also improves stability. We then apply a 488 μs start up delay, to allow the clock to stabilize before it is applied to any logic. Finally, we enable the Xosc.

Next, we wait until the Xosc is stable and ready to be used as a clock Source.

while !xosc.is_ready() {}

Once the clock is stable, we can also enable failure detection. To do so, we must provide the EnabledDfll to act as the backup safe clock. We can also select a divider for the safe clock, so that it loosely matches the Xosc frequency. In thise case, we divide the 48 MHz Dfll down to 24 MHz, which is the closest option to 20 MHz.

xosc.enable_failure_detection(clocks.dfll, SafeClockDiv::Div2);

In the event of a clock failure, the Xosc would be automatically switched to the safe clock, and EnabledXosc::has_failed would return true. If the problem were later resolved, the Xosc could be switched back to the crystal with EnabledXosc::switch_back.

The complete example is provided below.

use atsamd_hal::{
    clock::v2::{
        clock_system_at_reset,
        xosc::{CrystalCurrent, SafeClockDiv, StartUpDelay, Xosc},
    },
    gpio::Pins,
    pac::Peripherals,
    time::U32Ext,
};
let mut pac = Peripherals::take().unwrap();
let pins = Pins::new(pac.PORT);
let (buses, clocks, tokens) = clock_system_at_reset(
    pac.OSCCTRL,
    pac.OSC32KCTRL,
    pac.GCLK,
    pac.MCLK,
    &mut pac.NVMCTRL,
);
let mut xosc = Xosc::from_crystal(tokens.xosc0, pins.pa14, pins.pa15, 20.mhz())
    .current(CrystalCurrent::Medium)
    .loop_control(true)
    .low_buf_gain(true)
    .start_up_delay(StartUpDelay::Delay488us)
    .enable();
while !xosc.is_ready() {}
xosc.enable_failure_detection(clocks.dfll, SafeClockDiv::Div2);

Structs

An external multipurpose crystal oscillator controller
Singleton token that can be exchanged for an Xosc

Enums

Type-level variant of the Xosc operating Mode
Crystal current level
Type-level variant of the Xosc operating Mode
Value-level enum identifying one of two possible Xosc operating modes
Division factor for the safe clock prescaler
Start up delay before continuous Xosc monitoring takes effect
Type-level variant of XoscId representing the identity of XOSC0
Type-level variant of XoscId representing the identity of XOSC1

Traits

Type-level enum for the Xosc operating mode
Type-level enum identifying one of two possible Xoscs

Type Definitions

Type alias for the corresponding EnabledXosc
Type alias for the corresponding EnabledXosc
Type alias for the Xosc input Pin
Type alias for the Xosc output Pin
Type alias for the corresponding Xosc
Type alias for the corresponding Xosc