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:
§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 Gclk
s, 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 Increment
ed.
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 Gclk
s. 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
Gclk
s. While normal Gclk
s 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 aGclk
output - Gclk
Token - Singleton token that can be exchanged for a
Gclk
- Gclk
Tokens - Set of
GclkToken
s representing the disabledGclk
s at power-on reset
Enums§
- DynGclk
Id - Value-level enum identifying one of 12 possible
Gclk
s - DynGclk
Source Id - 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 - Gclk10
Id - Type-level variant of
GclkId
representing the identity of GCLK10 - Gclk11
Id - Type-level variant of
GclkId
representing the identity of GCLK11 - Gclk
Div8 - Enum for the clock division factor of all
Gclk
s other thanGclk1
- Gclk
Div16 - Enum for the clock division factor of
Gclk1
only
Traits§
- Gclk0Io
- Set of
PinId
s whose implementations ofGclkIo
map toGclk0Id
- Gclk
Divider - Trait unifying the two
Gclk
divider types,GclkDiv8
andGclkDiv16
- GclkId
- Type-level enum identifying one of 12 possible
Gclk
s - GclkIo
- Trait mapping each
PinId
to its correspondingGclkId
when used as aGclk
input or output - Gclk
Source Id - Type-level enum of possible clock
Source
s for aGclk
- NotGclk
Io - Type-level enum of
GclkSourceId
types that are not aGclkIo
Type Aliases§
- Enabled
Gclk - An
Enabled
Gclk
- Enabled
Gclk0 - Type alias for the corresponding
EnabledGclk
- Enabled
Gclk1 - Type alias for the corresponding
EnabledGclk
- Enabled
Gclk2 - Type alias for the corresponding
EnabledGclk
- Enabled
Gclk3 - Type alias for the corresponding
EnabledGclk
- Enabled
Gclk4 - Type alias for the corresponding
EnabledGclk
- Enabled
Gclk5 - Type alias for the corresponding
EnabledGclk
- Enabled
Gclk6 - Type alias for the corresponding
EnabledGclk
- Enabled
Gclk7 - Type alias for the corresponding
EnabledGclk
- Enabled
Gclk8 - Type alias for the corresponding
EnabledGclk
- Enabled
Gclk9 - Type alias for the corresponding
EnabledGclk
- Enabled
Gclk10 - Type alias for the corresponding
EnabledGclk
- Enabled
Gclk11 - 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