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