Timer 2 Capture at Low Frequency Input

I am currently using a Capture Event on Timer 2. I will post my initialization code for that event. I am sending a square wave (incoming wave is 10Vpp with a 2.5 V offset and 50% duty cycle signaled down to a 3V input for the nrf52832 GPIOTE input) to mimic a car's speed and tachometer sensor to translate the timer ticks into a car speed in mph or engines revs in rpm. At frequencies below 50 Hz, the timer seems to capture at times inconsistent values, causing the pointer to jump erratically before stabilizing. At frequencies above 50 Hz, I do not have this issue. Any reason why these low frequencies could be causing this? I have scoped the incoming signal and it is smooth and consistent, so no issues there. The period of the incoming signal becomes jumpy at these lower frequencies.  

 //setup a timer for Speedo/Tach pulses
    nrf_drv_timer_config_t timer_cfg2 = NRF_DRV_TIMER_DEFAULT_CONFIG; //default settings should use NRF_TIMER_FREQ_8MHz, changable in sdk_config.h
    timer_cfg2.frequency = NRF_TIMER_FREQ_8MHz;
    timer_cfg2.interrupt_priority = APP_IRQ_PRIORITY_HIGH;

    err_code = nrf_drv_timer_init(&system_timer_cc_0_2, &timer_cfg2, system_timer_cc_0_2_handler);
    APP_ERROR_CHECK(err_code);

    //set up timer compare on CC[1] for overflow on Timer 2, SPD_COUNT_MAX = 66535 for 16bit timing
    nrf_drv_timer_extended_compare(&system_timer_cc_0_2, NRF_TIMER_CC_CHANNEL1,SPD_COUNT_MAX, NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK, true);

    system_timer_cc_0_1_init(); //setup system timer 1 events and tasks
    system_timer_cc_0_2_init(); //setup system timer 2 events and tasks

void system_timer_cc_0_2_init(void)
{
    uint32_t capture_task_addr;
    uint32_t compare_evt_addr;
    uint32_t gpiote_task_addr;
    uint32_t time_ticks;
    ret_code_t err_code;
    nrf_drv_gpiote_in_config_t in_config;

    /* CC[1]
     * Set PPI task for overflow on system timer channel[1]
     * 
    */
    /*
    compare_evt_addr = nrf_drv_timer_event_address_get(&system_timer_cc_0_2, NRF_TIMER_EVENT_COMPARE1);
    */
    // Enable the compare event interrupt on Channel 1
    nrf_drv_timer_compare_int_enable(&system_timer_cc_0_2, NRF_TIMER_CC_CHANNEL1);

    /* CC[2]
     * Setup PPI tasks for GPIOTE and system timer channel[2]
     * for speedo/tach
    */

    //VCE: The below block needs to be called for each pin
    in_config.pull = NRF_GPIO_PIN_NOPULL; //User defined
    in_config.sense = NRF_GPIOTE_POLARITY_LOTOHI; //User defined
    in_config.hi_accuracy = true; //User defined
    in_config.is_watcher = false; //Don't change this
    in_config.skip_gpio_setup = false; //Don't change this

    //enable the pin for GPIOTE
    err_code = nrf_drv_gpiote_in_init(SPEEDO_TACHO_PIN_NUMBER, &in_config, speedo_tach_input_handler);
    APP_ERROR_CHECK(err_code);
    
    //setup a ppi channel to associate with the system_timer_cc_0_2 capture events
    err_code = nrf_drv_ppi_channel_alloc(&(ppi_channel[2]));
    APP_ERROR_CHECK(err_code);

    capture_task_addr = nrf_drv_timer_event_address_get(&system_timer_cc_0_2, NRF_TIMER_TASK_CAPTURE2);
    nrfx_gpiote_init(); 
    gpiote_task_addr = nrf_drv_gpiote_in_event_addr_get(SPEEDO_TACHO_PIN_NUMBER);

    err_code = nrf_drv_ppi_channel_assign(ppi_channel[2],  gpiote_task_addr, capture_task_addr);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_ppi_channel_enable(ppi_channel[2]);
    APP_ERROR_CHECK(err_code);
    
    nrf_drv_gpiote_in_event_enable(SPEEDO_TACHO_PIN_NUMBER, true);

    //Once all system_timer_cc_0_2 shared peripherals are ready, enable the shared timer
    nrf_drv_timer_enable(&system_timer_cc_0_2);
}



/*
 * system_timer_cc_0_2_handler()
 * cc[1] match event handler on speedo/tach
 * this is the place to handle overflows of timer 
 * inputs: event_type - determines what timer channel event this is
 *         p_context - any event details that may be expected
 */
void system_timer_cc_0_2_handler (nrf_timer_event_t event_type, void * p_context)
{
    volatile uint16_t timerCount = 0;
    switch(event_type)
    {
        case NRF_TIMER_EVENT_COMPARE1:                    // call when timer 2 overflows
            nrf_drv_timer_pause(&system_timer_cc_0_2);    // pause timer
            mOverFlowCount++;

            if (accumSpdCnt > (SPD_PERIOD_MAX - SPD_COUNT_MAX))
            {
                    accumSpdCnt = SPD_PERIOD_MAX;    // limit peak value

                    // If we are at the max value (and really we should never
                    // get here since previous passed through this code should
                    // have published the value).  This is a fail safe and therefore
                    // we should not scale the value by the hw divider
            }
            else
            {
                  accumSpdCnt += SPD_COUNT_MAX;    // room to add more ticks

                  // When the pulse count exceeds the current value, we are slowing down
                  // and need to publish the values so the gauge values decrease
                  if (accumSpdCnt > curSpdCnt)
                  {
                      curSpdCnt = accumSpdCnt;
                  }
            } 
            timerSetCC1(SPD_COUNT_MAX);
            break;
            
        default:
            break;
            
    }
    
}

/*
 * speedo_tach_input_handler()
 * cc[2] match event handler on speedo/tach
 * this is the place to capture pulse period
 * inputs: pin - GPIOTE pin triggered
 *         action - type of Input that occured
 */
void speedo_tach_input_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
    volatile uint16_t timerCount;
    switch(action)
    {
        case NRF_GPIOTE_POLARITY_LOTOHI:
            nrf_drv_timer_pause(&system_timer_cc_0_2);    // pause timer
            NRF_TIMER2->TASKS_CAPTURE[2] = 1;             // This is asking to capture the current counter into CC[2]
            timerCount = NRF_TIMER2->CC[2];               // counter value  at the instant when capture task was triggered is now ready to be read in CC[2]
            // Avoid overflowing the accumulated number of counts
            if (accumSpdCnt > (SPD_PERIOD_MAX - timerCount))
            {
                  // Limit the peak value:
                  // if the count is at the max value, this is a fail safe
                  // to keep it there.  We should have on prior passes
                  // already reached the max speed
                  accumSpdCnt = SPD_PERIOD_MAX;     // limit peak value
            }
            else
            {
                  // Add the new tick count - there is room for more
                  accumSpdCnt += (timerCount);
        
                  // Publish Speedo input pulse period in uS and the associate hw divider value
  		  if(SpdReadingLock == 0)
  		  {
  			curSpdCnt = accumSpdCnt;
  			SpdReadingLock = 1;
  		  }
            }
     
            // Reset the accumulated count - even if skipping publishing due
            // to changing the HW divider.  We need to count from edge to
            // edge of the sample pulse
            accumSpdCnt = 0;
            mOverFlowCount = 0;
            timerSetCC1(SPD_COUNT_MAX);
            break;
        default:
            break;
      }
}

// clears value from timer 2 and reloads max ticks count for speedo/tach measurements
void timerSetCC1(uint16_t val)
{
    nrf_drv_timer_clear(&system_timer_cc_0_2);    // CLears old timer value to start at 0.
    nrf_drv_timer_extended_compare(&system_timer_cc_0_2, NRF_TIMER_CC_CHANNEL1, val, NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK, true);
    nrf_drv_timer_resume(&system_timer_cc_0_2);    // resume timer
}

Related