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.

  • Then you've triggered the start task before the calibration task, use:

    if(m_saadc_calibrate == true)
    {
    	NRF_LOG_INFO("SAADC calibration starting...");    //Print on UART
    	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;
    	while(!NRF_SAADC->EVENTS_STARTED);
    	NRF_SAADC->EVENTS_STARTED = 0;
    	
    	m_saadc_calibrate = false;
    }

    in order to re-trigger the start task, as this will reload the PTR and MAXCNT registers.

  • Thank you, but this is also not working together with my code. It stops and hangs on:

    while(!NRF_SAADC->EVENTS_STARTED);

    PPI is still active for the SAMPLE task.

    I'm really confused now about this START, STARTED, END, DONE- things...
    I'm not able to comprehend this procedure of events and tasks while debugging. 

  • Try this:

    if(m_saadc_calibrate == true)
    {
        // Disable the PPI triggering of the sample task
        
        NRF_SAADC->EVENTS_STOPPED = 0;
    	NRF_SAADC->TASKS_STOP;
    	while(!NRF_SAADC->EVENTS_STOPPED);
    	NRF_SAADC->EVENTS_STOPPED = 0;
    
    	NRF_LOG_INFO("SAADC calibration starting...");    //Print on UART
    	while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS); //Trigger calibration task
    	while(!NRF_SAADC->EVENTS_CALIBRATEDONE);
    	NRF_SAADC->EVENTS_CALIBRATEDONE = 0;
    	
    	NRF_SAADC->EVENTS_STOPPED = 0;
    	NRF_SAADC->TASKS_STOP;
    	while(!NRF_SAADC->EVENTS_STOPPED);
    	NRF_SAADC->EVENTS_STOPPED = 0;
    	
    	NRF_SAADC->EVENTS_STARTED = 0;
    	NRF_SAADC->TASKS_START;
    	while(!NRF_SAADC->EVENTS_STARTED);
    	NRF_SAADC->EVENTS_STARTED = 0;
    	
    	// Enable the PPI triggering of the sample task
    	
    	m_saadc_calibrate = false;
    }

    The PPI system will operate even though you've halted the CPU with the debugger, and the SAADC will also operate when the CPU is halted. 

  • Still hanging on while(!NRF_SAADC->EVENTS_STOPPED)

    Here is my current codeif this helps for further debugging

    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);
    }
    
    
    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;
    	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: 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)
        {
    				//nrfx_saadc_abort();
    			
            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        
        }
    }

    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 1
        nrf_saadc_channel_config_t channel_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(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);
        
        //Configure SAADC channel 3
        channel_config.pin_p = NRF_SAADC_INPUT_AIN2;
        channel_config.reference = NRF_SAADC_REFERENCE_INTERNAL;
        channel_config.gain = NRF_SAADC_GAIN4;
        channel_config.acq_time = SAADC_CH_CONFIG_TACQ_40us;
        
        //Initialize SAADC channel 3
        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], 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);
    }
    
    
    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_set(THERMISTOR_VREF);
    		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)
    {
    
    }

    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
        
        NRF_LOG_INFO("Zirbel Datalogger successfuly started...");
        
        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();
                
                NRF_SAADC->EVENTS_STOPPED = 0;
                NRF_SAADC->TASKS_STOP;
                while(!NRF_SAADC->EVENTS_STOPPED);
                NRF_SAADC->EVENTS_STOPPED = 0;
                
                NRF_LOG_INFO("SAADC calibration starting...");    //Print on UART
                while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS); //Trigger calibration task
                while(!NRF_SAADC->EVENTS_CALIBRATEDONE);
                NRF_SAADC->EVENTS_CALIBRATEDONE = 0;
                
                NRF_SAADC->EVENTS_STOPPED = 0;
                NRF_SAADC->TASKS_STOP;
                while(!NRF_SAADC->EVENTS_STOPPED);
                NRF_SAADC->EVENTS_STOPPED = 0;
                
                NRF_SAADC->EVENTS_STARTED = 0;
                NRF_SAADC->TASKS_START;
                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)
            {
                ...
            }

    Thank you for your help, I appreciate that very much!

    Manuel

Reply
  • Still hanging on while(!NRF_SAADC->EVENTS_STOPPED)

    Here is my current codeif this helps for further debugging

    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);
    }
    
    
    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;
    	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: 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)
        {
    				//nrfx_saadc_abort();
    			
            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        
        }
    }

    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 1
        nrf_saadc_channel_config_t channel_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(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);
        
        //Configure SAADC channel 3
        channel_config.pin_p = NRF_SAADC_INPUT_AIN2;
        channel_config.reference = NRF_SAADC_REFERENCE_INTERNAL;
        channel_config.gain = NRF_SAADC_GAIN4;
        channel_config.acq_time = SAADC_CH_CONFIG_TACQ_40us;
        
        //Initialize SAADC channel 3
        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], 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);
    }
    
    
    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_set(THERMISTOR_VREF);
    		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)
    {
    
    }

    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
        
        NRF_LOG_INFO("Zirbel Datalogger successfuly started...");
        
        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();
                
                NRF_SAADC->EVENTS_STOPPED = 0;
                NRF_SAADC->TASKS_STOP;
                while(!NRF_SAADC->EVENTS_STOPPED);
                NRF_SAADC->EVENTS_STOPPED = 0;
                
                NRF_LOG_INFO("SAADC calibration starting...");    //Print on UART
                while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS); //Trigger calibration task
                while(!NRF_SAADC->EVENTS_CALIBRATEDONE);
                NRF_SAADC->EVENTS_CALIBRATEDONE = 0;
                
                NRF_SAADC->EVENTS_STOPPED = 0;
                NRF_SAADC->TASKS_STOP;
                while(!NRF_SAADC->EVENTS_STOPPED);
                NRF_SAADC->EVENTS_STOPPED = 0;
                
                NRF_SAADC->EVENTS_STARTED = 0;
                NRF_SAADC->TASKS_START;
                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)
            {
                ...
            }

    Thank you for your help, I appreciate that very much!

    Manuel

Children
Related