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 Reply Children
  • Thank you for your reply. My current implementation performs each operation, including calibration, in blocking mode according to the documentation. If I include an event handler, whose implementation is not yet supported on an RTOS, I have to do event handling manually with the ISR I provided above.

    This is a problem because the nrfx_saadc driver has an internal structure which relies on information passed to the nrfx_saadc_handler_t and I can not replicate those updates with my ISR. This makes the driver think that resources are still busy when they in fact are not. I can't update this internal struct with my ISR because it is declared as static.

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

  • I see a typo when you assign the input pins to your SAADC channels, it should be:

        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;

  • Oh thanks! It is not like that in my actual code. I renamed a lot of things to make it easier for support.

  • The nrfx 2.1.0 was just relased, I can see there are fixes that may affect your issue, can you give it a go?

    https://github.com/NordicSemiconductor/nrfx 

Related