Module gclk

Source
Expand description

§Generic Clock Controllers

§Overview

The generic clock controller is central to the clocking system in ATSAMD chips. It provides 12 generic clock generators to modify and distribute clocks to other peripherals. Within the clock tree, these clock generators act as the branch clocks, connecting internal or external root or branch clocks to other branch or leaf clocks.

Each clock generator takes an input clock, optionally divides it, and produces an output clock. The input clock can be:

  • A GPIO input (Pin)
  • An external crystal oscillator (Xosc)
  • An external 32 kHz oscillator (Xosc32k)
  • The ultra-low power 32 kHz oscillator (OscUlp32k)
  • The 48 MHz DFLL (Dfll)
  • A DPLL (Dpll)
  • Generic clock generator #1 (Gclk1)

The output clock can be:

  • A peripheral channel clock (Pclk)
  • A GPIO pin (GclkOut)

§Example

The configuration of a Gclk is best shown with an example. However, the example assumes you are already familiar with the basics of the clock module. See the clock module documentation for an overview.

Suppose we start with the default clock tree after power-on reset.

DFLL (48 MHz)
└── GCLK0 (48 MHz)
    └── Main clock (48 MHz)

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

DFLL (48 MHz)
└── GCLK0 (48 MHz)
    └── Main clock (48 MHz)

GCLK_IN1 (PB14, 24 MHz)
└── GCLK1 (12 MHz)
    ├── SERCOM0
    └── GCLK2 (3 MHz)
        ├── SERCOM1
        └── GCLK_OUT2 (PA16, 3 MHz)

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

use atsamd_hal::{
    clock::v2::{
        clock_system_at_reset,
        gclk::{Gclk, GclkDiv8, GclkDiv16},
        pclk::Pclk,
    },
    gpio::Pins,
    pac::Peripherals,
    fugit::RateExtU32,
};
let mut pac = Peripherals::take().unwrap();
let (mut buses, clocks, tokens) = clock_system_at_reset(
    pac.oscctrl,
    pac.osc32kctrl,
    pac.gclk,
    pac.mclk,
    &mut pac.nvmctrl,
);
let pins = Pins::new(pac.port);

Next, we use Gclk::from_pin to create a Gclk from a GclkToken, GPIO Pin and frequency, in Hertz. In this case, we create an instance of Gclk1.

At this point, notice that Gclk<G, I> takes two type parameters. G is a GclkId identifying which of the 12 generators this Gclk represents. Gclk1<I> is simply an alias for Gclk<Gclk1Id, I>. I is an Id type identifying the input clock, which must be a valid GclkSourceId. In this case, I is PB14, which is a GclkSourceId for Gclk1, because it implements GclkIo with GclkIo::GclkId = Gclk1Id.

let gclk1 = Gclk::from_pin(tokens.gclks.gclk1, pins.pb15, 24.MHz());

While we have created a Gclk, we have not yet enabled it. But before doing so, we would like to set the divider to reduce the input frequency of 24 MHz to a 12 MHz output. We call Gclk::div, which uses a builder API, so that it can be chained with the call to Gclk::enable.

let gclk1 = gclk1.div(GclkDiv16::Div(2)).enable();

Note that the divider value supplied to Gclk::div must be wrapped by the GclkDiv16 enum. This is for a few different reasons. First, Gclk1 accepts a wider range of divider values than the other Gclks, which use GclkDiv8 instead. Second, the actual divider value is controlled by two register fields, and the set of valid values is best expressed as a Rust enum. The GclkDiv8 and GclkDiv16 enums are connected by the GclkDivider trait.

Once Gclk1 is enabled, we can use it to enable the Pclk for Sercom0. This follows the usual pattern. We provide a PclkToken and the EnabledGclk1. In return, we get an enabled Pclk and the EnabledGclk1 counter is Incremented.

let (pclk_sercom0, gclk1) = Pclk::enable(tokens.pclks.sercom0, gclk1);

Next, we use Gclk1 as a clock Source to create an instance of Gclk2 with Gclk::from_source. However, keep in mind that this is only true for Gclk1. No other Gclk can act as a Source for another Gclk.

let (gclk2, gclk1) = Gclk::from_source(tokens.gclks.gclk2, gclk1);

The pattern repeats now. We divide Gclk1 by 4 to produce the Gclk2 output. Then we enable it to produce an EnabledGclk2 and use it to yield another Pclk.

let gclk2 = gclk2.div(GclkDiv8::Div(4)).enable();
let (pclk_sercom1, gclk2) = Pclk::enable(tokens.pclks.sercom1, gclk2);

Finally, we output Gclk2 directly to a GPIO pin. We supply the GPIO Pin to the EnabledGclk2 to yield a GclkOut.

let (gclk2, gclk2_out) = gclk2.enable_gclk_out(pins.pa16);

The full example is provided below.

use atsamd_hal::{
    clock::v2::{
        clock_system_at_reset,
        gclk::{Gclk, GclkDiv8, GclkDiv16},
        pclk::Pclk,
    },
    gpio::Pins,
    pac::Peripherals,
    fugit::RateExtU32,
};
let mut pac = Peripherals::take().unwrap();
let (mut buses, clocks, tokens) = clock_system_at_reset(
    pac.oscctrl,
    pac.osc32kctrl,
    pac.gclk,
    pac.mclk,
    &mut pac.nvmctrl,
);
let pins = Pins::new(pac.port);
let gclk1 = Gclk::from_pin(tokens.gclks.gclk1, pins.pb15, 24.MHz());
let gclk1 = gclk1.div(GclkDiv16::Div(2)).enable();
let (pclk_sercom0, gclk1) = Pclk::enable(tokens.pclks.sercom0, gclk1);
let (gclk2, gclk1) = Gclk::from_source(tokens.gclks.gclk2, gclk1);
let gclk2 = gclk2.div(GclkDiv8::Div(4)).enable();
let (pclk_sercom1, gclk2) = Pclk::enable(tokens.pclks.sercom1, gclk2);
let (gclk2, gclk2_out) = gclk2.enable_gclk_out(pins.pa16);

§Gclk0

Gclk0 is significant and special relative to the other Gclks. It is the clock generator for the processor’s main clock, so it can never be disabled. Consequently, it has a special API not available to the other Gclks. While normal Gclks can only change their clock Source or divider while disabled, Gclk0 can never be disabled, so we provide this functionality on EnabledGclk0 instead.

We model the main clock’s consumption of Gclk0 by setting its Enabled counter to U1 in clock_system_at_reset. This prevents users from ever disabling EnabledGclk0, because there is no way to Decrement its Counter to U0.

Additionally, we provide functions to change the clock Source, divider, etc. on EnabledGclk0, but we restrict them to the case where N = U1. This prevents users from changing its Source or divider if any other, additional clock consumes it (besides the main clock).

Structs§

Gclk
Generic clock generator used to distribute clocks to various peripherals
GclkOut
A GPIO Pin configured as a Gclk output
GclkToken
Singleton token that can be exchanged for a Gclk
GclkTokens
Set of GclkTokens representing the disabled Gclks at power-on reset

Enums§

DynGclkId
Value-level enum identifying one of 12 possible Gclks
DynGclkSourceId
Value-level enum of possible clock sources for a Gclk
Gclk0Id
Type-level variant of GclkId representing the identity of GCLK0
Gclk1Id
Type-level variant of GclkId representing the identity of GCLK1
Gclk2Id
Type-level variant of GclkId representing the identity of GCLK2
Gclk3Id
Type-level variant of GclkId representing the identity of GCLK3
Gclk4Id
Type-level variant of GclkId representing the identity of GCLK4
Gclk5Id
Type-level variant of GclkId representing the identity of GCLK5
Gclk6Id
Type-level variant of GclkId representing the identity of GCLK6
Gclk7Id
Type-level variant of GclkId representing the identity of GCLK7
Gclk8Id
Type-level variant of GclkId representing the identity of GCLK8
Gclk9Id
Type-level variant of GclkId representing the identity of GCLK9
Gclk10Id
Type-level variant of GclkId representing the identity of GCLK10
Gclk11Id
Type-level variant of GclkId representing the identity of GCLK11
GclkDiv8
Enum for the clock division factor of all Gclks other than Gclk1
GclkDiv16
Enum for the clock division factor of Gclk1 only

Traits§

Gclk0Io
Set of PinIds whose implementations of GclkIo map to Gclk0Id
GclkDivider
Trait unifying the two Gclk divider types, GclkDiv8 and GclkDiv16
GclkId
Type-level enum identifying one of 12 possible Gclks
GclkIo
Trait mapping each PinId to its corresponding GclkId when used as a Gclk input or output
GclkSourceId
Type-level enum of possible clock Sources for a Gclk
NotGclkIo
Type-level enum of GclkSourceId types that are not a GclkIo

Type Aliases§

EnabledGclk
An Enabled Gclk
EnabledGclk0
Type alias for the corresponding EnabledGclk
EnabledGclk1
Type alias for the corresponding EnabledGclk
EnabledGclk2
Type alias for the corresponding EnabledGclk
EnabledGclk3
Type alias for the corresponding EnabledGclk
EnabledGclk4
Type alias for the corresponding EnabledGclk
EnabledGclk5
Type alias for the corresponding EnabledGclk
EnabledGclk6
Type alias for the corresponding EnabledGclk
EnabledGclk7
Type alias for the corresponding EnabledGclk
EnabledGclk8
Type alias for the corresponding EnabledGclk
EnabledGclk9
Type alias for the corresponding EnabledGclk
EnabledGclk10
Type alias for the corresponding EnabledGclk
EnabledGclk11
Type alias for the corresponding EnabledGclk
Gclk0
Type alias for the corresponding Gclk
Gclk1
Type alias for the corresponding Gclk
Gclk2
Type alias for the corresponding Gclk
Gclk3
Type alias for the corresponding Gclk
Gclk4
Type alias for the corresponding Gclk
Gclk5
Type alias for the corresponding Gclk
Gclk6
Type alias for the corresponding Gclk
Gclk7
Type alias for the corresponding Gclk
Gclk8
Type alias for the corresponding Gclk
Gclk9
Type alias for the corresponding Gclk
Gclk10
Type alias for the corresponding Gclk
Gclk11
Type alias for the corresponding Gclk