Skip to main content

pic32_hal/clock/
refclock.rs

1//! Reference clock generator
2
3use core::marker::PhantomData;
4
5#[cfg(any(
6    feature = "pic32mx1xxfxxxb",
7    feature = "pic32mx2xxfxxxb",
8    feature = "pic32mx37x",
9    feature = "pic32mx47x",
10))]
11use crate::pac::{osc::RegisterBlock, OSC};
12
13#[cfg(feature = "pic32mx2x4fxxxb")]
14use crate::pac::{cru::RegisterBlock, CRU};
15
16use super::Error;
17
18/// Selected input clock for reference oscillator
19#[derive(PartialEq, Eq, Clone, Copy, Debug)]
20#[repr(u8)]
21pub enum Source {
22    Sysclock = 0,
23    Pbclock = 1,
24    Posc = 2,
25    Frc = 3,
26    Lprc = 4,
27    Sosc = 5,
28    Usbpll = 6,
29    Syspll = 7,
30    Refclki = 8,
31}
32
33/// Reference clock generator singleton
34pub struct Refclock {
35    pub(super) _private: PhantomData<()>, // prevent direct construction outside of the clock module
36}
37
38#[cfg(any(
39    feature = "pic32mx1xxfxxxb",
40    feature = "pic32mx2xxfxxxb",
41    feature = "pic32mx37x",
42    feature = "pic32mx47x",
43))]
44macro_rules! regs {
45    () => {
46        unsafe { &*OSC::ptr() as &RegisterBlock }
47    };
48}
49
50#[cfg(feature = "pic32mx2x4fxxxb")]
51macro_rules! regs {
52    () => {
53        unsafe { &*CRU::ptr() as &RegisterBlock }
54    };
55}
56
57impl Refclock {
58    /// Enable reference clock generator
59    pub fn enable(&self) {
60        regs!().refoconset.write(|w| w.on().bit(true));
61        // busy waiting until active bit is set
62        while !regs!().refocon.read().active().bit() {}
63    }
64
65    /// Disable reference clock generator
66    pub fn disable(&self) {
67        regs!().refoconclr.write(|w| w.on().bit(true));
68        // busy waiting until active bit is clear
69        while regs!().refocon.read().active().bit() {}
70    }
71
72    /// Enable clock output
73    pub fn output_enable(&self) {
74        regs!().refoconset.write(|w| w.oe().bit(true));
75    }
76
77    /// Disable clock output
78    pub fn output_disable(&self) {
79        regs!().refoconclr.write(|w| w.oe().bit(true));
80    }
81
82    /// Select a clock source
83    ///
84    /// Returns an `InvalidState` error if called when active. Call this before
85    /// `enable()` or after `disable()`
86    pub fn select_source(&self, s: Source) -> Result<(), Error> {
87        if regs!().refocon.read().active().bit() {
88            return Err(Error::InvalidState);
89        }
90        regs!()
91            .refocon
92            .write(|w| unsafe { w.rosel().bits(s as u8) });
93        Ok(())
94    }
95
96    /// Set reference clock divisor
97    ///
98    /// `div_q8` is the fractional clock divisor in Q16.8 representation. The
99    /// minimum value is 2 (represented as 0x000200). The maximum value is
100    /// 2^16 - 1/(2^8) (represented as 0xffffff). `div_q8` refers to the total
101    /// clock ratio including an additional /2 division described in the data sheet.
102    /// Returns an `InvalidArgument` error if the divisor is out of range and an
103    /// `InvalidState` error if called during an ongoing divisor change
104    /// operation.
105    ///
106    /// Remark: not clear (to me) from the documentation if changing the integer
107    /// part of the divisor works when the reference clock module is active;
108    /// needs to be tested.
109    pub fn set_divisor(&self, div_q8: u32) -> Result<(), Error> {
110        // this might look strange but there is also a fixed /2 clock division
111        // in addition to the fractional divider.
112        let m = div_q8 & 0x1ff; // fractional part: 9 LSB
113        let n = div_q8 >> 9; // integer part (15 bit)
114        if n == 0 || n > 32767 {
115            return Err(Error::InvalidArgument);
116        }
117        if self.set_divisor_ongoing() {
118            return Err(Error::InvalidState);
119        }
120        regs!()
121            .refotrim
122            .write(|w| unsafe { w.rotrim().bits(m as u16) });
123        regs!()
124            .refocon
125            .modify(|_, w| unsafe { w.rodiv().bits(n as u16).divswen().bit(true) });
126        Ok(())
127    }
128
129    /// Check if a clock divisor setting operation is ongoing.
130    ///
131    /// Returns false if the ACTIVE bit is not set regardless of whether a
132    /// divisor setting has previously been initiated.
133    pub fn set_divisor_ongoing(&self) -> bool {
134        regs!().refocon.read().active().bit() && regs!().refocon.read().divswen().bit()
135    }
136}