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();
        }
    }

}

  • I forgot to mention, for enable the oversample for two channels, I comment out Line 293 & 294 at nrfx_saadc_channel_init of nrfx_saadc.c.

    Line 293 & 294

        // Oversampling can be used only with one channel.

        //NRFX_ASSERT((nrf_saadc_oversample_get() == NRF_SAADC_OVERSAMPLE_DISABLED) ||

        //            (m_cb.active_channels == 0));

  • Hi,

    If I understand you correctly you are trying to execute a combination that is not supported in hardware. From the documentation you can find:

    http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.nrf52832.ps.v1.1/saadc.html?cp=2_1_0_36_4#saadc_operationmodes 

    "Scan mode and oversampling cannot be combined."

    So if you are using oversampling then only one channel can be enabled at any time (only one CH[n].PSELP is set).

    Best regards,
    Kenneth

  • Hello Kenneth,

    Thank you for your reply.

    However, according to the documentation, oversampling with scan mode is supported, if burst is enabled on all channels.

    "Scan mode can be combined with BURST=1, if burst is enabled on all channels." 

    And also according to this post, https://devzone.nordicsemi.com/f/nordic-q-a/20556/how-does-the-saadc-scan-mode-use-the-burst-1, it also answered that when burst is enabled for all the active channels, scan mode with oversampling will work correctly.

    I did a test with following code for scan mode with oversampling and burst enabled for channel 1 & 2 at the SAME sampling rate (250ms). From the log, the SAADC does appear to work correctly.

    Moreover, for the "oversampling then only one channel can be enabled at any time", according to this post, https://devzone.nordicsemi.com/f/nordic-q-a/26659/saacd-scan-oversample/104878#104878, although the SAADC driver in the SDK is not support the multichannel oversampling yet, it can be quite simple to add this function by following the Jørgen's instructions. To simplify my test, I just comment out the Line 293 & 294 at nrfx_saadc_channel_init of nrfx_saadc.c.

    Therefore my question is: For sampling two analog signals at different sampling rate in scan mode with oversampling and burst enabled, how to initialize and un-nitialize the channel 1 & 2 correctly? Thanks!

    Here is the log for scan mode with oversampling and burst enabled for channel 1 & 2 at the SAME sampling rate:

    <info> app: ch0 result cnt: 0, raw: 100
    <info> app: ch1 result cnt: 0, raw:3522, value: 4.1283521
    <info> app: ch0 result cnt: 1, raw: 164
    <info> app: ch1 result cnt: 1, raw:3517, value: 4.1224913
    <info> app: ch0 result cnt: 2, raw: 163
    <info> app: ch1 result cnt: 2, raw:3517, value: 4.1224913
    <info> app: ch0 result cnt: 3, raw: 167
    <info> app: ch1 result cnt: 3, raw:3516, value: 4.1213188
    <info> app: ch0 result cnt: 4, raw: 177
    <info> app: ch1 result cnt: 4, raw:3530, value: 4.1377291
    <info> app: ch0 result cnt: 5, raw: 179
    <info> app: ch1 result cnt: 5, raw:3567, value: 4.1810994

    Here is my code for testing scan mode with oversampling and burst enabled for channel 1 & 2 at the SAME sampling rate:

    #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);
    
    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 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;
            
            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("ch0 result 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 cnt: %d, raw:%i, value: %.7f", countCh1, p_event->data.done.p_buffer[1], ch1Value);
            NRF_LOG_INFO("%s", logChar);
            
            countCh0++;
            countCh1++;
        }
    }
    
    
    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);
        
        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);
    }
    
    
    /**
     * @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();
        
        //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();
            }
        }
    
    }
    
    

  • My suggestion is that you set a flag in the SAADC callback function, and then you in main initialize and un-initialize the channel 1 and 2 as you see fit depending on the flag. I think the problem is that the callback handler is called from nrfx_saadc_irq_handler(), and that occurs before the nrfx_saadc_irq_handler is finished executing and you thereby create a bad state if you uninit and init the saadc from the callback handler.

  • 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;
            }
        }
    
    }
    
    

     

Related