diff options
Diffstat (limited to 'embassy-rp')
| -rw-r--r-- | embassy-rp/src/pio_programs/clock_divider.rs | 43 |
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 | ||
| 3 | use fixed::traits::ToFixed; | ||
| 4 | use fixed::types::extra::U8; | 3 | use fixed::types::extra::U8; |
| 5 | 4 | ||
| 6 | use crate::clocks::clk_sys_freq; | 5 | use 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] | ||
| 18 | pub fn calculate_pio_clock_divider(target_hz: u32) -> fixed::FixedU32<U8> { | 16 | pub 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. |
| 23 | fn 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 | ||
| 30 | pub 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)] |
| 33 | mod tests { | 52 | mod 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 | } |
