This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Frequency detector possible?

Hi. Can anyone think of a way to use the nRF52832 (probably Timer) to detect approximately 125KHz-150KHz variable square wave down to about a 1Hz resolution? I'd like to detect frequency changes at about 100 times a second though. Ideally I'd like to do this without any external hardware (like a heterodyne mixer). Thoughts? Thanks!

(FINAL answer at very bottom. The answer is YES, the nRF52832 can be used even with a SoftDevice to accurately detect a few hundred KHz signal down to a few Hz resolution)

  • OK, I know this is severely broken but it's a good first shot I think. Thoughts?:

    
    static void freqDetectorInit(void)
    {
    	NVIC_SetPriority(TIMER4_IRQn, APP_IRQ_PRIORITY_LOW);
    	NVIC_EnableIRQ(TIMER4_IRQn);									// Calls TIMER4_IRQHandler
    
    		// Pin & GPIOTE init
        IOPinConfig(0, FREQ_MEASURE_PIN, 0, IOPINDIR_INPUT, IOPINRES_NONE, IOPINTYPE_NORMAL);
    	NRF_GPIOTE->CONFIG[0] = 0x01 << 0; 								// Event mode
    	NRF_GPIOTE->CONFIG[0] |= FREQ_MEASURE_PIN << 8;					// Pin number
    	NRF_GPIOTE->CONFIG[0] |= GPIOTE_CONFIG_POLARITY_LoToHi << 16;	// Event rising edge
    
    		// Timer 4: Rising edge event counter - detect 1000 events then generates an interrupt
    	NRF_TIMER4->TASKS_STOP = 1;										// Stop timer 4
    	NRF_TIMER4->MODE = TIMER_MODE_MODE_Counter;						// Counting external pulses
    	NRF_TIMER4->BITMODE = TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos;			// Only need 16 bits to count 1000 pulses
    	NRF_TIMER4->CC[0] = 1001;										// # pulses to detect (+1)
    	NRF_TIMER4->TASKS_CLEAR = 1;									// Clear the timer
    	NRF_TIMER3->INTENSET = TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos;	// Generate int when done to can get the results
    
    		// Timer 3: 16Mhz timer during 1000 events
    	NRF_TIMER3->TASKS_STOP = 1;
    	NRF_TIMER3->MODE = TIMER_MODE_MODE_Timer;
    	NRF_TIMER3->BITMODE = TIMER_BITMODE_BITMODE_32Bit << TIMER_BITMODE_BITMODE_Pos;
    	NRF_TIMER3->PRESCALER = 0;
    	NRF_TIMER3->CC[0] = 0;
    	NRF_TIMER3->TASKS_CLEAR = 1;
    
    		// Using PPI CH0, connect NRF_TIMER4->EVENTS_COMPARE[0] evevnt to NRF_TIMER3->TASKS_CAPTURE[0] task
    		// Also a 2nd task to clear counter and start timer at once to avoid a possible GPIO transition between clearing counter and starting timer
    	NRF_PPI->CH[0].EEP = NRF_TIMER4->EVENTS_COMPARE[0];
    	NRF_PPI->CH[0].TEP = NRF_TIMER3->TASKS_CAPTURE[0];
    	NRF_PPI->FORK[0].TEP = NRF_TIMER3->TASKS_CLEAR;
    	NRF_PPI->CHEN |= 1 << 0;
    	NRF_PPI->CHENSET |= 1 << 0;
    
    	NRF_TIMER3->TASKS_START = 1;
    	NRF_TIMER4->TASKS_START = 1;
    }
    
    
    extern "C" void TIMER4_IRQHandler(void)
    {
    	freqDetected = NRF_TIMER3->CC[0];		// Total count for 1000 events (in 0.0625us units)
    
    	NRF_TIMER3->TASKS_CLEAR = 1;
    	NRF_TIMER4->TASKS_CLEAR = 1;
    	NRF_TIMER4->TASKS_START = 1;
    }
    
    

  • Goot shot, but you forgot the second PPI cnannel:

    NRF_PPI->CH[1].EEP = NRF_GPIOTE->EVENTS_IN[0];
    NRF_PPI->CH[1].TEP = NRF_TIMER3->TASKS_START;
    NRF_PPI->FORK[1].TEP = NRF_TIMER4->TASKS_COUNT;
    NRF_PPI->CHENSET = 1 << 1;

    Timer will be started by PPI, don't trigger TASKS_START manually.

    NRF_PPI->CHENSET enables channels from bitmask, there's no need for logical OR.  NRF_PPI->CHEN |= 1 << 0 is redundant.

  • Ok. Great. Thanks. What about the interruption routine? That should get the results, right? And what should it reset? What should it do to restart the whole process?

  • The best way is to disable PPI channel that starts counting, stop TIMER3, clear TIMER3 and TIMER4, then re-enable PPI.

  • Please explain the differences and when you would use PPI's CHEN, CHENSET, and CHENCLR. The documentation doesn't explain why there are 3 and when to use them (that I've found) and the graphic at the top only shows CHEN. Thanks!

Related