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

SAADC scan mode of 3 channels --> buffer order swap

Hi,

I already read all the forum posts about this buffer order swap problem of the SAADC. Especially this one: https://devzone.nordicsemi.com/f/nordic-q-a/20291/offset-in-saadc-samples-with-easy-dma-and-ble/79053#79053

I tried to implement the PPI solution, but I am not able to bring this to work. I have still a swap in the order every second call of the callback function. Can anyone help me?

This is pretty much my firmware (without BLE stuff at the moment):

I'm using a compare event of the RTC to trigger the sample task in scan mode. Another compare event is used to enable the ppi channel again after disabling it after 10 samples per channel. The reason for that is that I want to sample at a high frequency but only 10 samples per second. The second PPI channel is used for the fix from the link above.

PPI:

void saadc_sampling_event_init(void)
{
    ret_code_t err_code;

    err_code = nrf_drv_ppi_init();
    APP_ERROR_CHECK(err_code);

    uint32_t rtc_compare_event_addr = nrf_drv_rtc_event_address_get(&rtc, NRF_RTC_EVENT_COMPARE_0);//nrf_drv_timer_compare_event_address_get(&m_timer, NRF_TIMER_CC_CHANNEL0);
    uint32_t saadc_sample_task_addr   = nrf_drv_saadc_sample_task_get();

    /* setup ppi channel so that timer compare event is triggering sample task in SAADC */
    err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel_1);
    APP_ERROR_CHECK(err_code);
	err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel_2);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_ppi_channel_assign(m_ppi_channel_1,
                                          rtc_compare_event_addr,
                                          saadc_sample_task_addr);
																					
    APP_ERROR_CHECK(err_code);
		
    err_code = nrf_drv_ppi_channel_assign(m_ppi_channel_2, nrf_saadc_event_address_get(NRF_SAADC_EVENT_END), nrf_saadc_task_address_get(NRF_SAADC_TASK_START));
	APP_ERROR_CHECK(err_code);
		
    err_code = nrf_drv_ppi_channel_enable(m_ppi_channel_2);
    APP_ERROR_CHECK(err_code);
}

SAADC:

#define SAMPLES_IN_BUFFER 10
#define ADC_CHANNEL_COUNT 3

void saadc_sampling_event_enable(void)
{
    ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel_1);

    APP_ERROR_CHECK(err_code);
}

void saadc_sampling_event_disable(void)
{
    ret_code_t err_code = nrf_drv_ppi_channel_disable(m_ppi_channel_1);

    APP_ERROR_CHECK(err_code);
}


void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
		int value = 0;
		int i;
	
		//Event is triggered when buffer is full! Not when one conversion is done!
		////https://devzone.nordicsemi.com/f/nordic-q-a/14777/ble_app_proximity-saadc-channel-data#post-id-85497
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
    {
        ret_code_t err_code;

        err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER*ADC_CHANNEL_COUNT); //Set buffer so the SAADC can write to it again. This is either "buffer 1" or "buffer 2"
        APP_ERROR_CHECK(err_code);
        
        NRF_LOG_INFO("ADC event number: %d", (int)m_adc_evt_counter);
				
        for (i = 0; i < SAMPLES_IN_BUFFER*ADC_CHANNEL_COUNT; i++)
        {
					NRF_LOG_INFO("%d", p_event->data.done.p_buffer[i]);
					switch(i%3)
					{
						case 0: battery_voltage_sum = battery_voltage_sum + p_event->data.done.p_buffer[i];
										break;
						case 1: thermistor_1_sum = thermistor_1_sum + p_event->data.done.p_buffer[i];
										break;
						case 2: thermistor_2_sum = thermistor_2_sum + p_event->data.done.p_buffer[i];
										break;	
					}
        }
					
				measDone = 1;
        m_adc_evt_counter++;
				
				//stop sampling until Compare 1 Event of RTC
				saadc_sampling_event_disable();
    }
}

void saadc_init(void)
{
		//Source for battery measurement: 
		//https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/measuring-lithium-battery-voltage-with-nrf52
		//Sampling frequeny should be 1Hz-10Hz for high saadc input resistance value! Otherwise voltage devider will be affected by the input reistance!
	
		//Total time < Sum(CH[x].tACQ+tCONV), x=0..enabled channels (Source: Datasheet)
		//Total time < #Channels*(40us + 2us)
		//3 Channels: 126us --> max sampling rate = 1/126uS = 7.94kHz --> set RTC Compare Event (PPI) to this frequency
	
	  ret_code_t err_code;

		//Configure SAADC
		nrf_drv_saadc_config_t  config = NRF_DRV_SAADC_DEFAULT_CONFIG;
		config.resolution = NRF_SAADC_RESOLUTION_12BIT;  
    config.oversample = NRF_SAADC_OVERSAMPLE_DISABLED;  

		//Initialize SAADC
    err_code = nrf_drv_saadc_init(&config, saadc_callback);                       
    APP_ERROR_CHECK(err_code);

		//Configure SAADC channel 0
		nrf_saadc_channel_config_t channel_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN7);
		channel_config.gain = NRF_SAADC_GAIN1_4;
		channel_config.acq_time = SAADC_CH_CONFIG_TACQ_3us; //needed for maximum source resistance of 800kOhm (Datasheet) --> 40us. With additional C --> 3us
		//channel_config.burst = NRF_SAADC_BURST_ENABLED;
	                                        
		//Initialize SAADC channel 0
    err_code = nrf_drv_saadc_channel_init(0, &channel_config);
    APP_ERROR_CHECK(err_code);
	
	  //Configure SAADC channel 1
		channel_config.pin_p = NRF_SAADC_INPUT_AIN0;
	 	channel_config.reference = NRF_SAADC_REFERENCE_VDD4;
		channel_config.gain = NRF_SAADC_GAIN1_4;

		//Initialize SAADC channel 1
    err_code = nrf_drv_saadc_channel_init(1, &channel_config);
    APP_ERROR_CHECK(err_code);
		
		//Configure SAADC channel 2
		channel_config.pin_p = NRF_SAADC_INPUT_AIN1;

		//Initialize SAADC channel 2
    err_code = nrf_drv_saadc_channel_init(2, &channel_config);
    APP_ERROR_CHECK(err_code);

		//Set SAADC buffer 1. The SAADC will start to write to this buffer
    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER*ADC_CHANNEL_COUNT);
    APP_ERROR_CHECK(err_code);

		//Set SAADC buffer 2. The SAADC will write to this buffer when buffer 1 is full. This will give the applicaiton time to process data in buffer 1.
    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER*ADC_CHANNEL_COUNT);
    APP_ERROR_CHECK(err_code);
}

RTC:

static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
{
		static int i = 2;
    if (int_type == NRF_DRV_RTC_INT_COMPARE0)
    {
        //nrf_gpio_pin_toggle(LED);
				//nrf_drv_rtc_counter_clear(&rtc);
				//reactivate compare irq! 
				nrf_drv_rtc_cc_set(&rtc,0,COMPARE_COUNTERTIME * i++,true);
    }
		
		else if (int_type == NRF_DRV_RTC_INT_COMPARE1)
    {
        nrf_gpio_pin_toggle(LED);
				nrf_drv_rtc_counter_clear(&rtc);
				//reactivate compare irq! 
				nrf_drv_rtc_cc_set(&rtc,0,COMPARE_COUNTERTIME * 1,true);
				nrf_drv_rtc_cc_set(&rtc,1,COMPARE_COUNTERTIME * 1000,true);
				i = 2;
				saadc_sampling_event_enable();
    }
		
    else if (int_type == NRF_DRV_RTC_INT_TICK)
    {
        rtcCounter++;
    }
		
}


static void lfclk_config(void)
{
    ret_code_t err_code = nrf_drv_clock_init();
    APP_ERROR_CHECK(err_code);

    nrf_drv_clock_lfclk_request(NULL);
}


static void rtc_config(void)
{
	uint32_t err_code;

	//Initialize RTC instance
	nrf_drv_rtc_config_t config = NRF_DRV_RTC_DEFAULT_CONFIG;
	config.prescaler = 32; // fRTC [kHz] = 32.768 / (PRESCALER + 1 ) = 1kHz
	//config.prescaler = 4095; // fRTC [kHz] = 32.768 / (PRESCALER + 1 ) --> 8Hz (minimum because prescsaler is 12bit)
	err_code = nrf_drv_rtc_init(&rtc, &config, rtc_handler);
	APP_ERROR_CHECK(err_code);

	//Enable tick event & interrupt
	nrf_drv_rtc_tick_enable(&rtc,true);

	//Set compare channel 0 to trigger interrupt after COMPARE_COUNTERTIME seconds
	err_code = nrf_drv_rtc_cc_set(&rtc,0,COMPARE_COUNTERTIME * 1,true); //1kHz
	APP_ERROR_CHECK(err_code);

	//Set compare channel 1 to trigger interrupt after COMPARE_COUNTERTIME seconds
	err_code = nrf_drv_rtc_cc_set(&rtc,1,COMPARE_COUNTERTIME * 1000,true); //1Hz
	APP_ERROR_CHECK(err_code);

	//Power on RTC instance
	nrf_drv_rtc_enable(&rtc);
}

void timer_handler(nrf_timer_event_t event_type, void * p_context)
{

}

Main:

int main(void)
{
	/* Configure board. */
    bsp_board_init(BSP_INIT_LEDS);
	bsp_board_leds_on();
	bsp_board_init(BSP_INIT_BUTTONS);

    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
    NRF_LOG_DEFAULT_BACKENDS_INIT();

	lfclk_config();
	rtc_config();
		
    saadc_init();
	saadc_sampling_event_init();
    saadc_sampling_event_enable();
		
	while(1)
    {
        if(measDone == 1)
        {
        ...

Thank you all and best regards! 
Manuel

Related