Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Look like a bug inside the function nrfx_saadc_calibrate_offset() in SDK 15.3.0 (nRF5_SDK_15.3.0_59ac345)

m_cb.adc_state is set to NRF_SAADC_STATE_CALIBRATION inside the function nrfx_saadc_calibrate_offset(), but the value is never changed to NRF_SAADC_STATE_IDLE, even is the function return with NRFX_SUCCESS.

Many functions, like nrfx_saadc_channel_init() will return with NRFX_ERROR_BUSY as m_cb.adc_state != NRF_SAADC_STATE_IDLE.

Workaround is to call nrfx_saadc_uninit() that set m_cb.adc_state = NRF_SAADC_STATE_IDLE, and doing a new call to nrfx_saadc_init()

Parents
  • Hi Daniel

    I've reported this internally. It might take a few days to get a reply, but I'll update you when I've gotten a confirmation or explanation on this!

    Best regards,

    Simon

  • Hi Simon,

    With some search, I found the solution from https://devzone.nordicsemi.com/f/nordic-q-a/33785/a-plurality-of-a-d-conversion-processing/129869#129869

    The application must wait long enough to run the IRQ. nrfx_saadc_irq_handler () sets m_cb.adc_state = NRF_SAADC_STATE_IDLE and calls our own adc_event_handler.

    I provide my solution in case it could help someone else. In my application, nrfx_saadc_calibrate_offset() is called once every 30 minutes. nrfx_saadc_uninit() and nrfx_saadc_init() are called in sequence as workaround for the errata 86 https://infocenter.nordicsemi.com/index.jsp?topic=%2Ferrata_nRF52832_Rev2%2FERR%2FnRF52832%2FRev2%2Flatest%2Fanomaly_832_86.html&cp=3_1_1_0_1_23

    nrfx_saadc_config_t saadcCfg;
    saadcCfg.resolution = NRF_SAADC_RESOLUTION_10BIT;
    saadcCfg.oversample = NRF_SAADC_OVERSAMPLE_DISABLED;
    saadcCfg.low_power_mode = false;
    saadcCfg.interrupt_priority = SAADC_IRQn;
    nrfx_saadc_init(&saadcCfg, ub_adc_event_handler);
    /*-<ADC offset calibration >-*/
    err_code = nrfx_saadc_calibrate_offset();
    APP_ERROR_CHECK(err_code);
    while (nrfx_saadc_is_busy()) // Wait for calibration to complete
    {
        __WFE(); //
        __SEV(); //
        __WFE(); // This sequence puts the system to sleep (SystemON) while waiting
    }
    // Nordic errata 86 workaround
    nrfx_saadc_uninit();
    nrfx_saadc_init(&saadcCfg, ub_adc_event_handler);
    power_vcc = power_vcc_read();
    printf("Vcc: %3d V\n", (int)((power_vcc + 0.005* 100));
     
    /*****************************************************************************/

    /**
    * \brief      Read the Vcc voltage
    *
    * \param: none
    * \return Vcc
    *
    */
    float power_vcc_read(void)
    {
        nrf_saadc_channel_config_t channelcfg;
        float volt;
        nrf_saadc_value_t ADC_count;

        // Channel configuration
        channelcfg.resistor_p = NRF_SAADC_RESISTOR_DISABLED;
        channelcfg.resistor_n = NRF_SAADC_RESISTOR_DISABLED;
        channelcfg.gain = NRF_SAADC_GAIN1_6;
        channelcfg.reference = NRF_SAADC_REFERENCE_INTERNAL;
        channelcfg.acq_time = SAADC_CH_CONFIG_TACQ_20us;
        channelcfg.mode = NRF_SAADC_MODE_SINGLE_ENDED;
        /* Burst mode used for oversampling */
        if (NRFX_SAADC_CONFIG_OVERSAMPLE != 0)
        {
            channelcfg.burst = NRF_SAADC_BURST_ENABLED;
        }
        else
        {
            channelcfg.burst = NRF_SAADC_BURST_DISABLED;
        }
        channelcfg.pin_p = (SUPPLY_VCC_ADC_PIN + 1); /* NRF_SAADC_INPUT_DISABLED is the value 0 */
        channelcfg.pin_n = NRF_SAADC_INPUT_DISABLED;
        nrfx_saadc_channel_init(0&channelcfg);

        // ADC conversion
        nrfx_saadc_sample_convert(SUPPLY_VCC_ADC_PIN, &ADC_count);
        /* 3.6 = Internal Reference (0.6V) * 6 */
        volt = 3.6 * ADC_count / cfg.adcmax;

        return (volt);
    }
    Best regards,
    Daniel
Reply
  • Hi Simon,

    With some search, I found the solution from https://devzone.nordicsemi.com/f/nordic-q-a/33785/a-plurality-of-a-d-conversion-processing/129869#129869

    The application must wait long enough to run the IRQ. nrfx_saadc_irq_handler () sets m_cb.adc_state = NRF_SAADC_STATE_IDLE and calls our own adc_event_handler.

    I provide my solution in case it could help someone else. In my application, nrfx_saadc_calibrate_offset() is called once every 30 minutes. nrfx_saadc_uninit() and nrfx_saadc_init() are called in sequence as workaround for the errata 86 https://infocenter.nordicsemi.com/index.jsp?topic=%2Ferrata_nRF52832_Rev2%2FERR%2FnRF52832%2FRev2%2Flatest%2Fanomaly_832_86.html&cp=3_1_1_0_1_23

    nrfx_saadc_config_t saadcCfg;
    saadcCfg.resolution = NRF_SAADC_RESOLUTION_10BIT;
    saadcCfg.oversample = NRF_SAADC_OVERSAMPLE_DISABLED;
    saadcCfg.low_power_mode = false;
    saadcCfg.interrupt_priority = SAADC_IRQn;
    nrfx_saadc_init(&saadcCfg, ub_adc_event_handler);
    /*-<ADC offset calibration >-*/
    err_code = nrfx_saadc_calibrate_offset();
    APP_ERROR_CHECK(err_code);
    while (nrfx_saadc_is_busy()) // Wait for calibration to complete
    {
        __WFE(); //
        __SEV(); //
        __WFE(); // This sequence puts the system to sleep (SystemON) while waiting
    }
    // Nordic errata 86 workaround
    nrfx_saadc_uninit();
    nrfx_saadc_init(&saadcCfg, ub_adc_event_handler);
    power_vcc = power_vcc_read();
    printf("Vcc: %3d V\n", (int)((power_vcc + 0.005* 100));
     
    /*****************************************************************************/

    /**
    * \brief      Read the Vcc voltage
    *
    * \param: none
    * \return Vcc
    *
    */
    float power_vcc_read(void)
    {
        nrf_saadc_channel_config_t channelcfg;
        float volt;
        nrf_saadc_value_t ADC_count;

        // Channel configuration
        channelcfg.resistor_p = NRF_SAADC_RESISTOR_DISABLED;
        channelcfg.resistor_n = NRF_SAADC_RESISTOR_DISABLED;
        channelcfg.gain = NRF_SAADC_GAIN1_6;
        channelcfg.reference = NRF_SAADC_REFERENCE_INTERNAL;
        channelcfg.acq_time = SAADC_CH_CONFIG_TACQ_20us;
        channelcfg.mode = NRF_SAADC_MODE_SINGLE_ENDED;
        /* Burst mode used for oversampling */
        if (NRFX_SAADC_CONFIG_OVERSAMPLE != 0)
        {
            channelcfg.burst = NRF_SAADC_BURST_ENABLED;
        }
        else
        {
            channelcfg.burst = NRF_SAADC_BURST_DISABLED;
        }
        channelcfg.pin_p = (SUPPLY_VCC_ADC_PIN + 1); /* NRF_SAADC_INPUT_DISABLED is the value 0 */
        channelcfg.pin_n = NRF_SAADC_INPUT_DISABLED;
        nrfx_saadc_channel_init(0&channelcfg);

        // ADC conversion
        nrfx_saadc_sample_convert(SUPPLY_VCC_ADC_PIN, &ADC_count);
        /* 3.6 = Internal Reference (0.6V) * 6 */
        volt = 3.6 * ADC_count / cfg.adcmax;

        return (volt);
    }
    Best regards,
    Daniel
Children
No Data
Related