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

nRF52832 SAADC two channels at different sampling rate with burst & oversample

Hello,

I am sampling two analog signals at different sampling rate with burst and oversample enabled.

  • nRF52832 (Rigado BMD-350)
  • SDK 15.2
  • IDE - Segger Embedded Studio 4.12
  • SAADC Config
    • resolution 12bit
    • oversample 4x
    • interrupt priority = APP_IRQ_PRIORITY_LOW
    • low power mode = true
  • Channel 0 - Burst enable, sample period = 250ms
  • Channel 1 - Burst enable, sample period = 3 seconds

According to this post, https://devzone.nordicsemi.com/f/nordic-q-a/15882/sampling-two-analog-signals-using-saadc-at-two-different-rate#post-id-91845, for sampling two analog signals at different sampling rate which can be achieved by initialize and un-nitialize the channel.

Therefore I initialize and un-nitialize the channel 1 at the SAADC callback function. The first sampling result of channel 1 is OK, get the correct value. However when trigger the second sample, after initialize channel 1 and set the buffer with nrfx_saadc_buffer_convert again, the SAADC stop response, I cannot get any callback.

Below is my log and code, could anyone help me to solve this? Any idea why this is happening?

Here is the Log:

 app: ch1 uninit, ch0 cnt: 0, raw: 117
 app: ch1 uninit, ch0 cnt: 1, raw: 120
 app: ch1 uninit, ch0 cnt: 2, raw: 122
 app: ch1 uninit, ch0 cnt: 3, raw: 130
 app: ch1 uninit, ch0 cnt: 4, raw: 130
 app: ch1 uninit, ch0 cnt: 5, raw: 126
 app: ch1 uninit, ch0 cnt: 6, raw: 127
 app: ch1 uninit, ch0 cnt: 7, raw: 125
 app: ch1 uninit, ch0 cnt: 8, raw: 125
 app: ch1 uninit, ch0 cnt: 9, raw: 124
 app: ch1 uninit, ch0 cnt: 10, raw: 133
 app: ch1 init, ch0 cnt: 11, raw: 128                      <--- channel 1 init first time
 app: ch1 set uninit, ch0 cnt: 12, raw: 122                <--- channel 1 unit
 app:  ch1 result, ch1 cnt: 0, raw:3529, value: 4.1365571  <--- channel 1 get correct result
 app: ch1 uninit, ch0 cnt: 13, raw: 189
 app: ch1 uninit, ch0 cnt: 14, raw: 117
 app: ch1 uninit, ch0 cnt: 15, raw: 112
 app: ch1 uninit, ch0 cnt: 16, raw: 113
 app: ch1 uninit, ch0 cnt: 17, raw: 111
 app: ch1 uninit, ch0 cnt: 18, raw: 112
 app: ch1 uninit, ch0 cnt: 19, raw: 110
 app: ch1 uninit, ch0 cnt: 20, raw: 109
 app: ch1 uninit, ch0 cnt: 21, raw: 112
 app: ch1 uninit, ch0 cnt: 22, raw: 112
 app: ch1 init, ch0 cnt: 23, raw: 109                      <--- channel 1 init second time
<--- SAADC Stop response, no more callback

Here is my code:

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nrf.h"
//#include "nrf_drv_saadc.h"
#include "nrfx_saadc.h"
//#include "boards.h"
#include "app_timer.h"
#include "app_error.h"
#include "app_util_platform.h"
#include "nrf_pwr_mgmt.h"
#include "nrf_drv_power.h"
#include "nrf_drv_clock.h"
//#include "nrf_drv_rtc.h"
#include "nrfx_rtc.h"
#include "nrf_delay.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

#define RTC_FREQUENCY 32
#define RTC_CC_VALUE 8                            //Determines the RTC interrupt frequency and thereby the SAADC sampling frequency

// ADC chnnel1 timer for sampling
APP_TIMER_DEF(appTimerChannel1);

typedef enum _CHANNEL_STATES
{
    channelUninitialize,
    channelInitialize,
    channelResult,
    
} CHANNEL_STATES;


static const  nrfx_rtc_t           rtc = NRFX_RTC_INSTANCE(2); /**< Declaring an instance of nrf_drv_rtc for RTC2. */
static nrf_saadc_channel_config_t channel_config1 = NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);
static nrf_saadc_value_t       m_buffer_pool[2][2];

static uint32_t countCh0;
static uint32_t countCh1;

static CHANNEL_STATES stateCh1;

static char logChar[64];

static void lfclk_config(void)
{
    ret_code_t err_code = nrf_drv_clock_init();                        //Initialize the clock source specified in the nrf_drv_config.h file, i.e. the CLOCK_CONFIG_LF_SRC constant
    APP_ERROR_CHECK(err_code);
    nrf_drv_clock_lfclk_request(NULL);
}

static void rtc_handler(nrfx_rtc_int_type_t int_type)
{
    uint32_t err_code;
    
    if (int_type == NRFX_RTC_INT_COMPARE0)
    {
        //if(!m_saadc_initialized)
        //{
        //    saadc_init();                                              //Initialize the SAADC. In the case when SAADC_SAMPLES_IN_BUFFER > 1 then we only need to initialize the SAADC when the the buffer is empty.
        //}
        //m_saadc_initialized = true;                                    //Set SAADC as initialized
        
        nrfx_saadc_sample();                                        //Trigger the SAADC SAMPLE task
        
        err_code = nrfx_rtc_cc_set(&rtc, 0, RTC_CC_VALUE, true);       //Set RTC compare value. This needs to be done every time as the nrf_drv_rtc clears the compare register on every compare match
        APP_ERROR_CHECK(err_code);
        nrfx_rtc_counter_clear(&rtc);                               //Clear the RTC counter to start count from zero
    }
}


static void rtc_config(void)
{
    uint32_t err_code;
    
    //Initialize RTC instance
    nrfx_rtc_config_t rtc_config = NRFX_RTC_DEFAULT_CONFIG;
    rtc_config.prescaler = RTC_FREQ_TO_PRESCALER(RTC_FREQUENCY);
    err_code = nrfx_rtc_init(&rtc, &rtc_config, rtc_handler);              //Initialize the RTC with callback function rtc_handler. The rtc_handler must be implemented in this applicaiton. Passing NULL here for RTC configuration means that configuration will be taken from the sdk_config.h file.
    APP_ERROR_CHECK(err_code);
    
    err_code = nrfx_rtc_cc_set(&rtc, 0, RTC_CC_VALUE, true);           //Set RTC compare value to trigger interrupt. Configure the interrupt frequency by adjust RTC_CC_VALUE and RTC_FREQUENCY constant in top of main.c
    APP_ERROR_CHECK(err_code);
    
    //Power on RTC instance
    nrfx_rtc_enable(&rtc);                                          //Enable RTC
}

static void saadc_callback(nrfx_saadc_evt_t const * p_event)
{
    //NRF_LOG_INFO("SAADC callback\r\n");
    if (p_event->type == NRFX_SAADC_EVT_DONE)                                                        //Capture offset calibration complete event
    {
        ret_code_t err_code;
        
        switch (stateCh1) {
            case channelUninitialize:
            {
                err_code = nrfx_saadc_buffer_convert(p_event->data.done.p_buffer, 1);  //Set buffer so the SAADC can write to it again. This is either "buffer 1" or "buffer 2"
                APP_ERROR_CHECK(err_code);

                NRF_LOG_INFO("ch1 uninit, ch0 cnt: %i, raw: %i", countCh0, p_event->data.done.p_buffer[0]);
                countCh0++;

            } break;
               
            case channelInitialize:
            {
                // init channel 1
                err_code = nrfx_saadc_channel_init(1, &channel_config1);
                APP_ERROR_CHECK(err_code);

                // set buffer to 2 for both channels 1&2
                err_code = nrfx_saadc_buffer_convert(p_event->data.done.p_buffer, 2);  //Set buffer so the SAADC can write to it again. This is either "buffer 1" or "buffer 2"
                APP_ERROR_CHECK(err_code);

                NRF_LOG_INFO("ch1 init, ch0 cnt: %i, raw: %i", countCh0, p_event->data.done.p_buffer[0]);
                countCh0++;
                stateCh1 = channelResult;

            } break;
                
            case channelResult:
            {
                err_code = nrfx_saadc_channel_uninit(1);
                APP_ERROR_CHECK(err_code);
                
                // set buffer to 1 for channels 0
                err_code = nrfx_saadc_buffer_convert(p_event->data.done.p_buffer, 1);  //Set buffer so the SAADC can write to it again. This is either "buffer 1" or "buffer 2"
                APP_ERROR_CHECK(err_code);

                NRF_LOG_INFO("ch1 set uninit, ch0 cnt: %i, raw: %i", countCh0, p_event->data.done.p_buffer[0]);
                
                // for channel1 adc result
                // gain: 1/4, external voltage divider: 1/2, internal ref. 0.6,
                float ch1Value = (((float) p_event->data.done.p_buffer[1]) * 8.0f * 0.6f) / 4095.0f;
                
                sprintf(logChar, " ch1 result, ch1 cnt: %d, raw:%i, value: %.7f", countCh1, p_event->data.done.p_buffer[1], ch1Value);
                NRF_LOG_INFO("%s", logChar);

                countCh0++;
                countCh1++;
                stateCh1 = channelUninitialize;

            } break;
                
            default:
                stateCh1 = channelUninitialize;
                break;
        }
    }
}


static void saadc_init(void)
{
    ret_code_t err_code;
    
    //Configure SAADC
    nrfx_saadc_config_t saadc_config;
    saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT;                                 //Set SAADC resolution to 12-bit. This will make the SAADC output values from 0 (when input voltage is 0V) to 2^12=2048 (when input voltage is 3.6V for channel gain setting of 1/6).
    saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X;                                           //Set oversample to 4x. This will make the SAADC output a single averaged value when the SAMPLE task is triggered 4 times.
    saadc_config.interrupt_priority = APP_IRQ_PRIORITY_LOW;                               //Set SAADC interrupt to low priority.
    saadc_config.low_power_mode = true;
    
    //Initialize SAADC
    err_code = nrfx_saadc_init(&saadc_config, saadc_callback);                         //Initialize the SAADC with configuration and callback function. The application must then implement the saadc_callback function, which will be called when SAADC interrupt is triggered
    APP_ERROR_CHECK(err_code);
    
    //Configure SAADC channel 0
    nrf_saadc_channel_config_t channel_config0 = NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
    channel_config0.gain = NRF_SAADC_GAIN1;
    channel_config0.reference = NRF_SAADC_REFERENCE_VDD4;
    channel_config0.acq_time = NRF_SAADC_ACQTIME_10US;
    channel_config0.burst = NRF_SAADC_BURST_ENABLED;
    err_code = nrfx_saadc_channel_init(0, &channel_config0);
    APP_ERROR_CHECK(err_code);
    
    //nrf_saadc_channel_config_t channel_config1 = NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);
    channel_config1.gain = NRF_SAADC_GAIN1_4;
    channel_config1.reference = NRF_SAADC_REFERENCE_INTERNAL;      // internal ref.: 0.6V
    channel_config1.acq_time = NRF_SAADC_ACQTIME_15US;
    channel_config1.burst = NRF_SAADC_BURST_ENABLED;
    //err_code = nrfx_saadc_channel_init(1, &channel_config1);
    //APP_ERROR_CHECK(err_code);
    
    stateCh1 = channelUninitialize;

    err_code = nrfx_saadc_buffer_convert(m_buffer_pool[0],1);    //Set SAADC buffer 1. The SAADC will start to write to this buffer
    APP_ERROR_CHECK(err_code);
    
    //err_code = nrfx_saadc_buffer_convert(m_buffer_pool[1],1);    //Set SAADC buffer 2. The SAADC will write to this buffer when buffer 1 is full. This will give the applicaiton time to process data in buffer 1.
    //APP_ERROR_CHECK(err_code);
}

static void appTimerCallbackChannel1(void * p_context)
{
    // do nothing
    UNUSED_PARAMETER(p_context);
    
    if (stateCh1 == channelUninitialize)
    {
        stateCh1 = channelInitialize;
    }
    
    return;
}


static void app_timer_config(void)
{
    ret_code_t err_code;
    
    // init application timer
    err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);

    // Create timers for adc channel1 sampling
    err_code = app_timer_create(&appTimerChannel1, APP_TIMER_MODE_REPEATED, appTimerCallbackChannel1);
    APP_ERROR_CHECK(err_code);
    
    // channel1 sample every 3 second
    err_code = app_timer_start(appTimerChannel1, APP_TIMER_TICKS(3000), NULL);
    APP_ERROR_CHECK(err_code);

    return;
}


/**
 * @brief Function for main application entry.
 */
int main(void)
{
    ret_code_t err_code;
    
    // log init
    err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);
    NRF_LOG_DEFAULT_BACKENDS_INIT();
    
    //initializing power management.
    err_code = nrf_pwr_mgmt_init();
    APP_ERROR_CHECK(err_code);

    //Enabling the DCDC converter for lower current consumption
    NRF_POWER->DCDCEN = 1;
    
    NRF_LOG_INFO("Test \r\n");
    
    //Configure low frequency 32kHz clock
    lfclk_config();
    
    // init saadc
    countCh0 = 0;
    countCh1 = 0;
    saadc_init();
    
    // init app timer
    app_timer_config();
    
    //Configure RTC. The RTC will generate periodic interrupts. Requires 32kHz clock to operate.
    rtc_config();
    
    while(1)
    {
        if (NRF_LOG_PROCESS() == false)
        {
            nrf_pwr_mgmt_run();
        }
    }

}

Parents
  • Hello Kenneth,

    Thank you for your reply.

    I modified my test code to initialize and un-initialize the channel 1 at the main loop. However I get the same error, after re-init the channel 1 and set the buffer with nrfx_saadc_buffer_convert at the 2nd sample, the SAADC stop response and there is no more callback from the SAADC.

    Here is the log:

    app: ch1 uninit, ch0 cnt: 0, raw: 98
     app: ch1 uninit, ch0 cnt: 1, raw: 89
     app: ch1 uninit, ch0 cnt: 2, raw: 93
     app: ch1 uninit, ch0 cnt: 3, raw: 89
     app: ch1 uninit, ch0 cnt: 4, raw: 94
     app: ch1 uninit, ch0 cnt: 5, raw: 98
     app: ch1 uninit, ch0 cnt: 6, raw: 97
     app: ch1 uninit, ch0 cnt: 7, raw: 100
     app: ch1 uninit, ch0 cnt: 8, raw: 107
     app: ch1 uninit, ch0 cnt: 9, raw: 104
     app: ch1 will init, ch0 cnt: 10, raw: 107
     app: ch1 init at main                                        <---- ch1 init at main 1st time
     app: ch1 will uninit, ch0 cnt: 11, raw: 117
     app: ch1 result, ch1 cnt: 0, raw:3438, value: 4.0298900      <---- ch1 correct result
     app: ch1 uninit at main                                      <---- ch1 un-init at main 1st time
     app: ch1 uninit, ch0 cnt: 12, raw: 185
     app: ch1 uninit, ch0 cnt: 13, raw: 111
     app: ch1 uninit, ch0 cnt: 14, raw: 120
     app: ch1 uninit, ch0 cnt: 15, raw: 128
     app: ch1 uninit, ch0 cnt: 16, raw: 127
     app: ch1 uninit, ch0 cnt: 17, raw: 131
     app: ch1 uninit, ch0 cnt: 18, raw: 134
     app: ch1 uninit, ch0 cnt: 19, raw: 135
     app: ch1 uninit, ch0 cnt: 20, raw: 138
     app: ch1 uninit, ch0 cnt: 21, raw: 138
     app: ch1 will init, ch0 cnt: 22, raw: 142
     app: ch1 init at main                                        <---- ch1 init at main 2nd time                                                    <--- SAADC Stop response, no more callback


    Here is the modified test code to initialize and un-initialize the channel 1 at the main loop.
    #include <stdbool.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <string.h>
    #include "nrf.h"
    //#include "nrf_drv_saadc.h"
    #include "nrfx_saadc.h"
    //#include "boards.h"
    #include "app_timer.h"
    #include "app_error.h"
    #include "app_util_platform.h"
    #include "nrf_pwr_mgmt.h"
    #include "nrf_drv_power.h"
    #include "nrf_drv_clock.h"
    //#include "nrf_drv_rtc.h"
    #include "nrfx_rtc.h"
    #include "nrf_delay.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    #define RTC_FREQUENCY 32
    #define RTC_CC_VALUE 8                            //Determines the RTC interrupt frequency and thereby the SAADC sampling frequency
    
    // ADC chnnel1 timer for sampling
    APP_TIMER_DEF(appTimerChannel1);
    
    typedef enum _CHANNEL_STATES
    {
        channelUninitialized,
        channelWillInitialize,
        channelInitializeAtMain,
        channelInitialized,
        channelUninitializeAtMain,
        
    } CHANNEL_STATES;
    
    
    static const  nrfx_rtc_t           rtc = NRFX_RTC_INSTANCE(2); /**< Declaring an instance of nrf_drv_rtc for RTC2. */
    static nrf_saadc_channel_config_t channel_config0 = NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
    static nrf_saadc_channel_config_t channel_config1 = NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);
    static nrf_saadc_value_t       m_buffer_pool[2][2];
    
    static uint32_t countCh0;
    static uint32_t countCh1;
    
    static CHANNEL_STATES stateCh1;
    
    static char logChar[64];
    
    static void lfclk_config(void)
    {
        ret_code_t err_code = nrf_drv_clock_init();                        //Initialize the clock source specified in the nrf_drv_config.h file, i.e. the CLOCK_CONFIG_LF_SRC constant
        APP_ERROR_CHECK(err_code);
        nrf_drv_clock_lfclk_request(NULL);
    }
    
    static void rtc_handler(nrfx_rtc_int_type_t int_type)
    {
        uint32_t err_code;
        
        if (int_type == NRFX_RTC_INT_COMPARE0)
        {
            //if(!m_saadc_initialized)
            //{
            //    saadc_init();                                              //Initialize the SAADC. In the case when SAADC_SAMPLES_IN_BUFFER > 1 then we only need to initialize the SAADC when the the buffer is empty.
            //}
            //m_saadc_initialized = true;                                    //Set SAADC as initialized
            
            if ((stateCh1 != channelInitializeAtMain) &&
                (stateCh1 != channelUninitializeAtMain))
            {
                nrfx_saadc_sample();                                        //Trigger the SAADC SAMPLE task
            }
            else
            {
                NRF_LOG_INFO("stateCh1 at main: %i", stateCh1);
            }
            
            err_code = nrfx_rtc_cc_set(&rtc, 0, RTC_CC_VALUE, true);       //Set RTC compare value. This needs to be done every time as the nrf_drv_rtc clears the compare register on every compare match
            APP_ERROR_CHECK(err_code);
            nrfx_rtc_counter_clear(&rtc);                               //Clear the RTC counter to start count from zero
        }
    }
    
    
    static void rtc_config(void)
    {
        uint32_t err_code;
        
        //Initialize RTC instance
        nrfx_rtc_config_t rtc_config = NRFX_RTC_DEFAULT_CONFIG;
        rtc_config.prescaler = RTC_FREQ_TO_PRESCALER(RTC_FREQUENCY);
        err_code = nrfx_rtc_init(&rtc, &rtc_config, rtc_handler);              //Initialize the RTC with callback function rtc_handler. The rtc_handler must be implemented in this applicaiton. Passing NULL here for RTC configuration means that configuration will be taken from the sdk_config.h file.
        APP_ERROR_CHECK(err_code);
        
        err_code = nrfx_rtc_cc_set(&rtc, 0, RTC_CC_VALUE, true);           //Set RTC compare value to trigger interrupt. Configure the interrupt frequency by adjust RTC_CC_VALUE and RTC_FREQUENCY constant in top of main.c
        APP_ERROR_CHECK(err_code);
        
        //Power on RTC instance
        nrfx_rtc_enable(&rtc);                                          //Enable RTC
    }
    
    static void saadc_callback(nrfx_saadc_evt_t const * p_event)
    {
        //NRF_LOG_INFO("SAADC callback\r\n");
        if (p_event->type == NRFX_SAADC_EVT_DONE)                                                        //Capture offset calibration complete event
        {
            ret_code_t err_code;
            
            switch (stateCh1) {
                case channelUninitialized:
                {
                    err_code = nrfx_saadc_buffer_convert(p_event->data.done.p_buffer, 1);  //Set buffer so the SAADC can write to it again. This is either "buffer 1" or "buffer 2"
                    APP_ERROR_CHECK(err_code);
    
                    NRF_LOG_INFO("ch1 uninit, ch0 cnt: %i, raw: %i", countCh0, p_event->data.done.p_buffer[0]);
                    countCh0++;
    
                } break;
    
                case channelWillInitialize:
                {
                    // init channel 1
                    //err_code = nrfx_saadc_channel_init(1, &channel_config1);
                    //APP_ERROR_CHECK(err_code);
    
                    // set buffer to 2 for both channels 1&2
                    //err_code = nrfx_saadc_buffer_convert(p_event->data.done.p_buffer, 2);  //Set buffer so the SAADC can write to it again. This is either "buffer 1" or "buffer 2"
                    //APP_ERROR_CHECK(err_code);
    
                    NRF_LOG_INFO("ch1 will init, ch0 cnt: %i, raw: %i", countCh0, p_event->data.done.p_buffer[0]);
                    countCh0++;
                    stateCh1 = channelInitializeAtMain;
    
                } break;
    
                case channelInitialized:
                {
                    //err_code = nrfx_saadc_channel_uninit(1);
                    //APP_ERROR_CHECK(err_code);
    
                    // set buffer to 1 for channels 0
                    //err_code = nrfx_saadc_buffer_convert(p_event->data.done.p_buffer, 1);  //Set buffer so the SAADC can write to it again. This is either "buffer 1" or "buffer 2"
                    //APP_ERROR_CHECK(err_code);
    
                    NRF_LOG_INFO("ch1 will uninit, ch0 cnt: %i, raw: %i", countCh0, p_event->data.done.p_buffer[0]);
    
                    // for channel1 adc result
                    // gain: 1/4, external voltage divider: 1/2, internal ref. 0.6,
                    float ch1Value = (((float) p_event->data.done.p_buffer[1]) * 8.0f * 0.6f) / 4095.0f;
    
                    sprintf(logChar, "ch1 result, ch1 cnt: %d, raw:%i, value: %.7f", countCh1, p_event->data.done.p_buffer[1], ch1Value);
                    NRF_LOG_INFO("%s", logChar);
    
                    countCh0++;
                    countCh1++;
                    stateCh1 = channelUninitializeAtMain;
    
                } break;
    
                default:
                    NRF_LOG_INFO("stateCh1: %i", stateCh1);
                    break;
            }
        }
    }
    
    
    static void saadc_init(void)
    {
        ret_code_t err_code;
        
        //Configure SAADC
        nrfx_saadc_config_t saadc_config;
        saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT;                                 //Set SAADC resolution to 12-bit. This will make the SAADC output values from 0 (when input voltage is 0V) to 2^12=2048 (when input voltage is 3.6V for channel gain setting of 1/6).
        saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X;                                           //Set oversample to 4x. This will make the SAADC output a single averaged value when the SAMPLE task is triggered 4 times.
        saadc_config.interrupt_priority = APP_IRQ_PRIORITY_LOW;                               //Set SAADC interrupt to low priority.
        saadc_config.low_power_mode = true;
        
        //Initialize SAADC
        err_code = nrfx_saadc_init(&saadc_config, saadc_callback);                         //Initialize the SAADC with configuration and callback function. The application must then implement the saadc_callback function, which will be called when SAADC interrupt is triggered
        APP_ERROR_CHECK(err_code);
        
        //Configure SAADC channel 0
        //nrf_saadc_channel_config_t channel_config0 = NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
        channel_config0.gain = NRF_SAADC_GAIN1;
        channel_config0.reference = NRF_SAADC_REFERENCE_VDD4;
        channel_config0.acq_time = NRF_SAADC_ACQTIME_10US;
        channel_config0.burst = NRF_SAADC_BURST_ENABLED;
        err_code = nrfx_saadc_channel_init(0, &channel_config0);
        APP_ERROR_CHECK(err_code);
        
        //nrf_saadc_channel_config_t channel_config1 = NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);
        channel_config1.gain = NRF_SAADC_GAIN1_4;
        channel_config1.reference = NRF_SAADC_REFERENCE_INTERNAL;      // internal ref.: 0.6V
        channel_config1.acq_time = NRF_SAADC_ACQTIME_15US;
        channel_config1.burst = NRF_SAADC_BURST_ENABLED;
        //err_code = nrfx_saadc_channel_init(1, &channel_config1);
        //APP_ERROR_CHECK(err_code);
        
        stateCh1 = channelUninitialized;
    
        err_code = nrfx_saadc_buffer_convert(m_buffer_pool[0],2);    //Set SAADC buffer 1. The SAADC will start to write to this buffer
        APP_ERROR_CHECK(err_code);
        
        //err_code = nrfx_saadc_buffer_convert(m_buffer_pool[1],2);    //Set SAADC buffer 2. The SAADC will write to this buffer when buffer 1 is full. This will give the applicaiton time to process data in buffer 1.
        //APP_ERROR_CHECK(err_code);
    }
    
    static void appTimerCallbackChannel1(void * p_context)
    {
        // do nothing
        UNUSED_PARAMETER(p_context);
        
        if (stateCh1 == channelUninitialized)
        {
            stateCh1 = channelWillInitialize;
        }
        
        return;
    }
    
    
    static void app_timer_config(void)
    {
        ret_code_t err_code;
        
        // init application timer
        err_code = app_timer_init();
        APP_ERROR_CHECK(err_code);
    
        // Create timers for adc channel1 sampling
        err_code = app_timer_create(&appTimerChannel1, APP_TIMER_MODE_REPEATED, appTimerCallbackChannel1);
        APP_ERROR_CHECK(err_code);
        
        // channel1 sample every 3 second
        err_code = app_timer_start(appTimerChannel1, APP_TIMER_TICKS(3000), NULL);
        APP_ERROR_CHECK(err_code);
    
        return;
    }
    
    
    /**
     * @brief Function for main application entry.
     */
    int main(void)
    {
        ret_code_t err_code;
        
        // log init
        err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
        NRF_LOG_DEFAULT_BACKENDS_INIT();
        
        //initializing power management.
        err_code = nrf_pwr_mgmt_init();
        APP_ERROR_CHECK(err_code);
    
        //Enabling the DCDC converter for lower current consumption
        NRF_POWER->DCDCEN = 1;
        
        NRF_LOG_INFO("Test \r\n");
        
        //Configure low frequency 32kHz clock
        lfclk_config();
        
        // init saadc
        countCh0 = 0;
        countCh1 = 0;
        saadc_init();
        
        // init app timer
        app_timer_config();
        
        //Configure RTC. The RTC will generate periodic interrupts. Requires 32kHz clock to operate.
        rtc_config();
        
        while(1)
        {
            if (NRF_LOG_PROCESS() == false)
            {
                nrf_pwr_mgmt_run();
            }
            
            switch (stateCh1) {
                case channelInitializeAtMain:
                {
                    // uninit channel 0
                    //err_code = nrfx_saadc_channel_uninit(0);
                    //APP_ERROR_CHECK(err_code);
    
                    // reinit channel 0
                    //err_code = nrfx_saadc_channel_init(0, &channel_config0);
                    //APP_ERROR_CHECK(err_code);
    
                    // init channel 1
                    err_code = nrfx_saadc_channel_init(1, &channel_config1);
                    APP_ERROR_CHECK(err_code);
                    
                    // set buffer to 2 for both channels 1&2
                    err_code = nrfx_saadc_buffer_convert(m_buffer_pool[0], 2);  //Set buffer so the SAADC can write to it again. This is either "buffer 1" or "buffer 2"
                    APP_ERROR_CHECK(err_code);
                    
                    NRF_LOG_INFO("ch1 init at main");
                    stateCh1 = channelInitialized;
                    
                } break;
    
                case channelUninitializeAtMain:
                {
                    // uninit channel 0
                    //err_code = nrfx_saadc_channel_uninit(0);
                    //APP_ERROR_CHECK(err_code);
                    
                    // uninit channel 1
                    err_code = nrfx_saadc_channel_uninit(1);
                    APP_ERROR_CHECK(err_code);
    
                    // re-init channel 0
                    //err_code = nrfx_saadc_channel_init(0, &channel_config0);
                    //APP_ERROR_CHECK(err_code);
    
                    // set buffer to 1 for channels 0
                    err_code = nrfx_saadc_buffer_convert(m_buffer_pool[0], 1);  //Set buffer so the SAADC can write to it again. This is either "buffer 1" or "buffer 2"
                    APP_ERROR_CHECK(err_code);
    
                    NRF_LOG_INFO("ch1 uninit at main");
                    stateCh1 = channelUninitialized;
                    
                } break;
    
                default:
                    break;
            }
        }
    
    }
    
    

     

  • I don't have a better suggestion to try and fail a bit, and clean up your code. I would do all uninit() and init() in main, triggering of adc samples only when a channel is initialized, and not from the adc callback handler. Avoid mix non-blocking and blocking adc operations, as for instance the non-blocking may que the next operation while the current is on-going. 

  • Hi Kenneth,

    As you can see from my last test code, I already do all uninit() and init() in the main loop, not from the adc callback handler. Moreover I am using non-blocking operations only.

    So, the problem is not from my code? Then it should be the SDK or SAADC hardware issue. 

  • I can see the same behavior here, I will need to contact the designers and get their comment.

    Best regards,
    Kenneth

  • I can confirm that there is an issue with the hardware, so the specific configuration you are doing here is not possible in combination with init() and uninit() channels dynamically, we will look into create an errata or update the documentation to state that BURST = 1 shall not be used with scan. So in your case you will either need to sample both channels always or reconfigure channel 0 to sample different inputs at different times.

    Sorry for the time this has taken.

    Best regards,
    Kenneth

  • Hi Kenneth

    Thank you for confirming the issue. And I have a follow-up question.

    If I init both channels "always" to do the sampling, I can still use BURST = 1, right?

    According to this post, it should work. https://devzone.nordicsemi.com/f/nordic-q-a/20556/how-does-the-saadc-scan-mode-use-the-burst-1

Reply Children
Related