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

nrf52840 saadc calibration error

After starting the program, I do the first calibration and read the data 30 times.
The standard deviation is between 2.0 and 2.3.

Perform 30 "Calibration -> Read Data" each time
The standard deviation is 3.8 to 4.7 degrees.

The offset varies with each calibration.
How can I minimize this?

I use the internal reference(0.6V) and 1/6 gain

Parents
  • Hi 

    I will help Stian out with this case. 

    Can you let me know how you calibrate the ADC and trigger the sample? 

    Are you able to share a code sample, or a reference to the code used?

    Best regards
    Torbjørn

  • void saadc_calibration(void){
        uint8_t busy_count = 0;

        nrfx_saadc_abort();
        (void)nrfx_saadc_calibrate_offset();

        while (nrfx_saadc_is_busy() && busy_count++ < 100U) {
            nrf_delay_us(1U);
        }

        if (busy_count < 100U) {
            s_saadc_cal_state = (uint8_t)SAADC_CAL_DONE;
            NRF_LOG_INFO("SAADC calibration complete, busy_count %d", busy_count );
        }
        else {
            s_saadc_cal_state = (uint8_t)SAADC_CAL_RETRY;
            NRF_LOG_INFO("SAADC calibration fail");
        }
    }

    #define ADC_VOLTS(ADC_VALUE) \
        (int16_t)(((((double)ADC_VALUE * ADC_REF_VOLTAGE_IN_MILLIVOLTS) / ADC_RES_10BIT) * \
        ADC_PRE_SCALING_COMPENSATION) + 0.5)
    #define SAMPLES_COUNT 10
    static nrf_saadc_value_t m_buffer[SAMPLES_COUNT];

    void saadc_read_data(uint8_t channel){
        uint8_t i = 0;
        uint16_t min_index = 0;
        uint16_t max_index = SAMPLES_COUNT - 1U;
        int16_t min = INT16_MAX;
        int16_t max = INT16_MIN;
        int32_t avg, sum = 0;
        int16_t res = 0;


        for (i = 0 ; i < SAMPLES_COUNT ; i++) {
            (void)nrfx_saadc_sample_convert(channel,&m_buffer[i]);
        }

        //Searching a min or max value
        for (i = 0 ; i < SAMPLES_COUNT ; i++) {
            if (min > m_buffer[i]) {
                min_index = i;
                min = m_buffer[i];
            }

            if (max < m_buffer[i]) {
                max_index = i;
                max = m_buffer[i];
            }

            sum += (int32_t)m_buffer[i];
        }

        //Check index
        if (max_index != min_index) {
            //Get an average value without the max value and the min value
            sum -= (int32_t)m_buffer[max_index];
            sum -= (int32_t)m_buffer[min_index];
            avg = (int32_t)(((double)sum / (SAMPLES_COUNT - 2U)) + 0.5);
        }
        else {
            avg = (int32_t)(((double)sum / SAMPLES_COUNT) + 0.5);
        }

        return ADC_VOLTS(avg);
    }

    The busy_count used in saadc_calibration() is a time out function.
    When busy_count exceeds 100 and s_saadc_cal_state becomes SAADC_CAL_RETRY, saadc_calibration() is called again from another place.
    (However, I get a busy count of 43 in all calibrations, and calibration succeeds.)

    Adc sampling actually reads 10 times and uses 8 averages excluding the minimum and maximum values.

Reply
  • void saadc_calibration(void){
        uint8_t busy_count = 0;

        nrfx_saadc_abort();
        (void)nrfx_saadc_calibrate_offset();

        while (nrfx_saadc_is_busy() && busy_count++ < 100U) {
            nrf_delay_us(1U);
        }

        if (busy_count < 100U) {
            s_saadc_cal_state = (uint8_t)SAADC_CAL_DONE;
            NRF_LOG_INFO("SAADC calibration complete, busy_count %d", busy_count );
        }
        else {
            s_saadc_cal_state = (uint8_t)SAADC_CAL_RETRY;
            NRF_LOG_INFO("SAADC calibration fail");
        }
    }

    #define ADC_VOLTS(ADC_VALUE) \
        (int16_t)(((((double)ADC_VALUE * ADC_REF_VOLTAGE_IN_MILLIVOLTS) / ADC_RES_10BIT) * \
        ADC_PRE_SCALING_COMPENSATION) + 0.5)
    #define SAMPLES_COUNT 10
    static nrf_saadc_value_t m_buffer[SAMPLES_COUNT];

    void saadc_read_data(uint8_t channel){
        uint8_t i = 0;
        uint16_t min_index = 0;
        uint16_t max_index = SAMPLES_COUNT - 1U;
        int16_t min = INT16_MAX;
        int16_t max = INT16_MIN;
        int32_t avg, sum = 0;
        int16_t res = 0;


        for (i = 0 ; i < SAMPLES_COUNT ; i++) {
            (void)nrfx_saadc_sample_convert(channel,&m_buffer[i]);
        }

        //Searching a min or max value
        for (i = 0 ; i < SAMPLES_COUNT ; i++) {
            if (min > m_buffer[i]) {
                min_index = i;
                min = m_buffer[i];
            }

            if (max < m_buffer[i]) {
                max_index = i;
                max = m_buffer[i];
            }

            sum += (int32_t)m_buffer[i];
        }

        //Check index
        if (max_index != min_index) {
            //Get an average value without the max value and the min value
            sum -= (int32_t)m_buffer[max_index];
            sum -= (int32_t)m_buffer[min_index];
            avg = (int32_t)(((double)sum / (SAMPLES_COUNT - 2U)) + 0.5);
        }
        else {
            avg = (int32_t)(((double)sum / SAMPLES_COUNT) + 0.5);
        }

        return ADC_VOLTS(avg);
    }

    The busy_count used in saadc_calibration() is a time out function.
    When busy_count exceeds 100 and s_saadc_cal_state becomes SAADC_CAL_RETRY, saadc_calibration() is called again from another place.
    (However, I get a busy count of 43 in all calibrations, and calibration succeeds.)

    Adc sampling actually reads 10 times and uses 8 averages excluding the minimum and maximum values.

Children
No Data
Related