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);

Structs

APB clock controller
An enabled APB clock
Set of ApbClks for APB clocks that are enabled at power-on reset
Singleton token that can be exchanged for an ApbClk
Set of ApbTokens for APB clocks that are disabled at power-on reset

Enums

Value-level enum identifying a single APB clock

Traits

Type-level enum identifying one of the possible APB clocks