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

Parents
  • You need to update the buffer pointer and maxcount registers immediately after a STARTED event. Right now it's done after the END event. 
    By updating the buffer registers after the START task has been triggered you will overwrite the previous buffer as new buffers will not be used until the next START task. 

    See the SAADC's EasyDMA chapter.


    "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."
    Can you try to rephrase your description a bit, I did not quite catch why you're disabling the PPI to control the sampling, usually this is done with the MAXCNT register.

  • Thank you for your answer. But I do not fully understand it. How can I catch this STARTED event for calling nrf_drv_saadc_buffer_convert() then. Is there some samplecode for that?

    I have a hard time by understanding this PPI stuff like

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

    Well, MAXCNT is to set the maximum sample count for the buffer before firing the NRF_DRV_SAADC_EVT_DONE event, right?

    What I want to implement is 1 measurement per second. 10 measurements per channel are made at 1kHz. After the DONE event, I switch off the PPI channel so no more samples will be made. After 1 second, I switch on the PPI channel again for 10 measurements. Summarized I want to have one measurement per seccond but the measurements are a average out of 10 samples which were sampled in a high frequency.

    Is it clearer now?

    Thank you and best regards,
    Manuel

    #edit:

    When I activate the STARTED interrupt with nrf_saadc_int_enable(NRF_SAADC_INT_STARTED), I end up in a NRF_ERROR_INVALID_STATE error while calling APP_ERROR_CHECK(err_code) after enabling my PPI channel 1 which is responsible for triggering the sample task.

  • Unfortunately it starts to be very urgent. Any other ideas or suggestions?

    Thank you!

  • Well I see one potential issue in your code:

    you call nrf_drv_saadc_buffer_convert(m_buffer_pool[0], ADC_CHANNEL_COUNT), where you probably want to use SAMPLES_IN_BUFFER instead of ADC_CHANNEL_COUNT as the size of your buffers.

  • Thanks haakonsh. But this is intended. I'm using oversampling on three channels. Therfore I will have three values (each is an average of 256 samples) in the buffer after DONE event.

    I also found this: https://devzone.nordicsemi.com/f/nordic-q-a/48462/questions-on-saadc-calibration-gain-best-use

    Same code as I have except that I have three channels and I'm using higher oversampling. But it is still not working.

    My console output:

    Event number 6 is the calibration measurement which is always the same three values? Is this normal? And after this, the values start jumping.

  • Hi,

    I checked over your code and found an issue with how you trigger the tasks.

    You need to write '1' to the tasks in order to trigger the task. In your code you do this:

    NRF_SAADC->TASKS_START;

    While you should do this:

    NRF_SAADC->TASKS_START = 1;

    I see that this is what Håkon posted below as well.

    Effectively, the workaround is in implemented, and you will get stuck waiting for events that will never arrive.

    Best regards,
    Jørgen

  • Hi Jorgen,

    Sorry for the late answer. I have now implemented your suggestion and here is my new code:

    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_assign(m_ppi_channel_1,
                                              rtc_compare_event_addr,
                                              saadc_sample_task_addr);
    																					
        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);
    	
    	nrf_drv_rtc_counter_clear(&rtc);
    	//reactivate compare irq! 
    	nrf_drv_rtc_cc_set(&rtc,0,COMPARE_COUNTERTIME * 8,true);
    }
    
    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;
    	ret_code_t err_code;
    
        if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
        {
    		nrf_gpio_pin_toggle(LED);
    		
    		if((m_adc_evt_counter % SAADC_CALIBRATION_INTERVAL) == 0)       //Evaluate if offset calibration should be performed. Configure the SAADC_CALIBRATION_INTERVAL constant to change the calibration frequency
            {
                nrf_drv_saadc_abort();                                      // Abort all ongoing conversions. Calibration cannot be run if SAADC is busy
                m_saadc_calibrate = true;                                   // Set flag to trigger calibration in main context when SAADC is stopped
            }
        
            NRF_LOG_INFO("ADC event number: %d", (int)m_adc_evt_counter);
    			
    		for (i = 0; i < ADC_CHANNEL_COUNT; i++)
    		{
    			//NRF_LOG_INFO("%d", p_event->data.done.p_buffer[i]);
    			switch(i%4)
    			{
    
    				case 0: thermistor_1_sum = p_event->data.done.p_buffer[i];
    								break;
    				case 1: thermistor_2_sum = p_event->data.done.p_buffer[i];
    								break;	
    				case 2: battery_voltage_sum = p_event->data.done.p_buffer[i];
    								break;	
    			}
    		}
    						
    		if(m_saadc_calibrate == false)
            {
                err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_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);
    			
    			measDone = 1;
            }
    		
    		m_adc_evt_counter++;
    		
    		nrf_gpio_pin_clear(THERMISTOR_VREF);
        }
    	
    	else if (p_event->type == NRF_DRV_SAADC_EVT_CALIBRATEDONE)
        {
    		nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);
    		while(NRF_SAADC->EVENTS_STOPPED == 0);
    		NRF_SAADC->EVENTS_STOPPED = 0;
    		
            err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], ADC_CHANNEL_COUNT);             //Set buffer so the SAADC can write to it again. 
            APP_ERROR_CHECK(err_code);
            err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], ADC_CHANNEL_COUNT);             //Need to setup both buffers, as they were both removed with the call to nrf_drv_saadc_abort before calibration.
            APP_ERROR_CHECK(err_code);
        
            NRF_LOG_INFO("SAADC calibration complete !");                                              //Print on UART    
    
    		battery_voltage_sum = 0;
    		voltage_sum = 0;
    		thermistor_1_sum = 0;
    		thermistor_2_sum = 0;			
        }
    }
    
    void saadc_init(void)
    {
        ret_code_t err_code;
        
        //Configure SAADC
        nrf_drv_saadc_config_t  config = NRF_DRV_SAADC_DEFAULT_CONFIG;
        config.resolution = NRF_SAADC_RESOLUTION_14BIT;  
        config.oversample = SAADC_OVERSAMPLE_OVERSAMPLE_Over256x;
        //config.low_power_mode = true; 
        
        //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(2, &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;
        channel_config.acq_time = SAADC_CH_CONFIG_TACQ_40us;
        channel_config.burst = NRF_SAADC_BURST_ENABLED;
        
        //Initialize SAADC channel 1
        err_code = nrf_drv_saadc_channel_init(0, &channel_config);
        APP_ERROR_CHECK(err_code);
        
        //Configure SAADC channel 2
        channel_config.pin_p = NRF_SAADC_INPUT_AIN1;
        channel_config.acq_time = SAADC_CH_CONFIG_TACQ_40us;
        
        //Initialize SAADC channel 2
        err_code = nrf_drv_saadc_channel_init(1, &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], 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], ADC_CHANNEL_COUNT);
        APP_ERROR_CHECK(err_code);
    }

    RTC:

    static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
    {
        if (int_type == NRF_DRV_RTC_INT_COMPARE0)
        {
    		nrf_gpio_pin_set(THERMISTOR_VREF);
            //nrf_gpio_pin_toggle(LED);
    		nrf_drv_rtc_counter_clear(&rtc);
    		//reactivate compare irq! 
    		nrf_drv_rtc_cc_set(&rtc,0,COMPARE_COUNTERTIME * 8,true);
        }
    		
        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 = 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 * 8,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();
    	
    	NRF_POWER->DCDCEN = 1;	//Enabling the DCDC converter for lower current consumption
    	
    	lfclk_config();
    	rtc_config();
    	
    	nrf_gpio_cfg_output(THERMISTOR_VREF);
    	nrf_gpio_pin_set(THERMISTOR_VREF);
    		
        saadc_init();
    	saadc_sampling_event_init();
        saadc_sampling_event_enable();
    		
    	while(1)
        {
            if(m_saadc_calibrate == true)
    		{
    			// Disable the PPI triggering of the sample task
    			saadc_sampling_event_disable();
    
    			while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS); //Trigger calibration task
    			while(!NRF_SAADC->EVENTS_CALIBRATEDONE);
    			NRF_SAADC->EVENTS_CALIBRATEDONE = 0;
    			
    			NRF_SAADC->EVENTS_STARTED = 0;
    			NRF_SAADC->TASKS_START = 1;
    			while(!NRF_SAADC->EVENTS_STARTED);
    			NRF_SAADC->EVENTS_STARTED = 0;
    			
    			// Enable the PPI triggering of the sample task
    			saadc_sampling_event_enable();
    
    			m_saadc_calibrate = false;
    		}
    		
            if(measDone == 1)
            {
                //LOG VALUES
            }

    Indeed it works much better now! But still, in the first measurement after a calibration, there is a false value in the first buffer index. Here is the RTT output (error is marked yellow):

    I am still not able to solve this problem...

    Thank you and best regards,
    Manuel

Reply
  • Hi Jorgen,

    Sorry for the late answer. I have now implemented your suggestion and here is my new code:

    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_assign(m_ppi_channel_1,
                                              rtc_compare_event_addr,
                                              saadc_sample_task_addr);
    																					
        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);
    	
    	nrf_drv_rtc_counter_clear(&rtc);
    	//reactivate compare irq! 
    	nrf_drv_rtc_cc_set(&rtc,0,COMPARE_COUNTERTIME * 8,true);
    }
    
    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;
    	ret_code_t err_code;
    
        if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
        {
    		nrf_gpio_pin_toggle(LED);
    		
    		if((m_adc_evt_counter % SAADC_CALIBRATION_INTERVAL) == 0)       //Evaluate if offset calibration should be performed. Configure the SAADC_CALIBRATION_INTERVAL constant to change the calibration frequency
            {
                nrf_drv_saadc_abort();                                      // Abort all ongoing conversions. Calibration cannot be run if SAADC is busy
                m_saadc_calibrate = true;                                   // Set flag to trigger calibration in main context when SAADC is stopped
            }
        
            NRF_LOG_INFO("ADC event number: %d", (int)m_adc_evt_counter);
    			
    		for (i = 0; i < ADC_CHANNEL_COUNT; i++)
    		{
    			//NRF_LOG_INFO("%d", p_event->data.done.p_buffer[i]);
    			switch(i%4)
    			{
    
    				case 0: thermistor_1_sum = p_event->data.done.p_buffer[i];
    								break;
    				case 1: thermistor_2_sum = p_event->data.done.p_buffer[i];
    								break;	
    				case 2: battery_voltage_sum = p_event->data.done.p_buffer[i];
    								break;	
    			}
    		}
    						
    		if(m_saadc_calibrate == false)
            {
                err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_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);
    			
    			measDone = 1;
            }
    		
    		m_adc_evt_counter++;
    		
    		nrf_gpio_pin_clear(THERMISTOR_VREF);
        }
    	
    	else if (p_event->type == NRF_DRV_SAADC_EVT_CALIBRATEDONE)
        {
    		nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);
    		while(NRF_SAADC->EVENTS_STOPPED == 0);
    		NRF_SAADC->EVENTS_STOPPED = 0;
    		
            err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], ADC_CHANNEL_COUNT);             //Set buffer so the SAADC can write to it again. 
            APP_ERROR_CHECK(err_code);
            err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], ADC_CHANNEL_COUNT);             //Need to setup both buffers, as they were both removed with the call to nrf_drv_saadc_abort before calibration.
            APP_ERROR_CHECK(err_code);
        
            NRF_LOG_INFO("SAADC calibration complete !");                                              //Print on UART    
    
    		battery_voltage_sum = 0;
    		voltage_sum = 0;
    		thermistor_1_sum = 0;
    		thermistor_2_sum = 0;			
        }
    }
    
    void saadc_init(void)
    {
        ret_code_t err_code;
        
        //Configure SAADC
        nrf_drv_saadc_config_t  config = NRF_DRV_SAADC_DEFAULT_CONFIG;
        config.resolution = NRF_SAADC_RESOLUTION_14BIT;  
        config.oversample = SAADC_OVERSAMPLE_OVERSAMPLE_Over256x;
        //config.low_power_mode = true; 
        
        //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(2, &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;
        channel_config.acq_time = SAADC_CH_CONFIG_TACQ_40us;
        channel_config.burst = NRF_SAADC_BURST_ENABLED;
        
        //Initialize SAADC channel 1
        err_code = nrf_drv_saadc_channel_init(0, &channel_config);
        APP_ERROR_CHECK(err_code);
        
        //Configure SAADC channel 2
        channel_config.pin_p = NRF_SAADC_INPUT_AIN1;
        channel_config.acq_time = SAADC_CH_CONFIG_TACQ_40us;
        
        //Initialize SAADC channel 2
        err_code = nrf_drv_saadc_channel_init(1, &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], 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], ADC_CHANNEL_COUNT);
        APP_ERROR_CHECK(err_code);
    }

    RTC:

    static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
    {
        if (int_type == NRF_DRV_RTC_INT_COMPARE0)
        {
    		nrf_gpio_pin_set(THERMISTOR_VREF);
            //nrf_gpio_pin_toggle(LED);
    		nrf_drv_rtc_counter_clear(&rtc);
    		//reactivate compare irq! 
    		nrf_drv_rtc_cc_set(&rtc,0,COMPARE_COUNTERTIME * 8,true);
        }
    		
        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 = 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 * 8,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();
    	
    	NRF_POWER->DCDCEN = 1;	//Enabling the DCDC converter for lower current consumption
    	
    	lfclk_config();
    	rtc_config();
    	
    	nrf_gpio_cfg_output(THERMISTOR_VREF);
    	nrf_gpio_pin_set(THERMISTOR_VREF);
    		
        saadc_init();
    	saadc_sampling_event_init();
        saadc_sampling_event_enable();
    		
    	while(1)
        {
            if(m_saadc_calibrate == true)
    		{
    			// Disable the PPI triggering of the sample task
    			saadc_sampling_event_disable();
    
    			while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS); //Trigger calibration task
    			while(!NRF_SAADC->EVENTS_CALIBRATEDONE);
    			NRF_SAADC->EVENTS_CALIBRATEDONE = 0;
    			
    			NRF_SAADC->EVENTS_STARTED = 0;
    			NRF_SAADC->TASKS_START = 1;
    			while(!NRF_SAADC->EVENTS_STARTED);
    			NRF_SAADC->EVENTS_STARTED = 0;
    			
    			// Enable the PPI triggering of the sample task
    			saadc_sampling_event_enable();
    
    			m_saadc_calibrate = false;
    		}
    		
            if(measDone == 1)
            {
                //LOG VALUES
            }

    Indeed it works much better now! But still, in the first measurement after a calibration, there is a false value in the first buffer index. Here is the RTT output (error is marked yellow):

    I am still not able to solve this problem...

    Thank you and best regards,
    Manuel

Children
Related