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

nrf52 SAADC - NRFX_SAADC_EVT_DONE never called

using nRF5SDK 160098a08e2

I have the following code:

#define ADC_NOF_SAMPLES 16u
static int16_t s_sample_buffer[ ADC_NOF_SAMPLES ];
static nrf_saadc_channel_config_t const s_adc_channel0 = NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE( NRF_SAADC_INPUT_AIN5 );
static void saadc_event_isr( nrfx_saadc_evt_t const *p_event )
{
    volatile uint32_t numOfSamples = 0;
    switch ( p_event->type )
    {
    case NRFX_SAADC_EVT_DONE:  // Event generated when the buffer is filled with samples.
        numOfSamples = p_event->data.done.size;
        break;
    case NRFX_SAADC_EVT_LIMIT: // Event generated after one of the limits is reached.
        break;
    case NRFX_SAADC_EVT_CALIBRATEDONE: // Event generated when the calibration is complete.
        {
            nrfx_err_t err;
            err = nrfx_saadc_buffer_convert( &s_sample_buffer[0], ADC_NOF_SAMPLES );
            APP_ERROR_CHECK(err);
            /*err = nrfx_saadc_sample();
            APP_ERROR_CHECK(err);*/
        }
        break;
    default:
        break;
    }
}

    nrfx_err_t err;
    static nrfx_saadc_config_t const s_saadc_config = {
            .resolution = NRF_SAADC_RESOLUTION_12BIT,
            .oversample = NRF_SAADC_OVERSAMPLE_256X,
            .interrupt_priority = NRFX_SAADC_CONFIG_IRQ_PRIORITY,
            .low_power_mode = false
    };

    // ADC driver is initialized in non-blocking mode, since an event handler is given
    err = nrfx_saadc_init( &s_saadc_config, saadc_event_isr );

    if ( err == NRFX_SUCCESS )
    {
        // Initialize the ADC channel 0
        err = nrfx_saadc_channel_init( 0, &s_adc_channel0 );

        if ( err == NRFX_SUCCESS )
        {
            /*
            * Calibrate the offset with a call to nrfx_saadc_calibrate_offset.
            * You need to wait for the SAADCs DONE event before you can proceed.
            * */
            do
            {
                err = nrfx_saadc_calibrate_offset();
            } while ( err == NRFX_ERROR_BUSY );

            rc = 1u;
        }
    }



I never receive NRFX_SAADC_EVT_DONE. Please advise

Parents
  • Hello,

    Could you tell me, are you working out of the provided SAADC example from the SDK?
    Please provide the entire source code, as the root cause for your error might be located elsewhere.

    For future reference, when inserting code as part of a question, I ask that you format it with the code option to significantly increase readability.

    Best regards,
    Karl

  • So I tried to add the following code:

    			nrfx_err_t err;
    			err = nrfx_saadc_buffer_convert( &s_sample_buffer[0], ADC_NOF_SAMPLES );
    			APP_ERROR_CHECK(err);
    			err = nrfx_saadc_sample();
    			APP_ERROR_CHECK(err);
    
    			while(1)
    			{
    				while( nrfx_saadc_is_busy() ) {}
    				err = nrfx_saadc_sample();
    				APP_ERROR_CHECK(err);
    			}

    I never gets past nrfx_saadc_is_busy(). So the SAADC never gets into idle state. What is wrong with my initialization of this module ?

        nrfx_err_t err;
        static nrfx_saadc_config_t const s_saadc_config = {
        		.resolution = NRF_SAADC_RESOLUTION_12BIT,
        		.oversample = NRF_SAADC_OVERSAMPLE_DISABLED,//NRF_SAADC_OVERSAMPLE_128X,//NRF_SAADC_OVERSAMPLE_256X,
        		.interrupt_priority = NRFX_SAADC_CONFIG_IRQ_PRIORITY,
        		.low_power_mode = false
        };
    
        // ADC driver is initialized in non-blocking mode, since an event handler is given
        err = nrfx_saadc_init( &s_saadc_config, saadc_event_isr );
    
        if ( err == NRFX_SUCCESS )
        {
        	// Initialize the ADC channel 0
        	err = nrfx_saadc_channel_init( 0, &s_adc_channel0 );
    
        	if ( err == NRFX_SUCCESS )
        	{
        		/*
        		 * Calibrate the offset with a call to nrfx_saadc_calibrate_offset.
        		 * You need to wait for the SAADCs DONE event before you can proceed.
        		 * */
        		do
        		{
        			/*
    					The SAADC has a temperature dependent offset.
    					Therefore, it is recommended to calibrate the SAADC at least once before use,
    					and to re-run calibration every time the ambient temperature has changed by more than 10 °C.
    					Offset calibration is started by triggering the CALIBRATEOFFSET task, and the CALIBRATEDONE
    					event is generated when calibration is done.
        			 */
        			err = nrfx_saadc_calibrate_offset();
        		} while ( err == NRFX_ERROR_BUSY );
    
        		rc = 1u;
        	}
        }

  • BardenDK said:
    I do, here:

    My mistake, I had a hard time reading your initially posted code.
    Thank you for formatting all the code in your comments!

    BardenDK said:
    What is wrong with my initialization of this module ?

    Your initialization looks fine from what I can gather, I suspect this is rather a problem with the timing of the operations.

    Looking back at the code you posted originally, what is your intention when the NRFX_SAADC_EVT_DONE event occurs?
    Also, how often or at what event do you intend for the ADC to sample? I see from your first comment that you are showing me a call to app_timer_init, but I see no mention of it anywhere else.

    It is hard to get a good overview and understanding of your application without seeing all the relevant code.
    Let me know, if you would like me to make your ticket private at any time.

    Best regards,
    Karl

  • Alternatively, could you provide me with a stripped down version of your application - only the parts concerning the setup and use of the SAADC?
    So that I am able to replicate and debug on my part.

  • Hi Karl,

    I have had succes in getting the event to occur. But why i have to do, what i did, i do not fully understand when reading the API description. And it is also not a working solution for my project. Here goes, i did the follwoing:

     nrfx_err_t err;
     static nrf_saadc_channel_config_t const s_adc_channel0 = NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE( NRF_SAADC_INPUT_AIN5 );
        static nrfx_saadc_config_t const s_saadc_config = {
        		.resolution = NRF_SAADC_RESOLUTION_12BIT,
        		.oversample = NRF_SAADC_OVERSAMPLE_DISABLED,//NRF_SAADC_OVERSAMPLE_128X,//NRF_SAADC_OVERSAMPLE_256X,
        		.interrupt_priority = NRFX_SAADC_CONFIG_IRQ_PRIORITY,
        		.low_power_mode = false
        };
    
        // ADC driver is initialized in non-blocking mode, since an event handler is given
        err = nrfx_saadc_init( &s_saadc_config, saadc_event_isr );
    
        if ( err == NRFX_SUCCESS )
        {
        	// Initialize the ADC channel 0
        	err = nrfx_saadc_channel_init( 0u, &s_adc_channel0 );
    
        	if ( err == NRFX_SUCCESS )
        	{
        		/*
        		 * Calibrate the offset with a call to nrfx_saadc_calibrate_offset.
        		 * You need to wait for the SAADCs DONE event before you can proceed.
        		 * */
        		do
        		{
        			/*
    					The SAADC has a temperature dependent offset.
    					Therefore, it is recommended to calibrate the SAADC at least once before use,
    					and to re-run calibration every time the ambient temperature has changed by more than 10 °C.
    					Offset calibration is started by triggering the CALIBRATEOFFSET task, and the CALIBRATEDONE
    					event is generated when calibration is done.
        			 */
        			err = nrfx_saadc_calibrate_offset();
        		} while ( err == NRFX_ERROR_BUSY );
    
        		// The code below is from:
        		// https://devzone.nordicsemi.com/f/nordic-q-a/52512/look-like-a-bug-inside-the-function-nrfx_saadc_calibrate_offset-in-sdk-15-3-0-nrf5_sdk_15-3-0_59ac345
        		while ( nrfx_saadc_is_busy() ) // Wait for calibration to complete
        		{
        		    __WFE(); //
        		    __SEV(); //
        		    __WFE(); // This sequence puts the system to sleep (SystemON) while waiting
        		}
    
        		err = nrfx_saadc_buffer_convert( &s_sample_buffer[ 0u ], ADC_NOF_SAMPLES );
        		APP_ERROR_CHECK(err);
        		err = nrfx_saadc_sample();
        		APP_ERROR_CHECK(err);
    
        		while ( nrfx_saadc_is_busy() ) // Wait for calibration to complete
    			{
    				__WFE(); //
    				__SEV(); //
    				__WFE(); // This sequence puts the system to sleep (SystemON) while waiting
    
    				( void ) nrfx_saadc_sample();
    			}
    
        		rc = 1u;

    In the while loop from line 50 I am calling nrfx_saadc_sample(); and it looks like that for each call i get one sample.

    But this is not at all the behavior i have expected. I do not want a solution where I need to call a function for each sample. I expected it to work in a way where I could start the sampling and get the DONE event when the SAADC module had made the number of samples i had requested.

  • BardenDK said:
    But this is not at all the behavior i have expected. I do not want a solution where I need to call a function for each sample. I expected it to work in a way where I could start the sampling and get the DONE event when the SAADC module had made the number of samples i had requested.

    The most common use of the SAADC is to trigger it on a interrupt, for example generated by the RTC or any other timer. That way, you can get a sample every time a certain event happens or at a certain time interval.
    I still see no mention of where you use the timer you mentioned in your first comment, and you have not told me how often you intend for the ADC to sample.
    The buffer will not fill on its own, if the sampling happens too seldom or not at all. If this is the case, it could be the reason for the missing done event.
    Please tell me how often you intend for the sampling to happen, and if possible please also show me the code for how you have implemented that timing with the SAADC.

    I will get back to you on the code workaround that you have implemented, but my immediate thought is that the multiple WFE calls are unnecessary for your application.
    I will look closer on the code you have provided within a couple of hours. Please respond to the above questions in the meantime.

    Best regards,
    Karl

Reply
  • BardenDK said:
    But this is not at all the behavior i have expected. I do not want a solution where I need to call a function for each sample. I expected it to work in a way where I could start the sampling and get the DONE event when the SAADC module had made the number of samples i had requested.

    The most common use of the SAADC is to trigger it on a interrupt, for example generated by the RTC or any other timer. That way, you can get a sample every time a certain event happens or at a certain time interval.
    I still see no mention of where you use the timer you mentioned in your first comment, and you have not told me how often you intend for the ADC to sample.
    The buffer will not fill on its own, if the sampling happens too seldom or not at all. If this is the case, it could be the reason for the missing done event.
    Please tell me how often you intend for the sampling to happen, and if possible please also show me the code for how you have implemented that timing with the SAADC.

    I will get back to you on the code workaround that you have implemented, but my immediate thought is that the multiple WFE calls are unnecessary for your application.
    I will look closer on the code you have provided within a couple of hours. Please respond to the above questions in the meantime.

    Best regards,
    Karl

Children
No Data
Related