max7800x-hal 0.7.1

A Hardware Abstraction Layer for the MAX7800X microcontroller family
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
//! # Clock and Oscillator Configuration
//!
//! This module provides a full [typestate](https://docs.rust-embedded.org/book/static-guarantees/typestate-programming.html)
//! API for enabling oscillators, configuring the system clock, and calculating
//! clock frequencies. By using typestates, calculation of clock frequencies
//! are done entirely at compile time, with no runtime or memory overhead.

use core::marker::PhantomData;

pub enum OscillatorSourceEnum {
    /// Internal Primary Oscillator (100 MHz)
    Ipo,
    /// Internal Secondary Oscillator (60 MHz)
    Iso,
    // Inro,
    /// Internal Baud Rate Oscillator (7.3728 MHz)
    Ibro,
    /// External RTC Oscillator (32.768 kHz)
    ///
    /// Requires initialization of the RTC peripheral. Currently unsupported.
    Ertco,
}

/// Marker trait for an oscillator source.
pub trait OscillatorSource: crate::Sealed {
    const SOURCE: OscillatorSourceEnum;
    const BASE_FREQUENCY: u32;
}

pub struct InternalPrimaryOscillator;
pub struct InternalSecondaryOscillator;
// pub struct InternalNanoRingOscillator;
pub struct InternalBaudRateOscillator;
pub struct ExternalRtcOscillator;
// pub struct ExternalClockOscillator;

impl crate::Sealed for InternalPrimaryOscillator {}
impl crate::Sealed for InternalSecondaryOscillator {}
impl crate::Sealed for InternalBaudRateOscillator {}
impl crate::Sealed for ExternalRtcOscillator {}

impl OscillatorSource for InternalPrimaryOscillator {
    const SOURCE: OscillatorSourceEnum = OscillatorSourceEnum::Ipo;
    const BASE_FREQUENCY: u32 = 100_000_000; // 100 MHz
}
impl OscillatorSource for InternalSecondaryOscillator {
    const SOURCE: OscillatorSourceEnum = OscillatorSourceEnum::Iso;
    const BASE_FREQUENCY: u32 = 60_000_000; // 60 MHz
}
impl OscillatorSource for InternalBaudRateOscillator {
    const SOURCE: OscillatorSourceEnum = OscillatorSourceEnum::Ibro;
    const BASE_FREQUENCY: u32 = 7_372_800; // 7.3728 MHz
}
impl OscillatorSource for ExternalRtcOscillator {
    const SOURCE: OscillatorSourceEnum = OscillatorSourceEnum::Ertco;
    const BASE_FREQUENCY: u32 = 32_768; // 32.768 kHz
}

/// Marker trait for the state of an oscillator.
pub trait OscillatorState: crate::Sealed {}

pub struct Disabled;
pub struct Enabled;

impl crate::Sealed for Disabled {}
impl crate::Sealed for Enabled {}

impl OscillatorState for Disabled {}
impl OscillatorState for Enabled {}

/// Marker trait for a clock option (enabled oscillator or clock).
pub trait ClockOption: crate::Sealed {}

pub struct SystemClock;
pub struct PeripheralClock;

impl crate::Sealed for SystemClock {}
impl crate::Sealed for PeripheralClock {}
impl ClockOption for SystemClock {}
impl ClockOption for PeripheralClock {}

impl ClockOption for InternalPrimaryOscillator {}
impl ClockOption for InternalSecondaryOscillator {}
impl ClockOption for InternalBaudRateOscillator {}
impl ClockOption for ExternalRtcOscillator {}

/// Marker trait for the system clock divider
pub trait SystemClockDivider: crate::Sealed {
    const DIVISOR: u32;
}

pub struct DivUnknown;
pub struct Div1;
pub struct Div2;
pub struct Div4;
pub struct Div8;
pub struct Div16;
pub struct Div32;
pub struct Div64;
pub struct Div128;

impl crate::Sealed for DivUnknown {}
impl crate::Sealed for Div1 {}
impl crate::Sealed for Div2 {}
impl crate::Sealed for Div4 {}
impl crate::Sealed for Div8 {}
impl crate::Sealed for Div16 {}
impl crate::Sealed for Div32 {}
impl crate::Sealed for Div64 {}
impl crate::Sealed for Div128 {}

impl SystemClockDivider for DivUnknown {
    const DIVISOR: u32 = 1;
}
impl SystemClockDivider for Div1 {
    const DIVISOR: u32 = 1;
}
impl SystemClockDivider for Div2 {
    const DIVISOR: u32 = 2;
}
impl SystemClockDivider for Div4 {
    const DIVISOR: u32 = 4;
}
impl SystemClockDivider for Div8 {
    const DIVISOR: u32 = 8;
}
impl SystemClockDivider for Div16 {
    const DIVISOR: u32 = 16;
}
impl SystemClockDivider for Div32 {
    const DIVISOR: u32 = 32;
}
impl SystemClockDivider for Div64 {
    const DIVISOR: u32 = 64;
}
impl SystemClockDivider for Div128 {
    const DIVISOR: u32 = 128;
}

/// Oscillators represent the state of a physical oscillator. To use an
/// oscillator, it must be enabled. Then, it can be converted into a clock.
pub struct Oscillator<O: OscillatorSource, S: OscillatorState> {
    _source: PhantomData<O>,
    _state: PhantomData<S>,
}

/// Clocks are used to drive peripherals after the system clock is configured.
/// At this point, they can be safely cloned and copied.
pub struct Clock<SRC: ClockOption> {
    _src: PhantomData<SRC>,
    pub frequency: u32,
}
impl<SRC> Clone for Clock<SRC>
where
    SRC: ClockOption,
{
    fn clone(&self) -> Self {
        *self
    }
}
impl<SRC> Copy for Clock<SRC> where SRC: ClockOption {}

/// An OscillatorGuard protects the initialization of an [`Oscillator`],
/// ensuring that each oscillator source is only initialized once.
pub struct OscillatorGuard<O: OscillatorSource> {
    _source: PhantomData<O>,
}

impl<O> OscillatorGuard<O>
where
    O: OscillatorSource,
{
    pub(super) fn new() -> Self {
        Self {
            _source: PhantomData,
        }
    }
}

/// A collection of OscillatorGuards for each [`Oscillator`] source.
pub struct OscillatorGuards {
    pub ipo: OscillatorGuard<InternalPrimaryOscillator>,
    pub iso: OscillatorGuard<InternalSecondaryOscillator>,
    pub ibro: OscillatorGuard<InternalBaudRateOscillator>,
    pub ertco: OscillatorGuard<ExternalRtcOscillator>,
}

impl OscillatorGuards {
    pub(super) fn new() -> Self {
        Self {
            ipo: OscillatorGuard::new(),
            iso: OscillatorGuard::new(),
            ibro: OscillatorGuard::new(),
            ertco: OscillatorGuard::new(),
        }
    }
}

/// Initialization of an [`Oscillator`] requires consumption of a
/// corresponding typed OscillatorGuard.
impl<O> Oscillator<O, Disabled>
where
    O: OscillatorSource,
{
    pub fn new(_guard: OscillatorGuard<O>) -> Self {
        Self {
            _source: PhantomData,
            _state: PhantomData,
        }
    }
}

pub type Ipo = Oscillator<InternalPrimaryOscillator, Disabled>;
impl Ipo {
    pub fn enable(
        &self,
        reg: &mut super::GcrRegisters,
    ) -> Oscillator<InternalPrimaryOscillator, Enabled> {
        reg.gcr.clkctrl().modify(|_, w| w.ipo_en().set_bit());
        while reg.gcr.clkctrl().read().ipo_rdy().bit_is_clear() {}
        Oscillator {
            _source: PhantomData,
            _state: PhantomData,
        }
    }
}
impl Oscillator<InternalPrimaryOscillator, Enabled> {
    pub const fn into_clock(self) -> Clock<InternalPrimaryOscillator> {
        Clock::<InternalPrimaryOscillator> {
            _src: PhantomData,
            frequency: InternalPrimaryOscillator::BASE_FREQUENCY,
        }
    }
}

pub type Iso = Oscillator<InternalSecondaryOscillator, Disabled>;
impl Iso {
    pub fn enable(
        self,
        reg: &mut super::GcrRegisters,
    ) -> Oscillator<InternalSecondaryOscillator, Enabled> {
        reg.gcr.clkctrl().modify(|_, w| w.iso_en().set_bit());
        while reg.gcr.clkctrl().read().iso_rdy().bit_is_clear() {}
        Oscillator {
            _source: PhantomData,
            _state: PhantomData,
        }
    }
}
impl Oscillator<InternalSecondaryOscillator, Enabled> {
    pub const fn into_clock(self) -> Clock<InternalSecondaryOscillator> {
        Clock::<InternalSecondaryOscillator> {
            _src: PhantomData,
            frequency: InternalSecondaryOscillator::BASE_FREQUENCY,
        }
    }
}

// pub type Inro = Oscillator<InternalNanoRingOscillator, Disabled>;
// impl Inro {
//     pub fn enable(self, reg: &mut super::GcrRegisters) -> Oscillator<InternalNanoRingOscillator, Enabled> {
//         // INRO is always enabled
//         while reg.gcr.clkctrl().read().inro_rdy().bit_is_clear() {}
//         Oscillator {
//             _source: PhantomData,
//             _state: PhantomData,
//         }
//     }
// }

pub type Ibro = Oscillator<InternalBaudRateOscillator, Disabled>;
impl Ibro {
    pub fn enable(
        self,
        reg: &mut super::GcrRegisters,
    ) -> Oscillator<InternalBaudRateOscillator, Enabled> {
        // IBRO is always enabled
        while reg.gcr.clkctrl().read().ibro_rdy().bit_is_clear() {}
        Oscillator {
            _source: PhantomData,
            _state: PhantomData,
        }
    }
}
impl Oscillator<InternalBaudRateOscillator, Enabled> {
    pub const fn into_clock(self) -> Clock<InternalBaudRateOscillator> {
        Clock::<InternalBaudRateOscillator> {
            _src: PhantomData,
            frequency: InternalBaudRateOscillator::BASE_FREQUENCY,
        }
    }
}

pub type Ertco = Oscillator<ExternalRtcOscillator, Disabled>;
impl Oscillator<ExternalRtcOscillator, Disabled> {
    pub fn enable(
        self,
        reg: &mut super::GcrRegisters,
    ) -> Oscillator<ExternalRtcOscillator, Enabled> {
        reg.gcr.clkctrl().modify(|_, w| w.ertco_en().set_bit());
        while reg.gcr.clkctrl().read().ertco_rdy().bit_is_clear() {}
        todo!("ERTCO requires initialization of the RTC peripheral");
        // Oscillator {
        //     _source: PhantomData,
        //     _state: PhantomData,
        // }
    }
}

/// System clock setup configuration (source and divider).
pub struct SystemClockConfig<S: OscillatorSource, D: SystemClockDivider> {
    _source: PhantomData<S>,
    _divider: PhantomData<D>,
}

/// Initialized system clock configuration and resulting [`Clock`]s and frequencies.
pub struct SystemClockResults {
    pub sys_clk: Clock<SystemClock>,
    pub pclk: Clock<PeripheralClock>,
}

impl<S, D> SystemClockConfig<S, D>
where
    S: OscillatorSource,
    D: SystemClockDivider,
{
    pub fn new() -> Self {
        SystemClockConfig {
            _source: PhantomData,
            _divider: PhantomData,
        }
    }

    /// Set the source oscillator of the system clock (SYS_CLK).
    /// The oscillator must be enabled beforehand (enforced by the type system).
    pub fn set_source<NewS: OscillatorSource>(
        self,
        reg: &mut super::GcrRegisters,
        _oscillator: &Oscillator<NewS, Enabled>,
    ) -> SystemClockConfig<NewS, D> {
        match NewS::SOURCE {
            OscillatorSourceEnum::Ipo => {
                reg.gcr.clkctrl().modify(|_, w| w.sysclk_sel().ipo());
            }
            OscillatorSourceEnum::Iso => {
                reg.gcr.clkctrl().modify(|_, w| w.sysclk_sel().iso());
            }
            OscillatorSourceEnum::Ibro => {
                reg.gcr.clkctrl().modify(|_, w| w.sysclk_sel().ibro());
            }
            OscillatorSourceEnum::Ertco => {
                // reg.gcr.clkctrl().modify(|_, w| w.sysclk_sel().ertco());
                todo!("ERTCO requires initialization of the RTC peripheral");
            }
        }
        while reg.gcr.clkctrl().read().sysclk_rdy().bit_is_clear() {}
        SystemClockConfig {
            _source: PhantomData,
            _divider: PhantomData,
        }
    }

    /// Set the divider of the system clock (SYS_CLK).
    /// The divider must be a valid value (enforced by the type system).
    pub fn set_divider<NewD: SystemClockDivider>(
        self,
        reg: &mut super::GcrRegisters,
    ) -> SystemClockConfig<S, NewD> {
        match NewD::DIVISOR {
            1 => {
                reg.gcr.clkctrl().modify(|_, w| w.sysclk_div().div1());
            }
            2 => {
                reg.gcr.clkctrl().modify(|_, w| w.sysclk_div().div2());
            }
            4 => {
                reg.gcr.clkctrl().modify(|_, w| w.sysclk_div().div4());
            }
            8 => {
                reg.gcr.clkctrl().modify(|_, w| w.sysclk_div().div8());
            }
            16 => {
                reg.gcr.clkctrl().modify(|_, w| w.sysclk_div().div16());
            }
            32 => {
                reg.gcr.clkctrl().modify(|_, w| w.sysclk_div().div32());
            }
            64 => {
                reg.gcr.clkctrl().modify(|_, w| w.sysclk_div().div64());
            }
            128 => {
                reg.gcr.clkctrl().modify(|_, w| w.sysclk_div().div128());
            }
            _ => {
                unreachable!("Invalid system clock divider");
            }
        }
        while reg.gcr.clkctrl().read().sysclk_rdy().bit_is_clear() {}
        SystemClockConfig {
            _source: PhantomData,
            _divider: PhantomData,
        }
    }

    /// Freeze the system clock configuration and return configured clocks.
    pub const fn freeze(self) -> SystemClockResults {
        SystemClockResults {
            sys_clk: Clock::<SystemClock> {
                _src: PhantomData,
                frequency: S::BASE_FREQUENCY / D::DIVISOR,
            },
            pclk: Clock::<PeripheralClock> {
                _src: PhantomData,
                frequency: (S::BASE_FREQUENCY / D::DIVISOR) / 2,
            },
        }
    }
}