Hi,
I'm working with a SAME70 and need to calculate the frequency of an input square wave on a GPIO pin (PIO_PA5 in this example). I've read through the various Timer and Interrupt ASF guides, and have confirmed the functionality by inspecting the example projects. I've also read numerous similar threads on here regarding interrupts, latency, TC capture, etc.
The input square itself has a frequency range from 0Hz to ~5kHz. The input square wave is from a function generator, and I've verified the signal with my oscilloscope (just to make sure the signal itself was stable and didn't have any jitter).
Below is the relevant code:
/*Using TC1 Channel 0*/ #define ID_TC1_0 ID_TC3 #define TC1_0_Handler TC3_Handler volatile uint32_t tc_Count = 0; volatile uint32_t tc_Capture = 0; volatile uint32_t tc_Temp = 0; volatile float freq; /*TC 1 Channel 0 handler*/ void TC1_0_Handler(void) { tc_Count++; volatile uint32_t dummy; dummy = tc_get_status(TC1, 0); UNUSED(dummy); } /*Configure and initialise TC1 Channel 0*/ static void configure_tc1_0() { uint32_t ul_div; uint32_t ul_tcclks; uint32_t ul_sysclk = sysclk_get_cpu_hz(); pmc_enable_periph_clk(ID_TC1_0); tc_find_mck_divisor(1000, ul_sysclk, &ul_div, &ul_tcclks, ul_sysclk); tc_init(TC1, 0, ul_tcclks | TC_CMR_CPCTRG); tc_write_rc(TC1, 0, (ul_sysclk / ul_div) / 1000); NVIC_EnableIRQ((IRQn_Type) ID_TC1_0); tc_enable_interrupt(TC1, 0, TC_IER_CPCS); tc_start(TC1, 0); } /*PIO_PA5 interrupt handler*/ static void PIOA_Interrupt_Handler(uint32_t id, uint32_t mask) { //Capture current tc_Count value tc_Temp = tc_Count; if (id == ID_PIOA && mask == PIO_PA5) { //Check if this is the first rising edge if (tc_Capture == 0) { tc_Capture = tc_Count; } else { freq = 1 / (float)(tc_Temp - tc_Capture); } tc_Capture = tc_Temp; tc_Temp = 0; } } /*Initialisation function*/ void init(void) { //General initialisation etc. //Configure TC1_0 configure_tc1_0(); //Setup PIO_PA5 rising edge interrupt and callback pmc_enable_periph_clk(ID_PIOA); pio_disable_interrupt(ID_PIOA, PIO_PA5); NVIC_DisableIRQ((IRQn_Type) ID_PIOA); NVIC_ClearPendingIRQ((IRQn_Type) ID_PIOA); NVIC_EnableIRQ((IRQn_Type) ID_PIOA); pio_handler_set(PIOA, ID_PIOA, PIO_PA5, PIO_IT_RISE_EDGE, PIOA_Interrupt_Handler); pio_handler_set_priority(PIOA, (IRQn_Type) ID_PIOA, 0); pio_enable_interrupt(PIOA, PIO_PA5); }
As per the above code, I'm currently using a 1000Hz interrupt frequency, with ul_sysclk verified as being 150MHz. I have two issues with the above code:
- The calculated frequency is reported as twice that of the actual signal. E.g. an input frequency of 10Hz results in freq = 20Hz.
- The calculated frequency isn't stable for frequencies where the period isn't an integer (i.e. it has fractional parts). E.g. for a frequency of 10, the underlying period (100ms) results in freq being stable. However, a frequency of 9Hz (a period of 111.111ms) causes freq to bounce between two values due to rounding. I don't need to capture fractional frequencies, so can use a uint32_t for freq, but that doesn't address the underlying issue of not having enough resolution.
My questions:
- Is the above approach sound for low frequency (<5kHz) frequency capture?
- What would be a reasonable TC counter frequency to use, to allow for accurate (nearest 1Hz) frequency calculation between 1Hz and ~5kHz.
- Why does the above code result in a calculated frequency twice that of the actual input frequency?