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
}

Parents Reply Children
  • Are you saying to set up Timer2 as a counter and use the app timer to check every 10ms? Or set up Timer 2 as a counter and then set up another hardware timer to check?

  • Both can work, but my suggestion was the second one.

    Kenneth

  • I have it set up as follows to stop the first timer after 100ms and capture the value of the second timer (pulse counter). The CC value is always 0 and is not recording the pulses now. 

    //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_62500Hz;
        timer_cfg2.bit_width = NRF_TIMER_BIT_WIDTH_16;
        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 100ms interval on Timer 2
        nrf_drv_timer_extended_compare(&system_timer_cc_0_2, NRF_TIMER_CC_CHANNEL1, PULSE_READ_INTERVAL, NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK, true);
    
        //setup a counter for Speedo/Tach pulses
        nrf_drv_timer_config_t timer_cfg3 = NRF_DRV_TIMER_DEFAULT_CONFIG; //default settings should use NRF_TIMER_FREQ_8MHz, changable in sdk_config.h
        timer_cfg3.bit_width = NRF_TIMER_BIT_WIDTH_32;
        timer_cfg3.mode = NRF_TIMER_MODE_LOW_POWER_COUNTER;
        timer_cfg3.interrupt_priority = APP_IRQ_PRIORITY_HIGH;
    
        err_code = nrf_drv_timer_init(&system_timer_cc_0_3, &timer_cfg3, system_timer_cc_0_3_handler);
        APP_ERROR_CHECK(err_code);
    
    void system_timer_cc_0_3_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[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_PULLDOWN; //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_3 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_3, 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_3 shared peripherals are ready, enable the shared timer
        nrf_drv_timer_enable(&system_timer_cc_0_3);
    
    }
    
    void system_timer_cc_0_2_handler (nrf_timer_event_t event_type, void * p_context)
    {
        volatile uint32_t pulseCount;
        switch(event_type)
        {
            case NRF_TIMER_EVENT_COMPARE1:                    // call when timer 2 overflows
                NRF_TIMER3->TASKS_CAPTURE[2] = 1;             // This is asking to capture the current counter into CC[2]
                pulseCount = NRF_TIMER3->CC[2];               // counter value  at the instant when capture task was triggered is now ready to be read in CC[2]
                period = (uint32_t) ((1.0 / (float)pulses) * 100000);
                timerSetCC1(PULSE_READ_INTERVAL);
                pulses = 0;
                break;
                
            default:
                break;
        }
    }

Related