adc_read() returning -EAGAIN

Hi,

I am getting started with the 52840DK board and v2.5.0 of the nRF SDK. I have successfully run ADC and Timer examples, but am running into an issue trying to combine the two so that the ADC reads, on P0.02(AIN0) and P0.03(AIN1), occur within the timer handler. When moving the adc_read() into the timer handler it is returning error -11 (-EAGAIN), which is not a valid return value according to the documentation I have found. I'd appreciate any insight into what might be going wrong. Some pertinent files attached.

/*
 * Copyright (c) 2020 Libre Solar Technologies GmbH
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>

#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/util.h>
#include <nrfx_timer.h>

#if !DT_NODE_EXISTS(DT_PATH(zephyr_user)) || \
	!DT_NODE_HAS_PROP(DT_PATH(zephyr_user), io_channels)
#error "No suitable devicetree overlay specified"
#endif

#define DT_SPEC_AND_COMMA(node_id, prop, idx) \
	ADC_DT_SPEC_GET_BY_IDX(node_id, idx),

/* Data of ADC io-channels specified in devicetree. */
static const struct adc_dt_spec adc_channels[] = {
	DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels,
			     DT_SPEC_AND_COMMA)
};

typedef struct {
	uint16_t mic_lvl_raw;
	uint16_t out_lvl_raw;
	uint8_t mic_lvl;
	uint8_t out_lvl;
	bool mic_lvl_changed_flag;
	bool out_lvl_changed_flag;
} adc_read_level_data_t;

/** @brief Symbol specifying timer instance to be used. */
#define TIMER_INST_IDX 0

/** @brief Symbol specifying time in milliseconds to wait for handler execution. */
#define TIME_TO_WAIT_MS 5000UL

/**
 * @brief Function for handling TIMER driver events.
 *
 * @param[in] event_type Timer event.
 * @param[in] p_context  General purpose parameter set during initialization of
 *                       the timer. This parameter can be used to pass
 *                       additional information to the handler function, for
 *                       example, the timer ID.
 */
static void timer_handler(nrf_timer_event_t event_type, void * p_context)
{
	int err;
	uint16_t buf; // raw read value from ADC
	
	struct adc_sequence sequence = {
		.buffer = &buf,
		/* buffer size in bytes, not number of samples */
		.buffer_size = sizeof(buf),
	};

//	printk("Timer handler called");
	err = 0;

    if(event_type == NRF_TIMER_EVENT_COMPARE0)
    {
		adc_read_level_data_t *levels = p_context;

		for (size_t i = 0U; i < ARRAY_SIZE(adc_channels); i++) {
			(void)adc_sequence_init_dt(&adc_channels[i], &sequence);
//			err = adc_sequence_init_dt(&adc_channels[i], &sequence);
//			printk("Return value of adc_sequence_init() is: %d\n\r", err);

			err = adc_read(adc_channels[i].dev, &sequence);
			if (err < 0) {
				printk("Could not read ADC channel %d, error number (%d)\n", i, err);
				continue;
			}

			/* limit raw value ADC reading to positive values*/
			if ((int16_t) buf < 0)
				buf = 0;
			
			/* Convert raw ADC read value to 6-bit mic/out level based on current channel read
			only update mic_lvl or out_lvl if the 6-bit value has changed. */
//			if (i){
//				level_settings.mic_lvl_raw = buf;
//				if (level_settings.mic_lvl != (uint8_t)(level_settings.mic_lvl_raw >> 6)) {
//					level_settings.mic_lvl = (uint8_t)(level_settings.mic_lvl_raw >> 6);
//					level_settings.mic_lvl_changed_flag = true;
//				}
//				/* print the raw and converted values for the current adc channel*/
//				printk("%u : %u\n", level_settings.mic_lvl_raw, level_settings.mic_lvl);
//			}
			if (i){
				levels->mic_lvl_raw = buf;
				if (levels->mic_lvl != (uint8_t)(levels->mic_lvl_raw >> 6)) {
					levels->mic_lvl = (uint8_t)(levels->mic_lvl_raw >> 6);
					levels->mic_lvl_changed_flag = true;
				}
				/* print the raw and converted values for the current adc channel*/
//				printk("%u : %u\n", level_settings.mic_lvl_raw, level_settings.mic_lvl);
			}
//			else {
//				level_settings.out_lvl_raw = buf;
//				if (level_settings.out_lvl != (uint8_t)(level_settings.out_lvl_raw >> 6)) {
//					level_settings.out_lvl = (uint8_t)(level_settings.out_lvl_raw >> 6);
//					level_settings.out_lvl_changed_flag = true;
//				}
				/* print the raw value for the current adc channel*/
//				printk("%u : %u\n", level_settings.out_lvl_raw, level_settings.out_lvl);
//			}
			else {
				levels->out_lvl_raw = buf;
				if (levels->out_lvl != (uint8_t)(levels->out_lvl_raw >> 6)) {
					levels->out_lvl = (uint8_t)(levels->out_lvl_raw >> 6);
					levels->out_lvl_changed_flag = true;
				}
				/* print the raw value for the current adc channel*/
//				printk("%u : %u\n", level_settings.out_lvl_raw, level_settings.out_lvl);
			}

//			if (mic_max_min_raw_val < level_settings.mic_lvl_raw)
//				mic_max_min_raw_val = level_settings.mic_lvl_raw;
//			if (mic_max_min_conv_val < level_settings.mic_lvl)
//				mic_max_min_conv_val = level_settings.mic_lvl;
//			printk("The largest minimum raw mic level value from the ADC is %u\n", mic_max_min_raw_val);
//			printk("The largest minimum converted mic level value from the ADC is %u\n", mic_max_min_conv_val);


//			if (out_max_min_raw_val < level_settings.out_lvl_raw)
//				out_max_min_raw_val = level_settings.out_lvl_raw;
//			if (out_max_min_conv_val < level_settings.out_lvl)
//				out_max_min_conv_val = level_settings.out_lvl;
//			printk("The largest minimum raw out level value from the ADC is %u\n", out_max_min_raw_val);
//			printk("The largest minimum converted out level value from the ADC is %u\n", out_max_min_conv_val);
		}


//		printk("Channel %d\n\r Channel %d\n\r", levels->channels[0].channel_id, levels->channels[1].channel_id);
//		printk("%s\n\r", p_msg);

    }
}

int main(void)
{
	/* ADC variable declarations */
	int err;
	uint32_t mic_count = 0, out_count = 0;
//	volatile uint16_t buf; // raw read value from ADC
	
//	struct adc_sequence sequence = {
//		.buffer = &buf,
		/* buffer size in bytes, not number of samples */
//		.buffer_size = sizeof(buf),
//	};

	volatile adc_read_level_data_t level_settings;

	/* Timer variable declarations */
	nrfx_err_t status;

	nrfx_timer_t timer_inst = NRFX_TIMER_INSTANCE(TIMER_INST_IDX);
	uint32_t base_frequency = NRF_TIMER_BASE_FREQUENCY_GET(timer_inst.p_reg);
	nrfx_timer_config_t config = NRFX_TIMER_DEFAULT_CONFIG(base_frequency);
	config.bit_width = NRF_TIMER_BIT_WIDTH_32;
	config.p_context = (void *) &level_settings;

	/* Configure ADC channels individually prior to sampling. */
	for (size_t i = 0U; i < ARRAY_SIZE(adc_channels); i++) {
		if (!adc_is_ready_dt(&adc_channels[i])) {
			printk("ADC controller device %s not ready\n", adc_channels[i].dev->name);
			return 0;
		}

		err = adc_channel_setup_dt(&adc_channels[i]);
		if (err < 0) {
			printk("Could not setup channel #%d (%d)\n", i, err);
			return 0;
		}
	}

	/* Configure Timer prior to use */
	status = nrfx_timer_init(&timer_inst, &config, timer_handler);

#if defined(__ZEPHYR__)
    IRQ_DIRECT_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_TIMER_INST_GET(TIMER_INST_IDX)), IRQ_PRIO_LOWEST,
                       NRFX_TIMER_INST_HANDLER_GET(TIMER_INST_IDX), 0);
#endif

	nrfx_timer_clear(&timer_inst);

    /* Creating variable desired_ticks to store the output of nrfx_timer_ms_to_ticks function */
    uint32_t desired_ticks = nrfx_timer_ms_to_ticks(&timer_inst, TIME_TO_WAIT_MS);

    /*
     * Setting the timer channel NRF_TIMER_CC_CHANNEL0 in the extended compare mode to stop the timer and
     * trigger an interrupt if internal counter register is equal to desired_ticks.
     */
    nrfx_timer_extended_compare(&timer_inst, NRF_TIMER_CC_CHANNEL0, desired_ticks,
                                NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);

    nrfx_timer_enable(&timer_inst);

	while (1) {
		if (level_settings.mic_lvl_changed_flag){
			printk("ADC mic reading[%u]: %u\n", mic_count++, level_settings.mic_lvl);
			level_settings.mic_lvl_changed_flag = false;
		}

		if (level_settings.out_lvl_changed_flag){
			printk("ADC out reading[%u]: %u\n", out_count++, level_settings.out_lvl);
			level_settings.out_lvl_changed_flag = false;
		}



//		printk("ADC reading[%u]:\n", count++);
//		for (size_t i = 0U; i < ARRAY_SIZE(adc_channels); i++) {
//			uint16_t mic_max_min_raw_val = 0, out_max_min_raw_val = 0;
//			uint8_t mic_max_min_conv_val = 0, out_max_min_conv_val = 0;

//			printk("- %s, channel %d: ",
//			       adc_channels[i].dev->name,
//			       adc_channels[i].channel_id);

//			(void)adc_sequence_init_dt(&adc_channels[i], &sequence);

//			err = adc_read(adc_channels[i].dev, &sequence);
//			if (err < 0) {
//				printk("Could not read (%d)\n", err);
//				continue;
//			}

			/* limit raw value ADC reading to positive values*/
//			if ((int16_t) buf < 0)
//				buf = 0;
			
			/* Convert raw ADC read value to 6-bit mic/out level based on current channel read
			only update mic_lvl or out_lvl if the 6-bit value has changed. */
//			if (i){
//				level_settings.mic_lvl_raw = buf;
//				if (level_settings.mic_lvl != (uint8_t)(level_settings.mic_lvl_raw >> 6)) {
//					level_settings.mic_lvl = (uint8_t)(level_settings.mic_lvl_raw >> 6);
//					level_settings.mic_lvl_changed_flag = true;
//				}
				/* print the raw and converted values for the current adc channel*/
//				printk("%u : %u\n", level_settings.mic_lvl_raw, level_settings.mic_lvl);
//			}
//			else {
//				level_settings.out_lvl_raw = buf;
//				if (level_settings.out_lvl != (uint8_t)(level_settings.out_lvl_raw >> 6)) {
//					level_settings.out_lvl = (uint8_t)(level_settings.out_lvl_raw >> 6);
//					level_settings.out_lvl_changed_flag = true;
//				}
				/* print the raw value for the current adc channel*/
//				printk("%u : %u\n", level_settings.out_lvl_raw, level_settings.out_lvl);
//			}

//			if (mic_max_min_raw_val < level_settings.mic_lvl_raw)
//				mic_max_min_raw_val = level_settings.mic_lvl_raw;
//			if (mic_max_min_conv_val < level_settings.mic_lvl)
//				mic_max_min_conv_val = level_settings.mic_lvl;
//			printk("The largest minimum raw mic level value from the ADC is %u\n", mic_max_min_raw_val);
//			printk("The largest minimum converted mic level value from the ADC is %u\n", mic_max_min_conv_val);


//			if (out_max_min_raw_val < level_settings.out_lvl_raw)
//				out_max_min_raw_val = level_settings.out_lvl_raw;
//			if (out_max_min_conv_val < level_settings.out_lvl)
//				out_max_min_conv_val = level_settings.out_lvl;
//			printk("The largest minimum raw out level value from the ADC is %u\n", out_max_min_raw_val);
//			printk("The largest minimum converted out level value from the ADC is %u\n", out_max_min_conv_val);
//		}

//		k_sleep(K_MSEC(2000));
	}
	return 0;
}
7217.prj.conf0383.nrf52840dk_nrf52840.overlay

  • Hi,

    Do the function call work correctly if called from main() or another non-interrupt context? The issue might be that the timer handler runs in interrupt context, and you are calling a blocking function that depends on other interrupts in lower layers.

    Have you debugged/stepped the application to see where the error code comes from? My guess would be that it comes from k_sem_take() in adc_context_wait_for_completion() because it timed out waiting for the semaphore from the SAADC interrupt handler.

    Do you need to use a TIMER peripheral with interrupts to trigger the sampling, or could you use a separate Thread to trigger this? If you are only sampling every 5 seconds, this should likely be accurate enough.

    Best regards,
    Jørgen

Related