Error -11 when doing ADC read

I'm trying to do an ADC measurement on AIN6 on an nRF52832.  This is how I have things set up:

proj.conf:

#ADC/Comparator
CONFIG_ADC=y
CONFIG_NRFX_COMP=y

Overlay file:

/ {

zephyr,user {
    io-channels = <&adc 6>;
};

&adc {
    #address-cells = <1>;
    #size-cells = <0>;

    channel@6 {
        reg = <6>;
        zephyr,gain = "ADC_GAIN_1_6";
        zephyr,reference = "ADC_REF_INTERNAL";
        zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
        zephyr,input-positive = <NRF_SAADC_AIN6>; /* P0.30 */
        zephyr,resolution = <12>;
        zephyr,oversampling = <4>;
    };
};

ADC initialisation (this gets called on power up of the chip)

void adc_init (void)
{
    int8_t ret;

    if (!device_is_ready(adc_chan6.dev)) {
		LOG_ERR("ADC device not found");
		return;
	}

	// Setup ADC
	ret = adc_channel_setup_dt(&adc_chan6);
    if (ret < 0) {
		LOG_ERR("ADC channel set up failed");
		return;
	}

	ret = adc_sequence_init_dt(&adc_chan6, &sequence);
    if (ret < 0) {
		LOG_ERR("ADC channel sequence init failed");
		return;
	}
}

And firmware:

const struct adc_dt_spec adc_chan6 = ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 0);

struct adc_sequence sequence = {
	.buffer      = &sample_buffer,
	.buffer_size = sizeof(sample_buffer),
};


void counter_callback(const struct device *dev, void *user_data)
{
	if (sample_count < MAX_NUM_SAMPLES) {
		err = adc_read(adc_chan6.dev, &sequence);
		if (err < 0) {
			LOG_ERR("Could not read (%d)", err);
		}
		adc_samples[sample_count] = sample_buffer;
		sample_count++;
	}
	else {
		sampling_complete = true;
	}
}

I've got a counter set up to trigger an ADC read every ~ 300usec, and I take 32 samples.  Problem I am having is that the ADC read is returning error -11.  According to errno.h, this equates to EAGAIN, and there isn't any explanation on what that actually means, so I'm not exactly sure what I need to be doing to fix it.

Can anyone explain what this error is indicating?  And is there anything in my ADC setup that would indicate why I'm gettting this error?

Cheers,

Mike

  • Have done a bit more digging on this.  Not sure I have the solution, but I can see a potential issue.

    In my hardware, I have two separate ADC pins I want to monitor.  One of them I want to be able to detect when it goes above or below a certain value, and I need to be monitoring this continuously so I don't miss an event.  The other I just want to take 32 samples, each sample at ~ 300usec interval, and I need to repeat this process once every ~ 3 seconds.

    I think the upper/lower limit feature is only something that can be achieved using the NRFX SAADC API's, whilst the second functionality I can actually do as I currently am, using the Zephyr API's.  But I'm guessing I can't mix/match the two.  It may be that the -11 error is mixed up in all of that.  If I ignore that error, I do actually get a valid ADC reading.

    I'm still interested to understand what that -11 error actually means, but think I need to redo my firmware so its using only the NRFX SAADC drivers, and hopefully the issue will resolve itself

    Mike

  • Hi,

    That might be the issue, mixing between the nrfx and Zephyr API for the same peripheral is never a wise decision, as the Zephyr API will use the nrfx drivers in the backend to communicate with the nRF peripherals. This can be an issue when using the same peripheral as the API might reconfigure the driver etc.

    Another issue might be that you're calling adc_read() from an interrupt context, which might block it from completing as it might be depending on other resources. Can you try to set a flag in the counter callback handler instead, and then move the adc_read() to the main context where you check the status of the flag?

    If this resolves the issue, then you know the it's somehow related to resources and interrupt context.

    regards

    Jared 

  • Hi Jared,

    Thanks for looking into this for me.  I've put this firmware on the back-burner for the moment, because ultimately I need the limit detect function, so am focusing on getting the NRFX_SAADC drivers working in my application.

    Seemed easy on paper, but isn't quite working out like that!

    Not wanting to go off-topic of this ticket too much, but the issue I am having is that the result I'm getting back doesn't seem to align with what I am expecting.

    I've set up my code to try and emulate a simple, non-blocking approach, as I just need to take an ADC sample at set times.

    In my saadc handler, I have this:

    switch (p_event->type)
        {
            case NRFX_SAADC_EVT_DONE:
                LOG_DBG("SAADC event: DONE");
    
                samples_number = p_event->data.done.size;
                LOG_DBG("samples = %d", samples_number);
                for (uint16_t i = 0; i < samples_number; i++)
                {
                    LOG_DBG("[Sample %d] value == %d",
                                  i, NRFX_SAADC_SAMPLE_GET(NRF_SAADC_RESOLUTION_12BIT, p_event->data.done.p_buffer, i));            }
    
                break;
    

    I'm testing on an nRF52-DK, and am connecting the appropriate analog input (in my case, AIN6) to VDD, which should be 3V), or GND (so, 0V).  But I seem to be getting a result of ~ 1500 for VDD and ~ -150 for GND.

    I've got the resolution set to 12 bit, so sold have expected results of 4096 and 0.

    Any ideas what I'm doing wrong here?

    Thanks and regards,

    Mike

  • Hi Mike,

    Can you try this simple sample and connect the 3 inputs on the DK to VDD, VDD and the last one to GND.

    Can you share the log when you have done that?

    It should look something like this:

    [00:00:00.264,678] <inf> NRFX_EXAMPLE: Starting nrfx_saadc simple non-blocking example.
    [00:00:00.273,437] <inf> NRFX_EXAMPLE: Single channel SAADC test.
    [00:00:00.280,334] <inf> NRFX_EXAMPLE: SAADC event: CALIBRATEDONE
    [00:00:00.280,364] <inf> NRFX_EXAMPLE: SAADC event: DONE
    [00:00:00.280,395] <inf> NRFX_EXAMPLE: [Sample 0] value == 255
    [00:00:00.299,621] <inf> NRFX_EXAMPLE: SAADC event: CALIBRATEDONE
    [00:00:00.299,682] <inf> NRFX_EXAMPLE: SAADC event: DONE
    [00:00:00.299,682] <inf> NRFX_EXAMPLE: [Sample 0] value == 255
    [00:00:00.318,939] <inf> NRFX_EXAMPLE: SAADC event: CALIBRATEDONE
    [00:00:00.319,000] <inf> NRFX_EXAMPLE: SAADC event: DONE
    [00:00:00.319,000] <inf> NRFX_EXAMPLE: [Sample 0] value == 255
    [00:00:00.338,256] <inf> NRFX_EXAMPLE: SAADC event: CALIBRATEDONE
    [00:00:00.338,317] <inf> NRFX_EXAMPLE: SAADC event: DONE
    [00:00:00.338,317] <inf> NRFX_EXAMPLE: [Sample 0] value == 255
    [00:00:00.372,863] <inf> NRFX_EXAMPLE: SAADC event: CALIBRATEDONE
    [00:00:00.372,894] <inf> NRFX_EXAMPLE: SAADC event: DONE
    [00:00:00.372,924] <inf> NRFX_EXAMPLE: [Sample 0] value == 255
    [00:00:00.407,409] <inf> NRFX_EXAMPLE: SAADC event: CALIBRATEDONE
    [00:00:00.407,470] <inf> NRFX_EXAMPLE: SAADC event: DONE
    [00:00:00.407,470] <inf> NRFX_EXAMPLE: [Sample 0] value == 255
    [00:00:00.441,986] <inf> NRFX_EXAMPLE: SAADC event: CALIBRATEDONE
    [00:00:00.442,047] <inf> NRFX_EXAMPLE: SAADC event: DONE
    [00:00:00.442,047] <inf> NRFX_EXAMPLE: [Sample 0] value == 255
    [00:00:00.476,562] <inf> NRFX_EXAMPLE: SAADC event: CALIBRATEDONE
    [00:00:00.476,593] <inf> NRFX_EXAMPLE: SAADC event: DONE
    [00:00:00.476,623] <inf> NRFX_EXAMPLE: [Sample 0] value == 255
    [00:00:00.495,727] <inf> NRFX_EXAMPLE: Multiple channels SAADC test.
    [00:00:00.502,868] <inf> NRFX_EXAMPLE: SAADC event: CALIBRATEDONE
    [00:00:00.502,960] <inf> NRFX_EXAMPLE: SAADC event: DONE
    [00:00:00.502,960] <inf> NRFX_EXAMPLE: [Sample 0] value == 255
    [00:00:00.502,990] <inf> NRFX_EXAMPLE: [Sample 1] value == 255
    [00:00:00.502,990] <inf> NRFX_EXAMPLE: [Sample 2] value == 0
    [00:00:00.534,942] <inf> NRFX_EXAMPLE: SAADC event: CALIBRATEDONE
    [00:00:00.535,003] <inf> NRFX_EXAMPLE: SAADC event: DONE
    [00:00:00.535,034] <inf> NRFX_EXAMPLE: [Sample 0] value == 255
    [00:00:00.535,034] <inf> NRFX_EXAMPLE: [Sample 1] value == 255
    [00:00:00.535,064] <inf> NRFX_EXAMPLE: [Sample 2] value == 0
    [00:00:00.567,016] <inf> NRFX_EXAMPLE: SAADC event: CALIBRATEDONE
    [00:00:00.567,077] <inf> NRFX_EXAMPLE: SAADC event: DONE
    [00:00:00.567,077] <inf> NRFX_EXAMPLE: [Sample 0] value == 255
    [00:00:00.567,108] <inf> NRFX_EXAMPLE: [Sample 1] value == 255
    [00:00:00.567,108] <inf> NRFX_EXAMPLE: [Sample 2] value == 0
    [00:00:00.599,090] <inf> NRFX_EXAMPLE: SAADC event: CALIBRATEDONE
    [00:00:00.599,151] <inf> NRFX_EXAMPLE: SAADC event: DONE
    [00:00:00.599,151] <inf> NRFX_EXAMPLE: [Sample 0] value == 255
    [00:00:00.599,182] <inf> NRFX_EXAMPLE: [Sample 1] value == 255
    [00:00:00.599,182] <inf> NRFX_EXAMPLE: [Sample 2] value == -1
    [00:00:00.631,225] <inf> NRFX_EXAMPLE: SAADC event: CALIBRATEDONE
    [00:00:00.631,286] <inf> NRFX_EXAMPLE: SAADC event: DONE
    [00:00:00.631,317] <inf> NRFX_EXAMPLE: [Sample 0] value == 255
    [00:00:00.631,317] <inf> NRFX_EXAMPLE: [Sample 1] value == 255
    [00:00:00.631,347] <inf> NRFX_EXAMPLE: [Sample 2] value == 0
    [00:00:00.663,269] <inf> NRFX_EXAMPLE: SAADC event: CALIBRATEDONE
    [00:00:00.663,360] <inf> NRFX_EXAMPLE: SAADC event: DONE
    [00:00:00.663,360] <inf> NRFX_EXAMPLE: [Sample 0] value == 255
    [00:00:00.663,391] <inf> NRFX_EXAMPLE: [Sample 1] value == 255
    [00:00:00.663,391] <inf> NRFX_EXAMPLE: [Sample 2] value == 0
    [00:00:00.695,312] <inf> NRFX_EXAMPLE: SAADC event: CALIBRATEDONE
    [00:00:00.695,404] <inf> NRFX_EXAMPLE: SAADC event: DONE
    [00:00:00.695,404] <inf> NRFX_EXAMPLE: [Sample 0] value == 255
    [00:00:00.695,434] <inf> NRFX_EXAMPLE: [Sample 1] value == 255
    [00:00:00.695,434] <inf> NRFX_EXAMPLE: [Sample 2] value == -1
    [00:00:00.727,478] <inf> NRFX_EXAMPLE: SAADC event: CALIBRATEDONE
    [00:00:00.727,539] <inf> NRFX_EXAMPLE: SAADC event: DONE
    [00:00:00.727,539] <inf> NRFX_EXAMPLE: [Sample 0] value == 255
    [00:00:00.727,569] <inf> NRFX_EXAMPLE: [Sample 1] value == 255
    [00:00:00.727,569] <inf> NRFX_EXAMPLE: [Sample 2] value == 1

    DK should be supplied from the J2 USB header. 

    regards

    Jared 

  • Hi Jared,

    OK, got that example working easily enough.  Had a play around with it, because there were a few things I wanted to do differently and it wasn't obvious from the sample how to achieve it (I'm more a hardware guy)

    Things I wanted to change were:

    • only needed two channels, linked to AIN2 and AIN6
    • needed the gain set to 1/6 (my signal can be between 0.2 - 1.8V)
    • I wanted 12 bit resolution

    Potted summary of the changes I made to achieve this (after a bit of trial and error) were:

    #define AIN2    2
    #define AIN6    6
    
    #define CH0_AIN ANALOG_INPUT_TO_SAADC_AIN(AIN2)
    #define CH1_AIN ANALOG_INPUT_TO_SAADC_AIN(AIN6)
    
    #define RESOLUTION NRF_SAADC_RESOLUTION_12BIT
    
    /**
     * @brief SAADC channel configuration for the single-ended mode with 10 us sample acquisition time.
     *
     * This configuration sets up single-ended SAADC channel with the following options:
     * - resistor ladder disabled
     * - gain: 1/6
     * - reference voltage: internal 0.6 V
     * - sample acquisition time: 10 us
     * - burst disabled
     *
     * @param[in] _pin_p Positive input analog pin.
     * @param[in] _index Channel index.
     *
     * @sa nrfx_saadc_channel_t
     */
    #define NRFX_SAADC_CHANNEL_SE_ACQ_10US(_pin_p, _index)        \
    {                                                       \
        .channel_config =                                                  \
        {                                                                  \
            .resistor_p = NRF_SAADC_RESISTOR_DISABLED,                     \
            .resistor_n = NRF_SAADC_RESISTOR_DISABLED,                     \
            .gain       = NRF_SAADC_GAIN1_6,                               \
            .reference  = NRF_SAADC_REFERENCE_INTERNAL,                    \
            .acq_time   = NRF_SAADC_ACQTIME_10US,                          \
            .mode       = NRF_SAADC_MODE_SINGLE_ENDED,                     \
            .burst      = NRF_SAADC_BURST_DISABLED,                        \
        },                                                                 \
        .pin_p          = (nrf_saadc_input_t)_pin_p,                       \
        .pin_n          = NRF_SAADC_INPUT_DISABLED,                        \
        .channel_index  = _index,                                          \
    }
    
    static const nrfx_saadc_channel_t m_multiple_channels[] =
    {
        NRFX_SAADC_CHANNEL_SE_ACQ_10US(CH0_AIN, 0),
        NRFX_SAADC_CHANNEL_SE_ACQ_10US(CH1_AIN, 1)
    };

    In my configuration function, I do the following prior to starting up my measurement:

    void surge_saadc_input_config()
    {
        nrfx_err_t err;
        uint32_t channels_mask = 0;
    
        IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_SAADC), IRQ_PRIO_LOWEST, nrfx_saadc_irq_handler, 0, 0);
    
    	//err = nrfx_saadc_init(DT_IRQ(DT_NODELABEL(adc), priority));
        err = nrfx_saadc_init(NRFX_SAADC_DEFAULT_CONFIG_IRQ_PRIORITY);
    
        if (err != NRFX_SUCCESS) {
    		LOG_ERR("nrfx_saadc_init error: %08x", err);
    		return;
    	}
     
        err = nrfx_saadc_channels_config(m_multiple_channels, CHANNEL_COUNT);
        if (err != NRFX_SUCCESS) {
    		LOG_ERR("nrfx_saadc_channels_config error: %08x", err);
    		return;
    	}
    
        channels_mask = nrfx_saadc_channels_configured_get();
        err = nrfx_saadc_simple_mode_set(channels_mask,
                                            RESOLUTION,
                                            NRF_SAADC_OVERSAMPLE_DISABLED,
                                            saadc_handler);
        if (err != NRFX_SUCCESS) {
            LOG_ERR("nrfx_saadc_simple_mode_set error: %08x", err);
            return;
        }
            
        err = nrfx_saadc_buffer_set(m_samples_buffer, CHANNEL_COUNT);
        if (err != NRFX_SUCCESS) {
            LOG_ERR("nrfx_saadc_buffer_set error: %08x", err);
            return;
        }
    
        err = nrfx_saadc_offset_calibrate(saadc_handler);
        if (err != NRFX_SUCCESS) {
            LOG_ERR("nrfx_saadc_offset_calibrate error: %08x", err);
            return;
        }
    
    }

    Then, once the calibration is done, I can start triggering ADC measurements:

    void check_surge_zero_calibration(struct test_status_data *data)
    {
        nrfx_err_t status;
        uint8_t sample_count = 0;
        uint32_t sum_saadc_vals = 0;
        uint32_t zero_offset_val = 0;
        uint16_t zero_val = 0;
    
        //LOG_DBG("Called check_surge_zero_calibration API");
    	surge_saadc_input_config();
        k_usleep(10);
        if (m_saadc_calibration && !m_saadc_ready) {
            m_saadc_calibration = false;
            while (sample_count < MAX_SAMPLES) {
                status = nrfx_saadc_mode_trigger();
                if (status != NRFX_SUCCESS) {
                    LOG_ERR("Unsuccessful conversion: %d", status);
                    return;
                } else {
                    if (m_saadc_ready) {
                        sum_saadc_vals = sum_saadc_vals + conversion_result;
                        sample_count++;
                        m_saadc_ready = false;
                    }
                }
                k_msleep(1);
            }
            zero_offset_val = (uint32_t) sum_saadc_vals/MAX_SAMPLES;
            zero_val = saadc_to_mV(zero_offset_val);
            LOG_DBG("zero_val = %dmV", zero_val);
    
            if ((SURGE_ZERO_MIN < zero_val) && (zero_val < SURGE_ZERO_MAX))
            {
                data->no_surge_status = true;
            } else {
                data->no_surge_status = false;
            }
            data->zero_surge_val = zero_val;
            m_current_state = STATE_RMS_VOLTAGE;
        } else {
            LOG_ERR("Failed to complete SAADC calibration");
        }
    }

    A couple of things I've noticed:

    1. If I enable the LOG messages in the saadc_handler, to show whenever the  NRFX_SAADC_EVT_DONE or NRFX_SAADC_EVT_CALIBRATEDONE events are received, I notice that I am getting a NRFX_SAADC_EVT_CALIBRATEDONE event for every NRFX_SAADC_EVT_DONE.  I can't understand why that would be.  Does the nrfx_saadc_mode_trigger() API automatically do a calibration each time?
    2. Trying to set the conversion time to anything less than 10usec seems to stop things working.  Again, not sure why.  At this stage, a 10usec conversion is OK, but I may need to drop this down to 5usec for the next part of the firmware development.

    The next thing I need to implement is once I've performed my "once every 3 seconds" ADC measurement, I need to set things up so that the SAADC is constantly sampling and will trigger an NRFX_SAADC_EVT_LIMIT event, so that I can capture any situtation where the ADC pin I'm monitoring goes outside my limits.  Not sure if I can do this with the simple_non_blocking setup I have - I believe I have to go to the advanced version and have dual buffers so I can constantly capture samples.  Does that sound right to you?


    Regards,

    Mike

     
Related