aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp/src/pio_programs/clock_divider.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-rp/src/pio_programs/clock_divider.rs')
-rw-r--r--embassy-rp/src/pio_programs/clock_divider.rs43
1 files changed, 32 insertions, 11 deletions
diff --git a/embassy-rp/src/pio_programs/clock_divider.rs b/embassy-rp/src/pio_programs/clock_divider.rs
index 687fd53ba..28c931ed2 100644
--- a/embassy-rp/src/pio_programs/clock_divider.rs
+++ b/embassy-rp/src/pio_programs/clock_divider.rs
@@ -1,6 +1,5 @@
1//! Helper functions for calculating PIO clock dividers 1//! Helper functions for calculating PIO clock dividers
2 2
3use fixed::traits::ToFixed;
4use fixed::types::extra::U8; 3use fixed::types::extra::U8;
5 4
6use crate::clocks::clk_sys_freq; 5use crate::clocks::clk_sys_freq;
@@ -14,39 +13,61 @@ use crate::clocks::clk_sys_freq;
14/// # Returns 13/// # Returns
15/// 14///
16/// A fixed-point divider value suitable for use in a PIO state machine configuration 15/// A fixed-point divider value suitable for use in a PIO state machine configuration
17#[inline]
18pub fn calculate_pio_clock_divider(target_hz: u32) -> fixed::FixedU32<U8> { 16pub fn calculate_pio_clock_divider(target_hz: u32) -> fixed::FixedU32<U8> {
19 calculate_pio_clock_divider_inner(clk_sys_freq(), target_hz) 17 calculate_pio_clock_divider_value(clk_sys_freq(), target_hz)
20} 18}
21 19
22#[inline] 20/// Calculate a PIO clock divider value based on the desired target frequency.
23fn calculate_pio_clock_divider_inner(sys_freq: u32, target_hz: u32) -> fixed::FixedU32<U8> { 21///
22/// # Arguments
23///
24/// * `sys_hz` - The system clock frequency in Hz
25/// * `target_hz` - The desired PIO clock frequency in Hz
26///
27/// # Returns
28///
29/// A fixed-point divider value suitable for use in a PIO state machine configuration
30pub const fn calculate_pio_clock_divider_value(sys_hz: u32, target_hz: u32) -> fixed::FixedU32<U8> {
24 // Requires a non-zero frequency 31 // Requires a non-zero frequency
25 assert!(target_hz > 0, "PIO clock frequency cannot be zero"); 32 assert!(target_hz > 0, "PIO clock frequency cannot be zero");
26 33
27 // Calculate the divider 34 // Compute the integer and fractional part of the divider.
28 let divider = (sys_freq + target_hz / 2) / target_hz; 35 // Doing it this way allows us to avoid u64 division while
29 divider.to_fixed() 36 // maintaining precision.
37 let integer = sys_hz / target_hz;
38 let remainder = sys_hz % target_hz;
39 let frac = (remainder << 8) / target_hz;
40
41 let result = integer << 8 | frac;
42
43 // Ensure the result will fit in 16+8 bits.
44 assert!(result <= 0xffff_ff, "pio clock divider too big");
45 // The clock divider can't be used to go faster than the system clock.
46 assert!(result >= 0x0001_00, "pio clock divider cannot be less than 1");
47
48 fixed::FixedU32::from_bits(result)
30} 49}
31 50
32#[cfg(test)] 51#[cfg(test)]
33mod tests { 52mod tests {
53 use fixed::traits::ToFixed;
54
34 use super::*; 55 use super::*;
35 56
36 #[test] 57 #[test]
37 fn clock_divider_math() { 58 fn clock_divider_math() {
38 // A simple divider that must have a fractional part. 59 // A simple divider that must have a fractional part.
39 let divider = calculate_pio_clock_divider_inner(125_000_000, 40_000_000); 60 let divider = calculate_pio_clock_divider_value(125_000_000, 40_000_000);
40 let expected: fixed::FixedU32<U8> = 3.125.to_fixed(); 61 let expected: fixed::FixedU32<U8> = 3.125.to_fixed();
41 assert_eq!(divider, expected); 62 assert_eq!(divider, expected);
42 63
43 // A system clk so high it would overflow a u32 if shifted left. 64 // A system clk so high it would overflow a u32 if shifted left.
44 let divider = calculate_pio_clock_divider_inner(2_000_000_000, 40_000); 65 let divider = calculate_pio_clock_divider_value(2_000_000_000, 40_000);
45 let expected: fixed::FixedU32<U8> = 50000.to_fixed(); 66 let expected: fixed::FixedU32<U8> = 50000.to_fixed();
46 assert_eq!(divider, expected); 67 assert_eq!(divider, expected);
47 68
48 // A divider that requires all 8 fractional bits. 69 // A divider that requires all 8 fractional bits.
49 let divider = calculate_pio_clock_divider_inner(134_283_264, 16_777_216); 70 let divider = calculate_pio_clock_divider_value(134_283_264, 16_777_216);
50 let expected: fixed::FixedU32<U8> = 8.00390625.to_fixed(); 71 let expected: fixed::FixedU32<U8> = 8.00390625.to_fixed();
51 assert_eq!(divider, expected); 72 assert_eq!(divider, expected);
52 } 73 }