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

nrfx_saadc advanced mode oversampling

Hi all,

I am currently developing audio sampling on a custom board using the nrf52832. I'd like to sample at 20khz and am using the nrfxlib driver for this (https://github.com/zephyrproject-rtos/hal_nordic/blob/master/nrfx/drivers/include/nrfx_saadc.h).

I am running the ADC at approx. 20khz now and using the internal ADC timer and am now trying to do oversampling however I could not find any example code on how to do this.

Regular results are around 4000 (raw adc values) but when I add oversampling I get strange outliers (some around 7000 and some around 16000) that do not make any sense to me. Is my implementation correct or do you have any advice on what to do here?

static volatile nrf_saadc_value_t acoustic_buffer[1];

static volatile u32_t sample_counter = 0;
static volatile u32_t interrupts_triggered = 0;


void acoustic_interrupt(nrfx_saadc_evt_t const * p_event)
{
	/* Helper variables */
	static s32_t acoustic_window_ptp_prev = 0;
	static s32_t acoustic_window_ptp_diff = 0;

	s16_t acoustic_sample = 0;
	nrfx_err_t err;
	nrfx_saadc_evt_t saadc_event = *p_event;

	interrupts_triggered++;

	switch (saadc_event.type) {
		case NRFX_SAADC_EVT_DONE: {
			acoustic_sample = (s16_t) *(saadc_event.data.done.p_buffer);
			err = nrfx_saadc_buffer_set((nrf_saadc_value_t*) acoustic_buffer, sizeof(acoustic_buffer));
			if (NRFX_SUCCESS != err) {
				LOG_ERR("IRQ: %x", err);
			}
			/* Throw away garbage values */
			if (acoustic_sample > 5000) {
				return;
			}
			sample_counter++;
			break;
		}
		case NRFX_SAADC_EVT_BUF_REQ:
			return;
		case NRFX_SAADC_EVT_CALIBRATEDONE:
			LOG_INF("SAADC calibrated");
			return;
		case NRFX_SAADC_EVT_LIMIT:
		case NRFX_SAADC_EVT_READY:
		case NRFX_SAADC_EVT_FINISHED:
		default:
			LOG_WRN("Unexpected event received: %x", saadc_event.type);
			return;
	}

	if (acoustic_sample > 4000) {
		LOG_WRN("%i: %i / %i",sample_counter % acoustic_oversample_count, acoustic_sample, acoustic_sample_ac);
	}
}

  • Hi Edvin,

    I have been using the double buffering implementation quite succesfull, however, I am now trying to run the ISR on a lower priority level since it is conflicting with some other system interrupts (radio).

    Whenever I lower the priority my initial problem is popping up again: weird outliers. Do you have any idea what's the cause?

    Best regards,

    Mattia

  • Hi Edvin,

    I have been using the example https://github.com/NordicSemiconductor/nrfx/wiki/SAADC-Advanced-mode-with-IRQs

     but I am struggling t get it working when I want to stop and continue the conversion at some point.

    my init funcition is the same, with some difference based on my application, 500 samples instead of 1000 and my sampling freq is 10kHz:

    static void adc_init(void) {
        ret_code_t err_code;
        nrfx_saadc_channel_t tuner_ch   = NRFX_SAADC_DEFAULT_CHANNEL_SE(GUITAR_IN_ADC_CH, 0);
        nrfx_saadc_adv_config_t adc_cfg = {
            .oversampling      = NRF_SAADC_OVERSAMPLE_DISABLED, // Disable oversample.
            .burst             = false,                         // Disable burst.
            .internal_timer_cc = 16000000UL/ADC_SAMPLE_RATE,    // Sample at 10kHz.
            .start_on_end      = false,                         // Latch next buffer at END event in interrupt.
        };
        err_code = nrfx_saadc_init(NRFX_SAADC_CONFIG_IRQ_PRIORITY);
        APP_ERROR_CHECK(err_code);
        err_code = nrfx_saadc_channels_config(&tuner_ch, 1);
        APP_ERROR_CHECK(err_code);
        err_code = nrfx_saadc_advanced_mode_set((1<<0), NRF_SAADC_RESOLUTION_10BIT, &adc_cfg, adc_evt_handler);
        APP_ERROR_CHECK(err_code);
        err_code = nrfx_saadc_buffer_set(adc_buf[buf_to_load % 2], SAMPLES_NUMBER);
        APP_ERROR_CHECK(err_code);
        buf_to_load++;
    }

    and whenever I want to get a set of samples i do:

     my event handler looks like this, I modified it a bit for the sake of my application.

    static void adc_evt_handler(nrfx_saadc_evt_t const * p_event) {
        switch (p_event->type) {
            case NRFX_SAADC_EVT_DONE: {
                // Buffer with data is available here: p_event->data.done.p_buffer
                adc_samples = p_event->data.done.size;
                for (uint32_t i = 0; i < adc_samples; i++) {
                    guitar_samples[i] = p_event->data.done.p_buffer[i];
                }
                eventq_add(EVENT_TUNER_ADC_READY, 0, NULL);
            }
            break;
            case NRFX_SAADC_EVT_BUF_REQ:
                nrfx_saadc_buffer_set(adc_buf[buf_to_load % 2], SAMPLES_NUMBER);
                buf_to_load++;
                break;
            default:
                break;
        }
    }

    The first time I get the samples correctly. From what I understand since I configured the adc

    with :

            .start_on_end      = false,                         // Latch next buffer at END event in interrupt.
    

    Once the event :

    NRFX_SAADC_EVT_DONE
    is published ADC block wont convert anymore.
    If call again
    ret_code_t err_code = nrfx_saadc_mode_trigger();
    APP_ERROR_CHECK(err_code);
    error_code is 8.
    What is the proper way of utilizing the adc to start stop samples on demand without having the ADC running non_stop ?
Related