@@ -84,6 +84,30 @@ pub use clock_sources::{GPin0, GPin1};
8484
8585use clock_sources:: { PllSys , PllUsb , Rosc , Xosc } ;
8686
87+ /// Frequency counter accuracy
88+ ///
89+ /// See: https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf#table-fc-test-interval
90+ #[ repr( u8 ) ]
91+ #[ allow( missing_docs) ]
92+ pub enum FCAccuracy {
93+ _2048kHz = 0 ,
94+ _1024kHz,
95+ _512kHz,
96+ _256kHz,
97+ _128kHz,
98+ _64kHz,
99+ _32kHz,
100+ _16kHz,
101+ _8kHz,
102+ _4kHz,
103+ _2kHz,
104+ _1kHz,
105+ _500Hz,
106+ _250Hz,
107+ _125Hz,
108+ _62_5Hz,
109+ }
110+
87111#[ derive( Copy , Clone ) ]
88112/// Provides refs to the CLOCKS block.
89113struct ShareableClocks {
@@ -305,8 +329,12 @@ impl ClocksManager {
305329 . configure_clock ( & self . system_clock , self . system_clock . freq ( ) )
306330 }
307331
308- /// Approximates the frequency of the given clock source.
309- pub fn approximate_frequency < C : ClockSource > ( & mut self , _trg_clk : C ) -> HertzU32 {
332+ /// Measure the frequency of the given clock source by approximation.
333+ pub fn measure_frequency < C : ClockSource > (
334+ & mut self ,
335+ _trg_clk : C ,
336+ accuracy : FCAccuracy ,
337+ ) -> HertzU32 {
310338 // Wait for the frequency counter to be ready
311339 while self . clocks . fc0_status . read ( ) . running ( ) . bit_is_set ( ) {
312340 core:: hint:: spin_loop ( )
@@ -318,10 +346,14 @@ impl ClocksManager {
318346 . bits ( self . reference_clock . get_freq ( ) . to_kHz ( ) )
319347 } ) ;
320348
321- // Corresponds to a 1ms test time, which seems to give good enough accuracy
349+ // > The test interval is 0.98us * 2**interval, but let's call it 1us * 2**interval.
350+ // > The default gives a test interval of 250us
351+ //
352+ // https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf#reg-clocks-FC0_INTERVAL
353+ // https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf#table-fc-test-interval
322354 self . clocks
323355 . fc0_interval
324- . write ( |w| unsafe { w. fc0_interval ( ) . bits ( 10 ) } ) ;
356+ . write ( |w| unsafe { w. fc0_interval ( ) . bits ( accuracy as u8 ) } ) ;
325357
326358 // We don't really care about the min/max, so these are just set to min/max values.
327359 self . clocks
@@ -331,7 +363,7 @@ impl ClocksManager {
331363 . fc0_max_khz
332364 . write ( |w| unsafe { w. fc0_max_khz ( ) . bits ( 0xffffffff ) } ) ;
333365
334- // To measure rosc directly we use the value 0x03 .
366+ // Select which clock to measure .
335367 self . clocks
336368 . fc0_src
337369 . write ( |w| w. fc0_src ( ) . variant ( C :: FCOUNTER_SRC ) ) ;
@@ -341,7 +373,10 @@ impl ClocksManager {
341373 core:: hint:: spin_loop ( )
342374 }
343375
344- let speed_hz = self . clocks . fc0_result . read ( ) . khz ( ) . bits ( ) * 1000 ;
376+ // TODO: the result may not be valid (eg clock stopped during measurement). We may want to
377+ // return a Result instead. Is it worth the hassle though?
378+ let result = self . clocks . fc0_result . read ( ) ;
379+ let speed_hz = result. khz ( ) . bits ( ) * 1000 + result. frac ( ) . bits ( ) ;
345380 speed_hz. Hz ( )
346381 }
347382
0 commit comments