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

Offset in SAADC samples with Easy DMA and BLE

I have a nrf52 application that samples four saadc channels at 1kHZ. That is: Map four pins to ADC input and let Easy DMA take care of the sampling so that the data is processed ten times a second (100 * 4 samples). This works pretty well, except...

When I enable the BLE connection, the data is shifted in the buffer. Without BLE enabled, the data layout in the memory is as following {{1,2,3,4}, {1,2,3,4}, ...}. But, when BLE is activated, the memory layout is: {{4,1,2,3}, {4,1,2,3}, ...} I really don't know what causes the difference. I have no way to check if the data is shifted, or did the samples just swap places. I wonder if the softdevice blocks some of the samples that would cause the problem.

The saadc implementation is double buffered, like in "saadc_sample_from_two_pins - scan mode" here

The BLE implementation is based on ble_app_hrs_freertos in SDK 12.1.0. That is also the SDK version I'm using.

Any help would be appreciated.

  • Thanks Jørgen. I confirm that the first solution works. Triggering NRF_SAADC_TASK_START on event NRF_SAADC_EVENT_END solves the problem.

  • Are you able to elaborate on the code required to implement the first solution? I have tried the code below as a first attempt, but it hasn't resolved the issue and this is probably because I haven't interpreted the description of the first solution correctly.

    err_code = saadc_init();
    APP_ERROR_CHECK(err_code);
    
    nrf_drv_timer_config_t timer_config = NRF_DRV_TIMER_DEFAULT_CONFIG;
    err_code = nrf_drv_timer_init(&m_timer, &timer_config, timer_handler);
    RETURN_IF_ERROR(err_code);
    
    /* setup m_timer for compare event */
    uint32_t ticks = nrf_drv_timer_ms_to_ticks(&m_timer, meas_interval_ms);
    nrf_drv_timer_extended_compare(&m_timer, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);
    nrf_drv_timer_enable(&m_timer);
    
    uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer, NRF_TIMER_CC_CHANNEL0);
    uint32_t saadc_sample_event_addr = nrf_drv_saadc_sample_task_get();
    
    err_code = nrf_drv_ppi_init();
    RETURN_IF_ERROR(err_code);
    
    /* setup ppi channel so that timer compare event is triggering sample task in SAADC */
    err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
    RETURN_IF_ERROR(err_code);
    
    err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
    RETURN_IF_ERROR(err_code);
    
    err_code = nrf_drv_ppi_channel_assign(m_ppi_channel, timer_compare_event_addr, saadc_sample_event_addr);
    RETURN_IF_ERROR(err_code);
    
    err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel0);
    RETURN_IF_ERROR(err_code);
    
    err_code = nrf_drv_ppi_channel_enable(m_ppi_channel0);
    RETURN_IF_ERROR(err_code);
    
    err_code = nrf_drv_ppi_channel_assign(m_ppi_channel0, nrf_saadc_event_address_get(NRF_SAADC_EVENT_END), nrf_saadc_task_address_get(NRF_SAADC_TASK_START));
    RETURN_IF_ERROR(err_code);
    
  • Could you describe the problem a bit more? Does your code take the samples correctly, but the results get mixed up? Or is there some other kind of error?

    nrf_drv_ppi_channel_assign(m_ppi_channel0, nrf_saadc_event_address_get(NRF_SAADC_EVENT_END), nrf_saadc_task_address_get(NRF_SAADC_TASK_START));
    

    This is pretty much how I implemented it, although I first alloc and assign the channels and enable when everything is in place.

    I see no call to nrf_drv_saadc_buffer_convert function, that is needed to prepare the Easy DMA conversions.

  • Hi, this answer helped me a lot, and it seems to be working well!

    I was wondering though: what will happen if the START task is triggered while, for some reason, no buffer is queued up?

    ***edit***

    So, i checked the docs and nrf_drv_saadc.c, and it seems like things can still go wrong if you SAMPLE before setting RESULT?

    I also did a test where i sample for a long time, using the PPI solution, and did manage to still get a swap.

    I was wondering: will it help if i'd use the ADCs internal timer? **Edit** Ah, that can't be combined with scan mode.

  • If you trigger START task when no new buffer is "queued", the currently configured buffer will be overwritten. What do you mean by "SAMPLE before setting RESULT"? RESULTDONE is an event generated by the peripheral, it is not something you set (you can however clear it). You should not be able to get any "swaps" if using the PPI solution, as the buffer will always be ready to store samples. Please provide an example showing swapped buffers with PPI solution implemented.

Related