Expand description

Internal, ultra low power, 32 kHz oscillator

Overview

The osculp32k module provides access to the 32 kHz ultra low power internal oscillator (OSCULP32K) within the OSC32KCTRL peripheral.

The OSCULP32K clock is unlike most other clocks. First, it is an internal clock that is always enabled and can’t be disabled. And second, it has two separate outputs, one at 32 kHz and another divided down to 1 kHz. Moreover, none, either or both of these outputs can be enabled at any given time.

We can see, then, that the OSCULP32K peripheral forms its own, miniature clock tree. There is a 1:N producer clock that is always enabled; and there are two possible consumer clocks that can be independently and optionally enabled. In fact, this structure is illustrated by the OSCULP32K register, which has no regular ENABLE bit and two different enable bits for clock output, EN32K and EN1K.

To represent this structure in the type system, we divide the OSCULP32K peripheral into these three clocks. Users get access to the 1:N EnabledOscUlp32kBase clock Source at power-on reset, which can be consumed by both the OscUlp32k and OscUlp1k clocks. Note that OscUlp32k and OscUlp1k are themselves 1:N clocks as well.

Write lock

Rhe OSCULP32K register has a dedicated write lock bit that will freeze its configuration until the next power-on reset. We implement this by simply dropping the OscUlp32kBase clock, which prevents any further access to the OSCULP32K register.

Example

Creating and configuring the OSCULP32K clocks 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,
        osculp32k::{OscUlp1k, OscUlp32k},
    },
    pac::Peripherals,
};
let mut pac = Peripherals::take().unwrap();
let (buses, clocks, tokens) = clock_system_at_reset(
    pac.OSCCTRL,
    pac.OSC32KCTRL,
    pac.GCLK,
    pac.MCLK,
    &mut pac.NVMCTRL,
);

Next, we can extract the EnabledOscUlp32kBase clock from the Clocks struct and use it to enable the OscUlp1k and OscUlp32k clocks.

let base = clocks.osculp32k_base;
let (osculp1k, base) = OscUlp1k::enable(tokens.osculp32k.osculp1k, base);
let (osculp32k, base) = OscUlp32k::enable(tokens.osculp32k.osculp32k, base);

We can then override the calibration value read from flash at start up.

base.set_calibration(128);

And finally, we can set the write lock bit to freeze the configuation until the next power-on reset. Doing so also drops the EnabledOscUlp32kBase clock.

base.write_lock();

The complete example is shown below.

use atsamd_hal::{
    clock::v2::{
        clock_system_at_reset,
        osculp32k::{OscUlp1k, OscUlp32k},
    },
    pac::Peripherals,
};
let mut pac = Peripherals::take().unwrap();
let (buses, clocks, tokens) = clock_system_at_reset(
    pac.OSCCTRL,
    pac.OSC32KCTRL,
    pac.GCLK,
    pac.MCLK,
    &mut pac.NVMCTRL,
);
let base = clocks.osculp32k_base;
let (osculp1k, base) = OscUlp1k::enable(tokens.osculp32k.osculp1k, base);
let (osculp32k, mut base) = OscUlp32k::enable(tokens.osculp32k.osculp32k, base);
base.set_calibration(128);
base.write_lock();

Structs

Clock representing the 1 kHz output of the OscUlp32kBase clock
Singleton token that can be exchanged for OscUlp1k
Clock representing the 32 kHz output of the OscUlp32kBase clock
OSC3ULP2K base clock, which feeds the OscUlp1k and OscUlp32k clocks
Singleton token that can be exchanged for OscUlp32k
Set of tokens representing the disabled OSCULP32K clocks power-on reset

Enums

Type representing the identity of the OscUlp1k clock
Type representing the identity of the OscUlp32k clock

Type Definitions