Configure sampling rate for nrf5340 SAADC

I'm working on the nrf5340 with a modified sample found here (main.c of the base sample here) and nrf connect sdk 1.7.1

I see that adc_sequence_options .interval_us can set the time between samples in microseconds, so I tried to use that to set my sampling rate like so:

const struct adc_sequence_options sequence_opts = {
	.interval_us = 17, // 17 us between 1000 samples
	.callback = NULL,
	.user_data = NULL,
	.extra_samplings = BUFFER_SIZE - 1,
};

, but the board prints Error in adc sampling: -16.

Is there a way to either resolve my interval_us bug or another way to configure the sampling rate?

Thanks

Parents
  • Hello,

    What function is it that returns -16? I see that it is adc_read(), but have you tried to investigate inside adc_read() to see why it returns -16?

    Also, did you do any modifications to the sample? Or is it exactly like the one I can download from the github that you refer to?

    Best regards,

    Edvin

  • Hi Edvin,

    I searched the entire solution for the print statement, but nothing came up and I couldnt find what was returning the -16.  (I am using SEGGER studios)

    Its almost identical to the github, I modified it to read from pin 5 instead of pin 4: #define ADC_1ST_CHANNEL_ID 1

    and I use the math library (math.h) to use some functions(sqrt, powf) on the samples I receive.

    Again, I am open to other methods of setting the sampling rate.

    Thanks for the help,

    Janpaul

Reply
  • Hi Edvin,

    I searched the entire solution for the print statement, but nothing came up and I couldnt find what was returning the -16.  (I am using SEGGER studios)

    Its almost identical to the github, I modified it to read from pin 5 instead of pin 4: #define ADC_1ST_CHANNEL_ID 1

    and I use the math library (math.h) to use some functions(sqrt, powf) on the samples I receive.

    Again, I am open to other methods of setting the sampling rate.

    Thanks for the help,

    Janpaul

Children
  • Hello Janpaul,

    I did some digging. The unmodified sample from the github seems to work well, but when you set the sampling rate to 17µs it seems to return an error. I tried to set it to 100µs (0.1ms), and in that case the error was not returned. I tried to debug, and traced it down to:

    static inline int adc_context_wait_for_completion(struct adc_context *ctx)
    {
    #ifdef CONFIG_ADC_ASYNC
    	if (ctx->asynchronous) {
    		return 0;
    	}
    #endif /* CONFIG_ADC_ASYNC */
    
    	k_sem_take(&ctx->sync, K_FOREVER);
    	return ctx->status;
    }

    in adc_context.h.

    It is the return ctx->status that returns -16.

    I see that this probably happens because of this function (same file):

    static inline void adc_context_complete(struct adc_context *ctx, int status)
    {
    #ifdef CONFIG_ADC_ASYNC
    	if (ctx->asynchronous) {
    		if (ctx->signal) {
    			k_poll_signal_raise(ctx->signal, status);
    		}
    
    		k_sem_give(&ctx->lock);
    		return;
    	}
    #endif /* CONFIG_ADC_ASYNC */
    
    	/*
    	 * Override the status only when an error is signaled to this function.
    	 * Please note that adc_context_request_next_sampling() might have set
    	 * this field.
    	 */
    	if (status != 0) {
    		ctx->status = status;
    	}
    	k_sem_give(&ctx->sync);
    }

    which again can comes from this:

    static inline void adc_context_request_next_sampling(struct adc_context *ctx)
    {
    	if (atomic_inc(&ctx->sampling_requested) == 0) {
    		adc_context_start_sampling(ctx);
    	} else {
    		/*
    		 * If a sampling was already requested and was not finished yet,
    		 * do not start another one from here, this will be done from
    		 * adc_context_on_sampling_done() after the current sampling is
    		 * complete. Instead, note this fact, and inform the user about
    		 * it after the sequence is done.
    		 */
    		ctx->status = -EBUSY;
    	}
    }

    So the issue is that the new sample is requested before the previous sample is done. The sequence_options.interval_us is too small. 

    Do you particularly need an interval_us of 17, or was this a random test? The maximum sample rate of the ADC is 200kHz, so theoretically, it is possible to sample every 5µs, but not using the zephyr adc drivers. In that case I believe you would need to use the adc drivers directly. 

    Let me know if you really need that short interval, or if that was just a test.

    Best regards,

    Edvin

  • Hi Edvin,

    thank you for your digging. I was  digging to a bit in VS Code debugger SDK Connect 1.9.1 on nRF5340DK with example code 

    https://github.com/too1/ncs-peripheral-uart-adc/tree/d575cdb9a2e0d7e5f0984a6a228bcb0d3a6f1a7c

    The only thing I had to chang for proper build and flash was the LOG_WRN() to simple printk(), afterwards got ADC raw value: 426 , 425 or 427

    I built up some voltagefollower opamp circuit and adjust (by poti VDD to GND) the output to 2.5V and measured with my multimeter.

    Should be 512 by 10bit resolution, so i changed ADC definitions to ADC_GAIN_1 and ADC_REFERENCE to ADC_REF_VDD_1.

    Now adc_read() output err is -22 and raw value is 0

    By digging I found 

    	switch (channel_cfg->reference) {
    	case ADC_REF_INTERNAL:
    		config.reference = NRF_SAADC_REFERENCE_INTERNAL;
    		break;
    	case ADC_REF_VDD_1_4:
    		config.reference = NRF_SAADC_REFERENCE_VDD4;
    		break;
    	
    	default:
    		printk("Selected ADC reference is not valid\n");		//LOG_ERR
    		return -EINVAL;
    	}

    wanted to write the case for ADC_REF_VDD_1 by myself but in the nrf_saadc.h file

    /** @brief Reference selection for the analog-to-digital converter. */
    typedef enum
    {
        NRF_SAADC_REFERENCE_INTERNAL = SAADC_CH_CONFIG_REFSEL_Internal, ///< Internal reference (0.6 V).
        NRF_SAADC_REFERENCE_VDD4     = SAADC_CH_CONFIG_REFSEL_VDD1_4    ///< VDD/4 as reference.
        
    } nrf_saadc_reference_t;

    also no definition in the typdef enum ... why is this? Do I write all by myself or is ther better example ?

    My purpose: I have some analog signal amplification PCB and would like to digitize by nRF controller and send by BLE. The signal should be sampled by around 10-20us (10kHz of interrest, nyquist and so on). I would appreciate so much for some example code Slight smile

    would like to differentialy measure the signal to REF2.5 from the opamp pcb board, is the best way to measure differential mode or just single but referenced to REF2.5 through other AIN pin?

    Many thanks in advance,

    Christoph

  • Hello Christoph,

    It doesn't look like this is the same issue as discussed in this ticket. Do you mean that the "failed with err code -22" is caused by the "Selected ADC reference is not valid"? EINVAL = 6, and what you see is -22 (0x16), so I am not sure how this would cause it to fail with -22. 

    ChrtistophAT said:

    wanted to write the case for ADC_REF_VDD_1 by myself but in the nrf_saadc.h file

    What exactly do you want to do with an ADC_REF_VDD_1? What are you trying to implement?

    And what chip are you using?

    BR,

    Edvin

  • Hi Edvin,

    didnt know about the small amount of reference voltages you can choose, I believe I one time saw some mor like real VDD and simple Gain 1. Anyway saw now the 

    https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrfx/drivers/saadc/hal.html?highlight=nrf_saadc_reference#c.nrf_saadc_reference_t

    right way how to do 

    and now everything is fine -> 512 when 2.5V applied by 10bit resolution.

    -22 failer code when wrong ADC_REF was selected. Sorry for writing in this -6 ticket.

    What are you trying to implement?

    I have some dry muscel signal sensor, which is amplifying and filtering the signal with reference to 2.5V (VCC/2) from the PCB. For purpose of powerline noise 50Hz, I would like to ADC the amplified signal from the sensor and transfer through bluetooth package.

    Questions:

    adc mode single or differential to 2.5V reference?

    do I have to use ADC/driver lib for this fast sampling (15-20us)?

    exists some better example like the one I am struggling around?

    github.com/.../quote]

    Unfortunately ther is no example/peripheral/ saadc in the sdk connect 1.9.1

    Thank you for your advice in advance,

    Christoph

  • ChrtistophAT said:
    Unfortunately ther is no example/peripheral/ saadc in the sdk connect 1.9.1

    Not that I am aware of. 

    I suggest that you take a quick peek at the nRF5 SDK\examples\peripheral\saadc, to familiarize yourself with how the ADC works, and then you can use the nrfx driver ADC instead of the zephyr implementation. This way you should be able to get a higher sample rate.

    But again, for further questions, please open a new ticket. 

    BR,

    Edvin

Related