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)

Parents
  • Hi,

    Yes, you can do this with GPIOTE, PPI, and two timers - for example, TIMER1 is a pulse counter, TIMER2 is a 16-MHz timer.
    - configure TIMER1->CC[0] for a number of pulses to measure plus 1 (in your case, 1000 pulses is about 0.08 sec that meets your requirements)
    - configure first PPI channel to start TIMER2 and increment TIMER1 by GPIOTE rise event
    - configure second PPI channel to capture TIMER2 value into CC[0] by counter's TIMER1->COMPARE[0] event (after 1000 pulses)
    - to start measurement, clear TIMER1 and TIMER2, then enable both PPI channels
    - after TIMER1->COMPARE[0] event, TIMER2->CC[0] will contain total time for 1000 pulses in 1/16 usec units.

    1Hz resoultion is a challenge. A difference between 149999 and 150000 Hz is about 0.04 usec at 1000 periods, resolution of nRF52 timer is 1/16 usec - I believe you can get about 2-3 Hz resolution if everything is done carefully.

  • Hi Dmitry,

    I got it basically working largely from code from https://devzone.nordicsemi.com/f/nordic-q-a/9036/measuring-input-gpio-pin-frequency-with-soft-device-running. My code looks like this:

    static void freqDetectorInit(void)
    {
        IOPinConfig(0, FREQ_MEASURE_PIN, 0, IOPINDIR_INPUT, IOPINRES_NONE, IOPINTYPE_NORMAL);
    
    	NVIC_SetPriority(TIMER3_IRQn, APP_IRQ_PRIORITY_LOW);
    	NVIC_EnableIRQ(TIMER3_IRQn);									// Calls TIMER3_IRQHandler
    
        	// Timer 4: Freq counter
    	NRF_TIMER4->TASKS_STOP = 1;
    	NRF_TIMER4->MODE = TIMER_MODE_MODE_Counter;
    	NRF_TIMER4->BITMODE = (TIMER_BITMODE_BITMODE_32Bit << TIMER_BITMODE_BITMODE_Pos);
    	NRF_TIMER4->TASKS_CLEAR = 1;
    	NRF_TIMER4->EVENTS_COMPARE[0] = 0;
    
    		// Timer 3: Timed gate
    	NRF_TIMER3->TASKS_STOP = 1;
    	NRF_TIMER3->MODE = TIMER_MODE_MODE_Timer;
    	NRF_TIMER3->PRESCALER = 0;										// Fhck / 2^0
    	NRF_TIMER3->CC[0] = 16000000ULL / 1000;							// Detect 1000 events - careful changing this!
    	NRF_TIMER3->BITMODE = (TIMER_BITMODE_BITMODE_32Bit << TIMER_BITMODE_BITMODE_Pos);
    	NRF_TIMER3->TASKS_CLEAR = 1;
    	NRF_TIMER3->INTENSET = (TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos);
    	NRF_TIMER3->EVENTS_COMPARE[0] = 0;
    
    		// GPIOTE init
    	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
    
    		// PPI GPIOTE counter init on PPI CH1 set up to start the count
    	NRF_PPI->CHEN |= 1 << 1;										// Enable the channel - CH1
    	*(&(NRF_PPI->CH1_EEP)) = (uint32_t)&NRF_GPIOTE->EVENTS_IN[0];	// Event end point
    	*(&(NRF_PPI->CH1_TEP)) = (uint32_t)&NRF_TIMER4->TASKS_COUNT;	// Task end point
    	NRF_PPI->CHENSET |= 1 << 1;										// Enable the SET function
    
    		// PPI timer stop counter init on PPI CH0 set up to end the count
    	NRF_PPI->CHEN |= 1 << 0;
    	*(&(NRF_PPI->CH0_EEP)) = (uint32_t)&NRF_TIMER3->EVENTS_COMPARE[0];
    	*(&(NRF_PPI->CH0_TEP)) = (uint32_t)&NRF_TIMER4->TASKS_STOP;
    	NRF_PPI->CHENSET |= 1 << 0;
    
    	NRF_TIMER3->TASKS_START = 1;
    	NRF_TIMER4->TASKS_START = 1;
    }
    
    
    
    static volatile uint32_t freqDetected = 0;
    
    extern "C" void TIMER3_IRQHandler(void)
    {
    	if (NRF_TIMER3->EVENTS_COMPARE[0] != 0)
    	{
    		NRF_TIMER3->EVENTS_COMPARE[0] = 0;
    		NRF_TIMER4->TASKS_CAPTURE[0] = 1;
    
    		freqDetected = NRF_TIMER4->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;
    	} else
    		hang(1);
    }
    

    I'm not sure I've fully wrapped my head around it though because the values I get for 

    freqDetected

    only report KHz and not down to the Hz - so a signal of 123456Hz returns 123 and I miss the 456 which is the important part. When I change to 

    NRF_TIMER3->CC[0] = 16000000ULL;

    then I get freqDetected values down to the hertz: 123456 but then the sampling takes a full 1000ms where I need it to take about 10ms.

    What, if anything, might I be doing wrong, if you can see it?

    Thanks!

    Kevin

  • Thanks Hugh. I'll try that later today. But even on high impedance output, wouldn't the sig gen still be driving the signal hard into the input (which is also high impedance - an I/O pin on the µC?)

  • Simple answer - no; the high impedance output cannot drive "hard" and the coax cable capacitance will therefore produce a very soggy rise and fall at the I/O pin on the uC - which is very susceptible to noise and bounce and hence incorrect counts.

  • Well, interesting: It doesn't appear to be the signal at the input. Instead it appears to have something to do with a conflict with the SoftDevice: When I disable running the SoftDevice (but still initializing it), I get a bad count exactly every 65 times the IRQ is called to capture the count. All other counts are dead accurate. When I run the SoftDevice (even without connecting to it via BLE), some counts are bad but randomly so - not every 65 IRQ reads.

    I'm on to SOMETHING, but not sure what... Investigating...

  • Coax == antenna/Ariel, odds on radiated interference from the stack, oh except I see you have disabled it. Usual to use a twisted-pair differential driver at the source, such as RS485 which is cheap and reliable, with termination at the receiver. I doubt that your sig gen has that but the end design could include it.

  • Here's the latest. I'm totally confused so if anything pops into anyones head, please let me know. If not, I'll just move on with the project and "fix it in post" using a few pole software LPF. That's an ugly solution since I don't know why this is happening, but I can live with it I think.

    First, I verified the signal integrity right at the input pin to the µC. It looks nice with the expected amount of edge ringing. I tried pulses with a 3ns raise/fall time and with a 300ns raise/fall time. There is no difference in the bad frequencies reported so the input pin seems pretty tolerant of sloppy vs clean edges. I do see the ringing disappear at about 30ns (with the same quantity of bad frequencies still reported) but that is expected. I tried other voltages too with still the same results.

    But here's what's weird: When I disable initializing the BLE SoftDevice and I disable calling the BLE process loop, I get about 4-10 bad readings over 3000 readings recorded. When I just initialize the BLE SoftDevice but do not call the BLE process loop, I get about 10-20 bad readings over 3000 recorded (so it goes up). When I initialize and call the BLE process loop as in normal operation, I get about 20-40 bad readings over 3000 recorded (goes up even more). This doesn't change if I connect to the BLE Peripheral device and send data to the Central - I still see about 20-40 bad readings.

    Strange...

    Anyway, thanks everyone! 

Reply
  • Here's the latest. I'm totally confused so if anything pops into anyones head, please let me know. If not, I'll just move on with the project and "fix it in post" using a few pole software LPF. That's an ugly solution since I don't know why this is happening, but I can live with it I think.

    First, I verified the signal integrity right at the input pin to the µC. It looks nice with the expected amount of edge ringing. I tried pulses with a 3ns raise/fall time and with a 300ns raise/fall time. There is no difference in the bad frequencies reported so the input pin seems pretty tolerant of sloppy vs clean edges. I do see the ringing disappear at about 30ns (with the same quantity of bad frequencies still reported) but that is expected. I tried other voltages too with still the same results.

    But here's what's weird: When I disable initializing the BLE SoftDevice and I disable calling the BLE process loop, I get about 4-10 bad readings over 3000 readings recorded. When I just initialize the BLE SoftDevice but do not call the BLE process loop, I get about 10-20 bad readings over 3000 recorded (so it goes up). When I initialize and call the BLE process loop as in normal operation, I get about 20-40 bad readings over 3000 recorded (goes up even more). This doesn't change if I connect to the BLE Peripheral device and send data to the Central - I still see about 20-40 bad readings.

    Strange...

    Anyway, thanks everyone! 

Children
No Data
Related