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
Enums
Dfll