SSADC swapping scanning values depending on the speed

Hi,

We have an application scanning 4 ADC inputs AN1, AN5, AN6 and AN7

We setup the SSADC in scanning mode:


void saadc_init(void)
{
    ret_code_t err_code;
	
		nrf_saadc_channel_config_t channel_0_config =
			NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN1);
		nrf_saadc_channel_config_t channel_1_config =
			NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN5);
		nrf_saadc_channel_config_t channel_2_config =
			NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);
		nrf_saadc_channel_config_t channel_3_config =
			NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN7);
	
    channel_0_config.gain = NRF_SAADC_GAIN1_4;
    channel_0_config.reference = NRF_SAADC_REFERENCE_VDD4;
		channel_0_config.acq_time = NRF_SAADC_ACQTIME_20US;
	
    channel_1_config.gain = NRF_SAADC_GAIN1_4;
    channel_1_config.reference = NRF_SAADC_REFERENCE_VDD4;
		channel_1_config.acq_time = NRF_SAADC_ACQTIME_20US;
	
    channel_2_config.gain = NRF_SAADC_GAIN1_4;
    channel_2_config.reference = NRF_SAADC_REFERENCE_VDD4;
		channel_2_config.acq_time = NRF_SAADC_ACQTIME_20US;
	
    channel_3_config.gain = NRF_SAADC_GAIN1_4;
    channel_3_config.reference = NRF_SAADC_REFERENCE_VDD4;
		channel_3_config.acq_time = NRF_SAADC_ACQTIME_20US;

    err_code = nrf_drv_saadc_channel_init(0, &channel_0_config);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_saadc_channel_init(1, &channel_1_config);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_saadc_channel_init(2, &channel_2_config);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_saadc_channel_init(3, &channel_3_config);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_init( NULL, saadc_callback );
    APP_ERROR_CHECK(err_code);

    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);
			
}

We start sampling the ADC on a regular basis using PPI:

	err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel2);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_assign(m_ppi_channel2,
                                          nrf_drv_timer_event_address_get(&timer_3,
                                                                          NRF_TIMER_EVENT_COMPARE0),
                                          nrf_drv_saadc_sample_task_get() );
    APP_ERROR_CHECK(err_code);

Here is the SSADC callback, we do averaging here too.

void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
		static uint8_t channel = 0;
		static uint32_t array_index = 0;
		uint32_t avg_index;
	
		nrf_gpio_pin_set( DEBUG_PIN );
	
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
    {
        err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);
			
		debug_counter++;

		switch( channel )
		{
			case 0:
				
				adc_count_array[0][array_index] = p_event->data.done.p_buffer[0];
				channel++;
			
			break;
			
			case 1:
				
				adc_count_array[1][array_index] = p_event->data.done.p_buffer[1];
				channel++;
			
			break;

			case 2:
				
				adc_count_array[2][array_index] = p_event->data.done.p_buffer[2];
				channel++;
			
			break;
		
			case 3:
				
				adc_count_array[3][array_index] = p_event->data.done.p_buffer[3];
				channel = 0;
				
				array_index++;
				if( array_index > 9 )
				{
					
					
					array_index = 0;
					
					// Clear averages
					adc_count_average[0] = 0;
					adc_count_average[1] = 0;
					adc_count_average[2] = 0;
					adc_count_average[3] = 0;
					
					// Add all up
					for( avg_index = 0; avg_index < 10; avg_index ++ )
					{
						adc_count_average[0] += adc_count_array[0][avg_index];
						adc_count_average[1] += adc_count_array[1][avg_index];
						adc_count_average[2] += adc_count_array[2][avg_index];
						adc_count_average[3] += adc_count_array[3][avg_index];
					}
					
					// divide by 10
					
					adc_count_average[0] /= 10;
					adc_count_average[1] /= 10;
					adc_count_average[2] /= 10;
					adc_count_average[3] /= 10;
					
					adc_new_data = true;
					
				}
				
				
			break;
			
			default:			
			break;
			
		}
    }
}

The buffer is setup as follows: 

static nrf_saadc_value_t     m_buffer_pool[2][SAMPLES_IN_BUFFER];

SAMPLES_IN_BUFFER is set to 4

When we scan at low speed e.g. 5000us the values are correct:

When we scan at a higher speed e.g. 500us the values switch over:

The above values are from our averaging buffer.

We have also checked the result buffer directly. 

As you can see....

AN0 moves to AN5, AN5 moves to AN6, AN6 moves to AN7 and AN7 moves to AN0

Does anyone have any idea why these values are shifting position depending on the ppi / timer speed?

There is also a question on the forum that was never answered.

 SSADC PPI Order Problem 

 

  • Hello,

    As far as I am aware, the only known issue which can lead to this behavior you describe is if you use the calibrate offset task when the SAADC is active ([252] SAADC: Unexpected behavior when TASKS_CALIBRATEOFFSET is used during sampling). Are you doing calibration anywhere in your code outside of the saadc_callback()? 

    Thanks,

    Vidar

  • Hi Vidar,

    Happy new year!

    No we're not using that.

    However I think we've found the issue.

    We setup a test array and on each saadc call back we filled the array with the adc buffer.

    When analysing what we saw was that the buffer skip happened on the 6th call back.

    We then looked at a trace (see below), the top line indicates timer interrupts and the next two i2c operation in the saadc callback. As you can see there was a period after the 5th where there is no call back.

    It turns out this is BLE setup, as we set up the timer and ADC before BLE setup.

    Moving it fixed the issue.

    We still have a few questions:

    1. Why would the BLE blocking shift the SAADC buffer pointer?

    2. Does nrfx_saadc_sample() only start the ADC acquisition (not conversion)?

    3. If there are multiple channels are they all acquired at the same time or sequentially?

    4. Is the ADC callback called when the acquisition is completed?

    5. When we call nrfx_saadc_sample() to convert is there a way to know when its completed?

    Thanks  

  • Happy new year!

    Thanks for the update. I think I know what is happening now: the TIMER will continue to trigger the SAADC sample task even if the application has been prevented from processing the NRF_DRV_SAADC_EVT_DONE event and provided the SAADC with a new DMA sample buffer, same as the scenario Jørgen describes in this post:  RE: Offset in SAADC samples with Easy DMA and BLE .

    1.  This seems to be a side effect of not following the task and event order outlined in the product specification. I.e. if the SAMPLE task gets triggered before the next DMA buffer has been supplied and the start task has been triggered by nrf_drv_saadc_buffer_convert().

    2. It will do both. But it requires that you have run the nrf_drv_saadc_buffer_convert() first to prepare the SAADC for sampling.

    3. Sequentially.

    4. Yes, the callback is invoked when the sample buffer has been filled. 

    5. Yes, it will trigger the NRF_DRV_SAADC_EVT_DONE (assuming a single conversion will fill the sample buffer).

Related