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

nrfx_saadc.c Fills Buffer in Indefinite Order and Skips Samples

Hardware/Software
Device: nRF5340 PDK
Software versions:
NordicSemiconductor/nrfx v2.0.0
zephyrproject-rtos/zephyr v2.1.0


Description
I'm currently sampling 4 channels using `nrfx_saadc_simple_mode_set(...)` in blocking mode, and calling `nrfx_saadc_mode_trigger()` after calibrating the saadc in blocking mode.

The first time sampling the buffer looks like {CH0, CH1, CH3, CH2} (which is an issue of its own) and each time it is sampled afterwards it looks like {~2473, CH0, CH1, CH3}

ch0 = 247, ch1 = 328, ch2 = 525, ch3 = 427
ch0 = 2474, ch1 = 247, ch2 = 328, ch3 = 524
ch0 = 2483, ch1 = 248, ch2 = 328, ch3 = 524
ch0 = 2473, ch1 = 247, ch2 = 328, ch3 = 524


Code

#include <zephyr.h>
#include <sys/printk.h>
#include <power/power.h>
#include <soc.h>
#include <nrfx_saadc.h>

#define DEFAULT_CHANNEL_CONFIG = {                  \
    .channel_config =                               \
    {                                               \
        .resistor_p = NRF_SAADC_RESISTOR_DISABLED,  \
        .resistor_n = NRF_SAADC_RESISTOR_DISABLED,  \
        .gain       = NRF_SAADC_GAIN1_6,            \
        .reference  = NRF_SAADC_REFERENCE_INTERNAL, \
        .acq_time   = NRF_SAADC_ACQTIME_40US,       \
        .mode       = NRF_SAADC_MODE_SINGLE_ENDED,  \
        .burst      = NRF_SAADC_BURST_ENABLED,      \
    },                                              \
    .pin_p          = NRF_SAADC_INPUT_AIN0,         \
    .pin_n          = NRF_SAADC_INPUT_DISABLED,     \
    .channel_index  = 0                             \
};

K_TIMER_DEFINE(wake_timer, NULL, NULL);

ISR_DIRECT_DECLARE(saadc_isr){
    if (NRF_SAADC_S->EVENTS_STARTED){
      NRF_SAADC_S->EVENTS_STARTED = 0;
    }

    if (NRF_SAADC_S->EVENTS_END){
      NRF_SAADC_S->EVENTS_END = 0;
    }
    
    if (NRF_SAADC_S->EVENTS_DONE){
      NRF_SAADC_S->EVENTS_DONE = 0;
    }

    if (NRF_SAADC_S->EVENTS_RESULTDONE){
      NRF_SAADC_S->EVENTS_RESULTDONE = 0;
    }
    
    if (NRF_SAADC_S->EVENTS_CALIBRATEDONE){
      NRF_SAADC_S->EVENTS_CALIBRATEDONE = 0;
    }
    
    if (NRF_SAADC_S->EVENTS_STOPPED){
      NRF_SAADC_S->EVENTS_STOPPED = 0;
    }

    ISR_DIRECT_PM();
    
    return 1;
}

int main(void){
    nrfx_err_t err_code;
    nrfx_saadc_channel_t ch0 = DEFAULT_CHANNEL_CONFIG;
    nrfx_saadc_channel_t ch1 = DEFAULT_CHANNEL_CONFIG;
    nrfx_saadc_channel_t ch2 = DEFAULT_CHANNEL_CONFIG;
    nrfx_saadc_channel_t ch3 = DEFAULT_CHANNEL_CONFIG;
    
    ch1.pin_p = NRF_SAADC_INPUT_AIN6;
    ch1.channel_index = 1;
    ch1.pin_p = NRF_SAADC_INPUT_AIN7;
    ch1.channel_index = 2;
    ch1.pin_p = NRF_SAADC_INPUT_AIN2;
    ch1.channel_index = 3;

            while (true){
                // Connect IRQ
                IRQ_DIRECT_CONNECT(SAADC_IRQn, 6, saadc_isr, 0);

                err_code = nrfx_saadc_init(0);
                if (err_code != NRFX_SUCCESS)
                  return err_code;

                nrfx_saadc_channel_t channels[4] = {ch0, ch1, ch2, ch3};
                err_code = nrfx_saadc_channels_config(channels, 4);
                if (err_code != NRFX_SUCCESS)
                  return err_code;

                // Calibrate SAADC
                err_code = nrfx_saadc_offset_calibrate(NULL);
                if (err_code != NRFX_SUCCESS)
                  return err_code;
                
                u8_t channel_mask = (1U << 0) | ( 1U << 1) | (1U << 2) | (1U << 3);
                err_code = nrfx_saadc_simple_mode_set(channel_mask,
                                              NRF_SAADC_RESOLUTION_10BIT,
                                              NRF_SAADC_OVERSAMPLE_16X,
                                              NULL);
                if (err_code != NRFX_SUCCESS)
                    return err_code;
                
                // Give buffer and trigger sample
                nrf_saadc_value_t buffer[4] = {0};
                err_code = nrfx_saadc_buffer_set(buffer, 4);
                if (err_code != NRFX_SUCCESS)
                    return err_code;
                err_code = nrfx_saadc_mode_trigger();
                if (err_code != NRFX_SUCCESS)
                    return err_code;
                // Note: channel order is off in buffer
                nrf_saadc_value_t ch0_counts = buffer[0]; // Forced to ~250cnts
                nrf_saadc_value_t ch1_counts = buffer[1]; // Forced to ~350cnts
                nrf_saadc_value_t ch2_counts = buffer[2]; // Forced to ~450cnts
                nrf_saadc_value_t ch3_coutns = buffer[3]; // Forced to ~550cnts

                printk("ch0 = %d, ch1 = %d, ch2 = %d, ch3 = %d\n",
                       ch0_counts, ch1_counts, ch2_counts, ch3_counts);
                
                // Sleep for 100ms
                nrfx_saadc_uninit();
                k_timer_start(&wake_timer, K_MSEC(100), 0);
                k_cpu_idle();
            }
}

Parents
  • My first guess is that the offset calibration is not complete before you configure the buffer, and one or more of the samples taken during offset calibration has started to fill the buffer. You should wait for EVENTS_CALIBRATEDONE before you continue configuring the SAADC. 

  • I modified my code to work asynchronously with some event handling and it still didn't solve the issue. I realized a mistake I was making with my ISR, but it proved not to be causing any immediate problems.

    Changes
    [1] Calibrates before configuring channels
    [2] Waits for NRFX_SAADC_EVT_CALIBRATEDONE before configuring channels
    [3] Waits for NRFX_SAADC_EVT_DONE before unloading buffer
    [4] Uses nrfx_saadc_handler_t callback for event handling



    #include <zephyr.h>
    #include <sys/printk.h>
    #include <power/power.h>
    #include <soc.h>
    #include <nrfx_saadc.h>
    
    #define DEFAULT_CHANNEL_CONFIG = {                  \
        .channel_config =                               \
        {                                               \
            .resistor_p = NRF_SAADC_RESISTOR_DISABLED,  \
            .resistor_n = NRF_SAADC_RESISTOR_DISABLED,  \
            .gain       = NRF_SAADC_GAIN1_6,            \
            .reference  = NRF_SAADC_REFERENCE_INTERNAL, \
            .acq_time   = NRF_SAADC_ACQTIME_40US,       \
            .mode       = NRF_SAADC_MODE_SINGLE_ENDED,  \
            .burst      = NRF_SAADC_BURST_ENABLED,      \
        },                                              \
        .pin_p          = NRF_SAADC_INPUT_AIN0,         \
        .pin_n          = NRF_SAADC_INPUT_DISABLED,     \
        .channel_index  = 0                             \
    };
    
    static volatile bool calibrate_done = true;
    static volatile bool buf_full = true;
    
    K_TIMER_DEFINE(wake_timer, NULL, NULL);
    
    ISR_DIRECT_DECLARE(saadc_isr){
        nrfx_saadc_irq_handler();
        ISR_DIRECT_PM();
        return 1;
    }
    
    /*
     * ADC event handler
     * This function is called when the requested number of samples has been processed
     */
    static void saadc_handler(nrfx_saadc_evt_t const *p_event)
    {
        //TODO fill in
        switch (p_event->type)
        {
            case  NRFX_SAADC_EVT_DONE:  // Event generated when the buffer is filled with samples.
                buf_full = true;
                break;
            case NRFX_SAADC_EVT_LIMIT:  // Event generated after one of the limits is reached.
                break;
            case NRFX_SAADC_EVT_BUF_REQ:  // Event generated when the next buffer for continuous conversion is requested.
                break;
            case NRFX_SAADC_EVT_READY:  // Event generated when the first buffer is acquired by the peripheral and sampling can be started.
                break;
            case NRFX_SAADC_EVT_FINISHED: // Event generated when all supplied buffers are filled with results.
                break;
            case NRFX_SAADC_EVT_CALIBRATEDONE: // Event generated when the calibration is complete.
                calibrate_done = true;
                break;
        }
    }
    
    
    int main(void){
        nrfx_err_t err_code;
        nrfx_saadc_channel_t ch0 = DEFAULT_CHANNEL_CONFIG;
        nrfx_saadc_channel_t ch1 = DEFAULT_CHANNEL_CONFIG;
        nrfx_saadc_channel_t ch2 = DEFAULT_CHANNEL_CONFIG;
        nrfx_saadc_channel_t ch3 = DEFAULT_CHANNEL_CONFIG;
        
        ch1.pin_p = NRF_SAADC_INPUT_AIN6;
        ch1.channel_index = 1;
        ch2.pin_p = NRF_SAADC_INPUT_AIN7;
        ch2.channel_index = 2;
        ch3.pin_p = NRF_SAADC_INPUT_AIN2;
        ch3.channel_index = 3;
    
                while (true){
                    // Connect IRQ
                    IRQ_DIRECT_CONNECT(SAADC_IRQn, 0, saadc_isr, 0);
    
                    err_code = nrfx_saadc_init(0);
                    if (err_code != NRFX_SUCCESS)
                      return err_code;
                    
                    // Calibrate SAADC
                    calibrate_done = false;
                    err_code = nrfx_saadc_offset_calibrate(saadc_handler);
                    if (err_code != NRFX_SUCCESS)
                      return err_code;
                    
                    while(!calibrate_done){
                      k_sleep(K_MSEC(1));
                    }
    
                    nrfx_saadc_channel_t channels[4] = {ch0, ch1, ch2, ch3};
                    err_code = nrfx_saadc_channels_config(channels, 4);
                    if (err_code != NRFX_SUCCESS)
                      return err_code;
                    
                    u8_t channel_mask = (1U << 0) | ( 1U << 1) | (1U << 2) | (1U << 3);
                    err_code = nrfx_saadc_simple_mode_set(channel_mask,
                                                  NRF_SAADC_RESOLUTION_10BIT,
                                                  NRF_SAADC_OVERSAMPLE_16X,
                                                  saadc_handler);
                    if (err_code != NRFX_SUCCESS)
                        return err_code;
                    
                    // Give buffer and trigger sample
                    nrf_saadc_value_t buffer[4] = {0};
                    err_code = nrfx_saadc_buffer_set(buffer, 4);
                    if (err_code != NRFX_SUCCESS)
                        return err_code;
                    
                    buf_full = false;
                    err_code = nrfx_saadc_mode_trigger();
                    if (err_code != NRFX_SUCCESS)
                        return err_code;
                    while(!buf_full){
                      k_sleep(K_MSEC(1));
                    }
                    
                    // Note: channel order is off in buffer
                    nrf_saadc_value_t ch0_counts = buffer[0]; // Forced to ~250cnts
                    nrf_saadc_value_t ch1_counts = buffer[1]; // Forced to ~350cnts
                    nrf_saadc_value_t ch2_counts = buffer[2]; // Forced to ~450cnts
                    nrf_saadc_value_t ch3_coutns = buffer[3]; // Forced to ~550cnts
    
                    printk("ch0 = %d, ch1 = %d, ch2 = %d, ch3 = %d\n",
                           ch0_counts, ch1_counts, ch2_counts, ch3_counts);
                    
                    // Sleep for 100ms
                    calibrate_done = true;
                    nrfx_saadc_uninit();
                    k_timer_start(&wake_timer, K_MSEC(100), 0);
                    k_cpu_idle();
                }
    }

Reply
  • I modified my code to work asynchronously with some event handling and it still didn't solve the issue. I realized a mistake I was making with my ISR, but it proved not to be causing any immediate problems.

    Changes
    [1] Calibrates before configuring channels
    [2] Waits for NRFX_SAADC_EVT_CALIBRATEDONE before configuring channels
    [3] Waits for NRFX_SAADC_EVT_DONE before unloading buffer
    [4] Uses nrfx_saadc_handler_t callback for event handling



    #include <zephyr.h>
    #include <sys/printk.h>
    #include <power/power.h>
    #include <soc.h>
    #include <nrfx_saadc.h>
    
    #define DEFAULT_CHANNEL_CONFIG = {                  \
        .channel_config =                               \
        {                                               \
            .resistor_p = NRF_SAADC_RESISTOR_DISABLED,  \
            .resistor_n = NRF_SAADC_RESISTOR_DISABLED,  \
            .gain       = NRF_SAADC_GAIN1_6,            \
            .reference  = NRF_SAADC_REFERENCE_INTERNAL, \
            .acq_time   = NRF_SAADC_ACQTIME_40US,       \
            .mode       = NRF_SAADC_MODE_SINGLE_ENDED,  \
            .burst      = NRF_SAADC_BURST_ENABLED,      \
        },                                              \
        .pin_p          = NRF_SAADC_INPUT_AIN0,         \
        .pin_n          = NRF_SAADC_INPUT_DISABLED,     \
        .channel_index  = 0                             \
    };
    
    static volatile bool calibrate_done = true;
    static volatile bool buf_full = true;
    
    K_TIMER_DEFINE(wake_timer, NULL, NULL);
    
    ISR_DIRECT_DECLARE(saadc_isr){
        nrfx_saadc_irq_handler();
        ISR_DIRECT_PM();
        return 1;
    }
    
    /*
     * ADC event handler
     * This function is called when the requested number of samples has been processed
     */
    static void saadc_handler(nrfx_saadc_evt_t const *p_event)
    {
        //TODO fill in
        switch (p_event->type)
        {
            case  NRFX_SAADC_EVT_DONE:  // Event generated when the buffer is filled with samples.
                buf_full = true;
                break;
            case NRFX_SAADC_EVT_LIMIT:  // Event generated after one of the limits is reached.
                break;
            case NRFX_SAADC_EVT_BUF_REQ:  // Event generated when the next buffer for continuous conversion is requested.
                break;
            case NRFX_SAADC_EVT_READY:  // Event generated when the first buffer is acquired by the peripheral and sampling can be started.
                break;
            case NRFX_SAADC_EVT_FINISHED: // Event generated when all supplied buffers are filled with results.
                break;
            case NRFX_SAADC_EVT_CALIBRATEDONE: // Event generated when the calibration is complete.
                calibrate_done = true;
                break;
        }
    }
    
    
    int main(void){
        nrfx_err_t err_code;
        nrfx_saadc_channel_t ch0 = DEFAULT_CHANNEL_CONFIG;
        nrfx_saadc_channel_t ch1 = DEFAULT_CHANNEL_CONFIG;
        nrfx_saadc_channel_t ch2 = DEFAULT_CHANNEL_CONFIG;
        nrfx_saadc_channel_t ch3 = DEFAULT_CHANNEL_CONFIG;
        
        ch1.pin_p = NRF_SAADC_INPUT_AIN6;
        ch1.channel_index = 1;
        ch2.pin_p = NRF_SAADC_INPUT_AIN7;
        ch2.channel_index = 2;
        ch3.pin_p = NRF_SAADC_INPUT_AIN2;
        ch3.channel_index = 3;
    
                while (true){
                    // Connect IRQ
                    IRQ_DIRECT_CONNECT(SAADC_IRQn, 0, saadc_isr, 0);
    
                    err_code = nrfx_saadc_init(0);
                    if (err_code != NRFX_SUCCESS)
                      return err_code;
                    
                    // Calibrate SAADC
                    calibrate_done = false;
                    err_code = nrfx_saadc_offset_calibrate(saadc_handler);
                    if (err_code != NRFX_SUCCESS)
                      return err_code;
                    
                    while(!calibrate_done){
                      k_sleep(K_MSEC(1));
                    }
    
                    nrfx_saadc_channel_t channels[4] = {ch0, ch1, ch2, ch3};
                    err_code = nrfx_saadc_channels_config(channels, 4);
                    if (err_code != NRFX_SUCCESS)
                      return err_code;
                    
                    u8_t channel_mask = (1U << 0) | ( 1U << 1) | (1U << 2) | (1U << 3);
                    err_code = nrfx_saadc_simple_mode_set(channel_mask,
                                                  NRF_SAADC_RESOLUTION_10BIT,
                                                  NRF_SAADC_OVERSAMPLE_16X,
                                                  saadc_handler);
                    if (err_code != NRFX_SUCCESS)
                        return err_code;
                    
                    // Give buffer and trigger sample
                    nrf_saadc_value_t buffer[4] = {0};
                    err_code = nrfx_saadc_buffer_set(buffer, 4);
                    if (err_code != NRFX_SUCCESS)
                        return err_code;
                    
                    buf_full = false;
                    err_code = nrfx_saadc_mode_trigger();
                    if (err_code != NRFX_SUCCESS)
                        return err_code;
                    while(!buf_full){
                      k_sleep(K_MSEC(1));
                    }
                    
                    // Note: channel order is off in buffer
                    nrf_saadc_value_t ch0_counts = buffer[0]; // Forced to ~250cnts
                    nrf_saadc_value_t ch1_counts = buffer[1]; // Forced to ~350cnts
                    nrf_saadc_value_t ch2_counts = buffer[2]; // Forced to ~450cnts
                    nrf_saadc_value_t ch3_coutns = buffer[3]; // Forced to ~550cnts
    
                    printk("ch0 = %d, ch1 = %d, ch2 = %d, ch3 = %d\n",
                           ch0_counts, ch1_counts, ch2_counts, ch3_counts);
                    
                    // Sleep for 100ms
                    calibrate_done = true;
                    nrfx_saadc_uninit();
                    k_timer_start(&wake_timer, K_MSEC(100), 0);
                    k_cpu_idle();
                }
    }

Children
Related