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

     

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

     

Children
Related