Hi,
I'm trying to implement an application that acquires data from two channels of the SAADC in "parallel". I'm currently using the nRF52 DK and nRF5 SDK v17.1.0. After reading several post on DevZone and some examples on Github, I developed the code on the bottom of this post. In the reference posts that I read, it was stated several times that I have to uninit() one ADC channel for being able to switch to the other ADC channel. So in my code, after receiving the NRF_DRV_SAADC_EVT_DONE, I convert the buffer before trying to uninit() ADC channel 0 and call init() on channel 1 and vice versa. However, I always receive NRF_ERROR_BUDY, when trying to uninit().
I would be grateful for some hints, if I'm on the right track or if there is a major misunderstanding of how the ADC works.
Here is my code:
#include "saadc_itf.h" #include <stdbool.h> #include <stdint.h> #include "sdk_errors.h" #include "nrf_drv_saadc.h" #include "nrf_drv_ppi.h" #include "nrf_drv_timer.h" #include "nrf_queue.h" #include "nrf_delay.h" #include "nrf_log.h" #include "nrf.h" #include "app_util_platform.h" #include "app_error.h" #include "nrf_log_ctrl.h" #include "nrf_log_default_backends.h" #define SAMPLES_IN_BUFFER 64 #define SAMPLE_RATE 16000 static const nrf_drv_saadc_config_t saadc_config = { .resolution = NRF_SAADC_RESOLUTION_12BIT, .oversample = NRF_SAADC_OVERSAMPLE_DISABLED, .interrupt_priority = 6, .low_power_mode = false }; static const nrf_drv_timer_t m_timer = NRF_DRV_TIMER_INSTANCE(3); static nrf_ppi_channel_t m_ppi_channel; uint16_t CC_VALUE = 1000; // sampling frequency = 16 MHz / CC_VALUE //static nrf_saadc_value_t m_buffer_pool[2][SAMPLES_IN_BUFFER]; static nrf_saadc_value_t m_buffer_ch0[SAMPLES_IN_BUFFER]; static nrf_saadc_value_t m_buffer_ch1[SAMPLES_IN_BUFFER]; static uint32_t m_adc_evt_counter; static uint8_t test_array[128] = {0}; static uint8_t m_adc_channel_enabled; static nrf_saadc_channel_config_t channel_0_config; static nrf_saadc_channel_config_t channel_1_config; NRF_QUEUE_DEF(uint8_t, m_queue0, 5120, NRF_QUEUE_MODE_NO_OVERFLOW); NRF_QUEUE_DEF(uint8_t, m_queue1, 5120, NRF_QUEUE_MODE_NO_OVERFLOW); static nrf_ppi_channel_t m_ppi_channel; static uint32_t m_sample_rate_us = (1000000 / SAMPLE_RATE); uint32_t saadc_get_data(uint8_t * buffer, uint8_t buffer_len, uint8_t channel) { uint32_t err_code; if (channel == 0) { NRF_LOG_INFO("Current queue utilization: %d", ((int) nrf_queue_utilization_get(&m_queue))); //? use a different buffer size to match the BLE characteristic length err_code = nrf_queue_read(&m_queue0, buffer, (size_t) buffer_len); if (err_code == NRF_SUCCESS) { return NRF_SUCCESS; } else if (err_code == NRF_ERROR_NOT_FOUND) { NRF_LOG_DEBUG("Not enough data in queue."); NRF_LOG_INFO("Current queue utilization: %d", ((int) nrf_queue_utilization_get(&m_queue))); } else if (err_code == NRF_ERROR_BUSY) { NRF_LOG_DEBUG("Queue 0 busy"); } else { NRF_LOG_DEBUG("reading from queue 0 returned error."); APP_ERROR_CHECK(err_code); } return err_code; } else if (channel == 1) { NRF_LOG_INFO("Current queue utilization: %d", ((int) nrf_queue_utilization_get(&m_queue))); //? use a different buffer size to match the BLE characteristic length err_code = nrf_queue_read(&m_queue1, buffer, (size_t) buffer_len); if (err_code == NRF_SUCCESS) { return NRF_SUCCESS; } else if (err_code == NRF_ERROR_NOT_FOUND) { NRF_LOG_DEBUG("Not enough data in queue."); NRF_LOG_INFO("Current queue utilization: %d", ((int) nrf_queue_utilization_get(&m_queue))); } else if (err_code == NRF_ERROR_BUSY) { NRF_LOG_DEBUG("Queue 1 busy"); } else { NRF_LOG_DEBUG("reading from queue 1 returned error."); APP_ERROR_CHECK(err_code); } return err_code; } } uint32_t get_queue_utilization(uint8_t channel) { if (channel == 0) { uint32_t util = (int) nrf_queue_utilization_get(&m_queue0); return util; } else if (channel == 1) { uint32_t util = (int) nrf_queue_utilization_get(&m_queue1); return util; } } void saadc_callback(nrf_drv_saadc_evt_t const * p_event) { if (p_event->type == NRF_DRV_SAADC_EVT_DONE) { ret_code_t err_code; err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER); APP_ERROR_CHECK(err_code); if (m_adc_channel_enabled == 0) { err_code = nrf_queue_write(&m_queue0, p_event->data.done.p_buffer, (SAMPLES_IN_BUFFER * 2)); if (err_code == NRF_ERROR_NO_MEM) { // queue full NRF_LOG_DEBUG("QUEUE FULL!"); nrf_queue_reset(&m_queue0); } else if (err_code != NRF_SUCCESS) { APP_ERROR_CHECK(err_code); } } else if (m_adc_channel_enabled == 1) { err_code = nrf_queue_write(&m_queue1, p_event->data.done.p_buffer, (SAMPLES_IN_BUFFER * 2)); if (err_code == NRF_ERROR_NO_MEM) { // queue full NRF_LOG_DEBUG("QUEUE FULL!"); nrf_queue_reset(&m_queue1); } else if (err_code != NRF_SUCCESS) { APP_ERROR_CHECK(err_code); } } else { NRF_LOG_ERROR("Invalid ADC channel enabled: %d", m_adc_channel_enabled); } //uninit current channel err_code = nrf_drv_saadc_channel_uninit(m_adc_channel_enabled); APP_ERROR_CHECK(err_code); // switch channel m_adc_channel_enabled = 1 - m_adc_channel_enabled; // init other channel if (m_adc_channel_enabled == 0) { err_code = nrf_drv_saadc_channel_init(m_adc_channel_enabled, &channel_0_config); APP_ERROR_CHECK(err_code); } else if (m_adc_channel_enabled == 1) { err_code = nrf_drv_saadc_channel_init(m_adc_channel_enabled, &channel_1_config); APP_ERROR_CHECK(err_code); } else { NRF_LOG_ERROR("Invalid ADC channel selected: %d", m_adc_channel_enabled); } m_adc_evt_counter++; } } void timer_handler(nrf_timer_event_t event_type, void* p_context) { //NRF_LOG_DEBUG("TIMER HANDLER CALLED."); } uint32_t saadc_init(void) { ret_code_t err_code; err_code = nrf_drv_saadc_init(&saadc_config, saadc_callback); APP_ERROR_CHECK(err_code); channel_0_config.mode = NRF_SAADC_MODE_SINGLE_ENDED; channel_0_config.resistor_p = NRF_SAADC_RESISTOR_DISABLED; channel_0_config.resistor_n = NRF_SAADC_RESISTOR_DISABLED; channel_0_config.reference = NRF_SAADC_REFERENCE_INTERNAL; channel_0_config.acq_time = NRF_SAADC_ACQTIME_40US; channel_0_config.burst = NRF_SAADC_BURST_DISABLED; channel_0_config.pin_p = (nrf_saadc_input_t)(NRF_SAADC_INPUT_AIN1); channel_0_config.pin_n = NRF_SAADC_INPUT_DISABLED; channel_0_config.gain = NRF_SAADC_GAIN1_6; channel_1_config.mode = NRF_SAADC_MODE_SINGLE_ENDED; channel_1_config.resistor_p = NRF_SAADC_RESISTOR_DISABLED; channel_1_config.resistor_n = NRF_SAADC_RESISTOR_DISABLED; channel_1_config.reference = NRF_SAADC_REFERENCE_INTERNAL; channel_1_config.acq_time = NRF_SAADC_ACQTIME_40US; channel_1_config.burst = NRF_SAADC_BURST_DISABLED; channel_1_config.pin_p = (nrf_saadc_input_t)(NRF_SAADC_INPUT_AIN2); channel_1_config.pin_n = NRF_SAADC_INPUT_DISABLED; channel_1_config.gain = NRF_SAADC_GAIN4; m_adc_channel_enabled = 0; err_code = nrf_drv_saadc_channel_init(m_adc_channel_enabled, &channel_0_config); APP_ERROR_CHECK(err_code); err_code = nrf_drv_saadc_buffer_convert(m_buffer_ch0, SAMPLES_IN_BUFFER); APP_ERROR_CHECK(err_code); err_code = nrf_drv_saadc_buffer_convert(m_buffer_ch1, SAMPLES_IN_BUFFER); APP_ERROR_CHECK(err_code); return err_code; } void saadc_timer_init(void) { uint32_t time_us = m_sample_rate_us; uint32_t time_ticks; nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG; ret_code_t err_code = nrf_drv_timer_init(&m_timer, &timer_cfg, timer_handler); APP_ERROR_CHECK(err_code); time_ticks = nrf_drv_timer_us_to_ticks(&m_timer, time_us); nrf_drv_timer_extended_compare( &m_timer, NRF_TIMER_CC_CHANNEL0, time_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true); } void saadc_ppi_init(void) { ret_code_t err_code = nrf_drv_ppi_init(); APP_ERROR_CHECK(err_code); err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel); APP_ERROR_CHECK(err_code); uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer, NRF_TIMER_CC_CHANNEL0); uint32_t saadc_task_addr = nrf_drv_saadc_sample_task_get(); err_code = nrf_drv_ppi_channel_assign(m_ppi_channel, timer_compare_event_addr, saadc_task_addr); APP_ERROR_CHECK(err_code); err_code = nrf_drv_ppi_channel_enable(m_ppi_channel); APP_ERROR_CHECK(err_code); } void saadc_sampling_start(void) { nrf_drv_timer_enable(&m_timer); // Enable the timer to start sampling NRF_LOG_INFO("SAMPLING STARTED"); } void saadc_sampling_stop(void) { nrf_drv_timer_disable(&m_timer); // Disable the timer to stop sampling NRF_LOG_INFO("SAMPLING STOPPED"); } int main(void) { bool erase_bonds; uint32_t err_code; uint8_t current_channel = 0; // Initialize. log_init(); timers_init(); buttons_leds_init(&erase_bonds); power_management_init(); state_machine_init(&state_indication); // Initialize SAADC err_code = saadc_init(); saadc_timer_init(); saadc_ppi_init(); if (err_code == NRF_SUCCESS) { NRF_LOG_INFO("SAADC successfully initialized."); } else { NRF_LOG_INFO("Error initializing SAADC."); } saadc_sampling_start(); for (;;) { err_code = saadc_get_data(m_array, len_m_array, current_channel); current_channel = 1 - current_channel; if (err_code == NRF_SUCCESS) { // do something with the data } idle_state_handle(); NRF_LOG_FLUSH(); } }