Expand description

Digital Frequency Locked Loop

The dfll module provides access to the 48 MHz digital frequency locked loop (DFLL) within the OSCCTRL peripheral.

Operation modes

The DFLL can operate in both open-loop and closed-loop modes. In open-loop mode, it uses an internal oscillator to produce an unreferenced, 48 MHz output clock. While in closed-loop mode, the DFLL multiplies a low-frequency input clock to yield a 48 MHz output clock. The reference clock can be provided by a GCLK, through the DFLL peripheral channel clock, or it can be provided by the USB start-of-frame signal.

The DFLL is represented by the type Dfll<M>, where M is one of three Mode types. The default type is OpenLoop, while the other two types, FromPclk and FromUsb, represent closed-loop Modes with the corresponding Reference clock.

The DFLL at power-on reset

Because the DFLL can produce a 48 MHz clock from an internal oscillator, it is used as the system’s default master clock at power-on reset. While most clocks are disabled at reset and represented by items in the Tokens struct, the Dfll is Enabled at reset, so it is found in the Clocks struct.

At reset, the EnabledDfll is in OpenLoop Mode and has one consumer clock, so its complete type is EnabledDfll<U1>. The corresponding consumer is Gclk0, which is represented as EnabledGclk0<DfllId, U1>. Here, the EnabledGclk0 has its own consumer as well, which is the system’s master clock.

Example

Configuring the Dfll proceeds according to the principles outlined in the clock module documentation. Suppose we start with the default clock tree after power-on reset.

DFLL (48 MHz; open-loop mode)
└── GCLK0 (48 MHz)
    └── Master clock (48 MHz)

We would like to transform it to a clock tree like this:

XOSC0 (24 MHz; external clock)
└── GCLK0 (24 MHz)
    ├── Master clock (24 MHz)
    └── DFLL (48 MHz; closed-loop mode)

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, dfll::Dfll, xosc::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 create a 24 MHz Xosc clock from one of the Pins and enable it.

let xosc0 = Xosc::from_clock(tokens.xosc0, pins.pa14, 24.mhz()).enable();

We can then swap Gclk0 from the EnabledDfll to the EnabledXosc. This releases the EnabledDfll and Decrements its consumer count, which allows us to disable it and retrieve the underlying DfllToken.

let (gclk0, dfll, xosc0) = clocks.gclk0.swap_sources(clocks.dfll, xosc0);
let token_dfll = dfll.disable().free();

Next, we can enable the peripheral channel clock, or Pclk, for the Dfll, sourcing it from Gclk0. With the Pclk, we can then recreate the Dfll in closed-loop mode. Finally, we can adjust some of the closed-loop parameters before we enable it. The returned EnabledDfll can be used as a clock Source elsewhere in the clock tree.

let (pclk_dfll, gclk0) = Pclk::enable(tokens.pclks.dfll, gclk0);
let dfll = Dfll::from_pclk(token_dfll, pclk_dfll)
    .coarse_max_step(1)
    .fine_max_step(10)
    .quick_lock(false)
    .enable();

The entire example is provided below.

use atsamd_hal::{
    clock::v2::{clock_system_at_reset, dfll::Dfll, pclk::Pclk, xosc::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 xosc0 = Xosc::from_clock(tokens.xosc0, pins.pa14, 24.mhz()).enable();
let (gclk0, dfll, xosc0) = clocks.gclk0.swap_sources(clocks.dfll, xosc0);
let token_dfll = dfll.disable().free();
let (pclk_dfll, gclk0) = Pclk::enable(tokens.pclks.dfll, gclk0);
let dfll = Dfll::from_pclk(token_dfll, pclk_dfll)
    .coarse_max_step(1)
    .fine_max_step(10)
    .quick_lock(false)
    .enable();

Reconfiguring an EnabledDfll

In some cases, users may want to reconfigure the DFLL while it remains enabled. For instance, a user may want to place the DFLL in its closed-loop, USB recovery mode while in use by the system’s master clock. It would normally be impossible to do so with other clocks in the clock module, because changing the clock source would break an invariant of the clock tree. However, the DFLL is special, because its output frequency is always 48 MHz. Moreover, by design, consumers of the DFLL aren’t affected by its configuration (see the discussion on Id types).

For this reason, we define a special into_mode function on EnabledDfll. It will consume the EnabledDfll and transform it to use a different Mode.

While the Dfll constructors (i.e. open_loop, from_pclk, and from_usb) handle the Mode type for you, into_mode is generic over the initial and final Mode, so it takes and returns the corresponding Mode types directly. Furthermore, into_mode also accepts a closure, allowing you to modify the Dfll before the new Mode is applied.

Consider the following example. As above, we start with the clocks in their default configuration at power-on reset. Remember that the Dfll is used by the system’s master clock. At this point, we would like to reconfigure it to use an external 32 kHz clock on pin PA10. First, we construct a Gclk from the corresponding gpio Pin. Then we enable the Pclk for the DFLL and construct an instance of FromPclk. Finally, we call into_mode, which takes an instance of FromPclk and returns an instance of OpenLoop. Neither OpenLoop nor FromUsb need to store a corresponding resource, so they are both effectively equivalent to the () type. We can also change some of the DFLL control loop settings prior to the Mode change using the closure argument to into_mode.

use atsamd_hal::{
    clock::v2::{
        clock_system_at_reset,
        dfll::{Dfll, FromPclk},
        gclk::Gclk,
        pclk::Pclk,
    },
    gpio::Pins,
    pac::Peripherals,
    time::U32Ext,
};
let mut pac = Peripherals::take().unwrap();
let pins = Pins::new(pac.PORT);
let (buses, mut clocks, tokens) = clock_system_at_reset(
    pac.OSCCTRL,
    pac.OSC32KCTRL,
    pac.GCLK,
    pac.MCLK,
    &mut pac.NVMCTRL,
);
let gclk4 = Gclk::from_pin(tokens.gclks.gclk4, pins.pa10, 32_768.hz()).enable();
let (pclk, gclk4) = Pclk::enable(tokens.pclks.dfll, gclk4);
let from_pclk = FromPclk { pclk };
let (dfll, open_loop) = clocks.dfll.into_mode(from_pclk, |dfll| {
    dfll.set_coarse_max_step(1);
    dfll.set_fine_max_step(8);
    dfll.set_chill_cycle(false);
    dfll.set_run_standby(true);
});

Structs

Digital frequency-locked loop used to generate a 48 MHz clock
Singleton token that can be exchanged for the Dfll

Enums

Id type representing the identity of the DFLL clock
Value-level enum identifying the Dfll control loop mode
Value-level enum identifying one of two possible reference clocks for the Dfll

Traits

Type-level enum identifying the Dfll control loop mode
Type-level enum identifying one of two possible Dfll reference clocks

Type Definitions