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

Capture timer using PPI adc DONE event

Hello,

First time post and brand new to the nRF52832 chip. I am tasked with building firmware that will synchronize ADC samples across multiple devices. I have started with the BLE time_sync demo and have verified that the BLE time synchronization is working very well with TIMER2 & TIMER3. Each device begins capturing ADC samples at the same time and sends the samples to a central device with an included time stamp. Each BLE packet contains 75 samples plus a 32-bit timestamp. The timestamp can either be taken when the first of the 75 samples is captured or the time of the last sample capture.

The ADC sample capture is triggered off of a PPI timer running at 256Hz. This is setup as follows:

    nrf_ppi_channel_endpoint_setup(NRF_PPI_CHANNEL6, 
        (uint32_t) nrf_timer_event_address_get(NRF_TIMER3, NRF_TIMER_EVENT_COMPARE4),
        nrf_drv_saadc_sample_task_get());
        
    The data is stored via DMA using 2 buffers:

    //Set up DMA for conversion, triggered w/ NRF_SDADC_TASK_SAMPLE or nrfx__saadc_sample()
    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER);
    APP_ERROR_CHECK(err_code);

When a DMA buffer is filled, I receive the NRF_SAADC_EVENT_DONE event via the saadc_callback.

My problem is that there is quite a bit of jitter in the timestamps that I am currently capturing in the saadc_callback method. I believe this is because of the delays caused by the BLE activity and the priority of this event. I need the timestamp to be accurate to 1uS. I have verified that the two device clocks are accurate to this degree with the BLE syncing on average but my timestamps vary up to several milliseconds which is unacceptable.

I would like to capture the timer values (TIMER2 and TIMER3 used in the demo for tracking the synchronized device time) using PPI so that when I read the values in the callback, they are accurate to the time that the ADC event actually occurred. I have not been able to do this. I am trying to cache the values when the DMA DONE event occurs. I have tried the following with no luck. The Capture registers are never updated.

uint32_t chn7 = 7;
NRF_PPI->CHENCLR = (1 << chn7);
NRF_PPI->CH[chn7].EEP = (uint32_t) nrf_saadc_event_address_get(NRF_SAADC_EVENT_DONE),
NRF_PPI->CH[chn7].TEP = (uint32_t) &m_params.high_freq_timer[0]->TASKS_CAPTURE[4];
NRF_PPI->FORK[chn7].TEP = (uint32_t) &m_params.high_freq_timer[1]->TASKS_CAPTURE[4];

Can someone help me with my configuration? Or provide an alternate way of getting the timestamp accuracy I need?

Thanks so much,

-Mike

Parents
  • I should add that the following code is called in my saadc_callback method and it's output always shows:

    <info> time_sync: Sample LS: 0, HS: -1

    uint32_t ts_timestamp_get_sample_ticks()
    {
        uint32_t sync_timer_val = m_params.high_freq_timer[0]->CC[4];
        uint32_t count_timer_val = m_params.high_freq_timer[1]->CC[4];
        uint32_t peer_count = 0;
    
        NRF_LOG_INFO("Sample LS: %d, HS: %d", count_timer_val, sync_timer_val);
    
        return (((count_timer_val + peer_count) * time_sync_max_val) + sync_timer_val);
    }

    Which means that TIMER3->CC[4] is not being updated. It still contains the 0xFFFFFFFF that it was initialized with.

    -Mike

Reply
  • I should add that the following code is called in my saadc_callback method and it's output always shows:

    <info> time_sync: Sample LS: 0, HS: -1

    uint32_t ts_timestamp_get_sample_ticks()
    {
        uint32_t sync_timer_val = m_params.high_freq_timer[0]->CC[4];
        uint32_t count_timer_val = m_params.high_freq_timer[1]->CC[4];
        uint32_t peer_count = 0;
    
        NRF_LOG_INFO("Sample LS: %d, HS: %d", count_timer_val, sync_timer_val);
    
        return (((count_timer_val + peer_count) * time_sync_max_val) + sync_timer_val);
    }

    Which means that TIMER3->CC[4] is not being updated. It still contains the 0xFFFFFFFF that it was initialized with.

    -Mike

Children
  • Hey Mike, sorry for the delay, I was away on summer vacation. 

    If TIMER3's COMPARE[4] register is not updated then I assume the the following code is never executed:

    // I'm not sure what this code does; but removing the setup of CC[4] also causes things to not work
    if (m_params.high_freq_timer[0] == NRF_TIMER3 || m_params.high_freq_timer[0] == NRF_TIMER4)
    {
        // TIMERS 0,1, and 2 only have 4 compare registers
        m_params.high_freq_timer[0]->CC[4]   = time_sync_max_val / 2; // Only used for debugging purposes such as pin toggling
    }

    I suggest you verify this by setting a breakpoint here while debugging. 

    I also suggest you read the TIMER2 and 3's registers after you've initialized everything to verify that they are in the proper state, the same goes for the PPI and EGU registers.

    Also, to answer your code comment I found this comment the might help in time_synch.h, line 64:
    /** 16 MHz timer (e.g. NRF_TIMER2). NOTE: debug toggling only available if TIMER3 or TIMER4 is used for high_freq_timer[0]*/

    BR,
    Håkon.

  • Håkon,

    Thanks for getting back to me. The code you referenced is in the initialization section and only executes once. In my testing, I tried enabling and commenting out / disabling the line that sets CC[4] specifically. What I am hoping for is to get a PPI event to update CC[4] with the current TIMER3 value which isn't working. Whatever I set into CC[4] in the initialization routine is what I find whenever I read it back. It is not being updated.

    -Mike

Related