Expand description
Advanced peripheral bus clocks
Overview
APB clocks facilitate communication between the processor core and
peripherals on the APB bus. To communicate with a peripheral, the
corresponding APB clock must be enabled, which is done by setting a bit in
one of the four APBXMASK registers.
In this module, enabled APB clocks are represented by the ApbClk<A>
struct, where the type parameter A is a type that implements ApbId and
corresponds to one of the bits in an APBXMASK register.
While most other clocks in the clock module are configured through
mutually exclusive registers, the ApbClks share the four APBXMASK
registers. This presents a challenge for memory safety. Specifically, if we
allowed unrestricted access to the corresponding APBXMASK register through
each ApbClk, we could create data races.
To solve this problem, we restrict access to the APBXMASK registers using
the Apb type. Apb was created to act as a gateway to the APBXMASK
registers, allowing us to use &mut Apb as compile-time proof of exclusive
access to them.
Example
Enabling and disabling the ApbClks proceeds according to the principles
outlined in the clock module documentation. It is best shown with an
example.
Let’s start by using clock_system_at_reset to access the HAL clocking
structs.
use atsamd_hal::{
    clock::v2::{
        clock_system_at_reset,
    },
    pac::Peripherals,
};
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,
);Some APB clocks are enabled at power-on reset. We can find these in the
Clocks struct.
let apb_port = clocks.apbs.port;Other APB clocks are disabled at power-on reset. To enable these, we must
have access to the Apb bus type, which is found in the Buses struct.
As described above, Apb mediates access to the shared APBXMASK
registers. We call Apb::enable to convert an ApbToken into the
corresponding ApbClk. The existence of each ApbClk type represents
proof that the corresponding APB clock has been enabled.
let apb_sercom0 = buses.apb.enable(tokens.apbs.sercom0);The complete example is shown below.
use atsamd_hal::{
    clock::v2::{
        clock_system_at_reset,
    },
    pac::Peripherals,
};
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 apb_port = clocks.apbs.port;
let apb_sercom0 = buses.apb.enable(tokens.apbs.sercom0);