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

nRF52 SAADC Calibration Causes All Channels to Read The Same Analog Values

Hello,

I'm currently building a project for the nRF52 (using SDK 15.2). I need to have 2 ADC channels, 1 for temperature sensor and 1 for battery level. I figured that I need to do offset calibration every now and then but I'm having trouble with it. My code (attached below) is following the low power saadc example on github found at https://github.com/NordicPlayground/nRF52-ADC-examples/blob/master/saadc_low_power/main.c . If I run the calibration, the next reading from both ADC channels are always identical (they should have been very different from one another). Once I remove the calibration code, everything works as expected. Any help on how to properly do calibration for multiple ADC channels would be appreciated. 

//SAADC Callback
//SAMPLES_IN_BUFFER is defined as 2
void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
    ret_code_t err_code;
    //printf("Counter = %d\r\n", m_adc_evt_counter);  
    
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
    {
        if((m_adc_evt_counter % SAADC_CALIBRATION_INTERVAL) == 0)                                  //Evaluate if offset calibration should be performed. Configure the SAADC_CALIBRATION_INTERVAL constant to change the calibration frequency
        {
            nrf_drv_saadc_abort();                                                                      // Abort all ongoing conversions. Calibration cannot be run if SAADC is busy
            m_saadc_calibrate = true;                                                                   // Set flag to trigger calibration in main context when SAADC is stopped
            m_adc_evt_counter = 1;
            //printf("\r\nCalibrate\r\n");
        }

        if (m_saadc_calibrate == false)
        {
            for (int i = 0; i < p_event->data.done.size; i++)
            {
                //NRF_LOG_INFO("%d", p_event->data.done.p_buffer[i]);
                

                // Convert ADC reading to Temperature and send it through UART
                uint32_t adcReading = 0;
                uint32_t adcReading2 = 0;
                uint32_t temp = 0;
                float temperature = 0.0;
                float batteryLevel = 0.0;
           
                if (p_event->data.done.p_buffer[i] > 0)
                {
                    if (i == 0)
                    {
                        adcReading = p_event->data.done.p_buffer[i];
                        temperature = ConvertADCToTemperature((float)(adcReading));
                        temp = (int)(temperature * 10);
                        //NRF_LOG_INFO("ADC: %d \n", adcReading);
                        printf("\r\nNo: %d\r\n", CONFIG_REC_KEY);
                        printf("\r\nTemperature: %d ADC: %d\r\n", temp, adcReading); 
                    }

                    if (i == 1){                     
                      adcReading2 = p_event->data.done.p_buffer[i];
                      //NRF_LOG_INFO("ADC: %d \n", adcReading);
                      batteryLevel = (float)(adcReading2) * (float)(mV_per_LSB);
                      printf("\r\nBattery Adc Reading: %d, Battery Level: "NRF_LOG_FLOAT_MARKER "\r\n", adcReading2, NRF_LOG_FLOAT(batteryLevel)); 
                    }
                }
            }
            m_adc_evt_counter++;  
            err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER);
            APP_ERROR_CHECK(err_code);
        }     
    }
    if (p_event->type == NRF_DRV_SAADC_EVT_CALIBRATEDONE)
    {   
        printf("\r\nDone Calibrating\r\n");
        err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER);             //Set buffer so the SAADC can write to it again. 
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER);             //Need to setup both buffers, as they were both removed with the call to nrf_drv_saadc_abort before calibration.
        APP_ERROR_CHECK(err_code);
    }
}

void saadc_init(void)
{
    //For Temperature Sensor
    ret_code_t err_code;
    //nrf_saadc_channel_config_t channel_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);
    nrf_drv_saadc_config_t saadc_config = {.resolution = NRF_SAADC_RESOLUTION_14BIT, .interrupt_priority = APP_IRQ_PRIORITY_LOW};
    nrf_saadc_channel_config_t channel_config = {.gain = NRF_SAADC_GAIN1_6, .reference = NRF_SAADC_REFERENCE_INTERNAL, .pin_p = (nrf_saadc_input_t)(NRF_SAADC_INPUT_AIN0), .pin_n = NRF_SAADC_INPUT_DISABLED, .resistor_p = NRF_SAADC_RESISTOR_DISABLED, .resistor_n = NRF_SAADC_RESISTOR_DISABLED, .mode = NRF_SAADC_MODE_SINGLE_ENDED};
    //channel_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);

    //For Battery
    nrf_saadc_channel_config_t channel_config2 = {.gain = NRF_SAADC_GAIN1_6, .reference = NRF_SAADC_REFERENCE_INTERNAL, .pin_p = (nrf_saadc_input_t)(NRF_SAADC_INPUT_VDD), .pin_n = NRF_SAADC_INPUT_DISABLED, .resistor_p = NRF_SAADC_RESISTOR_DISABLED, .resistor_n = NRF_SAADC_RESISTOR_DISABLED, .mode = NRF_SAADC_MODE_SINGLE_ENDED};
    //channel_config2 = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN4);

    err_code = nrf_drv_saadc_init(&saadc_config, saadc_callback);
    APP_ERROR_CHECK(err_code);

    //Channel 0 -> Temperature Sensor
    err_code = nrf_drv_saadc_channel_init(0, &channel_config);
    APP_ERROR_CHECK(err_code);

    //Channel 1 -> Battery
    err_code = nrf_drv_saadc_channel_init(1, &channel_config2);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER);
    APP_ERROR_CHECK(err_code);
}

//main loop in main function
for (;;){
    if(m_saadc_calibrate == true)
    {
        //printf("\r\nSAADC calibration starting...  \r\n");    //Print on UART
        while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS); //Trigger calibration task
        m_saadc_calibrate = false;
    }
    idle_state_handle();
}

Edit:

Upon more testings, I found that: 

1. Reading right after calibration is as above (both channel 0 and 1 reads the same value)

2. Readings that are done after the reading in 1. yields a rotated reading in the buffer, meaning: 

    reading is supposed to be: [channel0, channel1] but instead, I get [channel1, channel0].

Thanks!

Parents Reply Children
No Data
Related