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

SAADC VDD measurement offset

Hi, I added the SAADC feature to a beacon project so I can measure the battery voltage. Interesting thing is that my readings always show around 100mV less then VDD voltage realy is. Now I am not sure is there some voltage drop inside of a chip or I am not reading correct value. 

The code for configuring SAADC:

#define UART_PRINTING_ENABLED
#define SAADC_CALIBRATION_INTERVAL 5              //Determines how often the SAADC should be calibrated relative to NRF_DRV_SAADC_EVT_DONE event. E.g. value 5 will make the SAADC calibrate every fifth time the NRF_DRV_SAADC_EVT_DONE is received.
#define SAADC_SAMPLES_IN_BUFFER 4                 //Number of SAADC samples in RAM before returning a SAADC event. For low power SAADC set this constant to 1. Otherwise the EasyDMA will be enabled for an extended time which consumes high current.
#define SAADC_OVERSAMPLE NRF_SAADC_OVERSAMPLE_4X  //Oversampling setting for the SAADC. Setting oversample to 4x This will make the SAADC output a single averaged value when the SAMPLE task is triggered 4 times. Enable BURST mode to make the SAADC sample 4 times when triggering SAMPLE task once.
#define SAADC_BURST_MODE 1                        //Set to 1 to enable BURST mode, otherwise set to 0.

#define ADC_REF_VOLTAGE_IN_MILLIVOLTS 600
#define ADC_RES_12BIT                 4095
#define ADC_PRE_SCALING_COMPENSATION  6
#define ADC_OFFSET                    100

#define ADC_RESULT_IN_MILLI_VOLTS(ADC_VALUE)\
        ((((ADC_VALUE) * ADC_REF_VOLTAGE_IN_MILLIVOLTS) / ADC_RES_12BIT) * ADC_PRE_SCALING_COMPENSATION) + ADC_OFFSET


void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
    ret_code_t err_code;
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)                                                        //Capture offset calibration complete event
    {
			
        //LEDS_INVERT(BSP_LED_1_MASK);                                                                    //Toggle LED2 to indicate SAADC buffer full		

        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
        }
        

#ifdef UART_PRINTING_ENABLED
        NRF_LOG_INFO("ADC event number: %d\r\n",(int)m_adc_evt_counter);                                //Print the event number on UART

        for (int i = 0; i < p_event->data.done.size; i++)
        {
            NRF_LOG_INFO("%d\r\n", ADC_RESULT_IN_MILLI_VOLTS(p_event->data.done.p_buffer[i]));                                     //Print the SAADC result on UART
        }
#endif //UART_PRINTING_ENABLED    
        advertising_stop();
        //NRF_LOG_INFO("%d, %d, \r\n", ADC_RESULT_IN_MILLI_VOLTS(p_event->data.done.p_buffer[0]), (ADC_RESULT_IN_MILLI_VOLTS(p_event->data.done.p_buffer[0]))/16);
        advertising_parameters_battery_update((ADC_RESULT_IN_MILLI_VOLTS(p_event->data.done.p_buffer[0]))/16);
        advertising_start();

        if(m_saadc_calibrate == false)
        {
            err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAADC_SAMPLES_IN_BUFFER);             //Set buffer so the SAADC can write to it again. 
            APP_ERROR_CHECK(err_code);
        }
        
        m_adc_evt_counter++;
  
    }
    else if (p_event->type == NRF_DRV_SAADC_EVT_CALIBRATEDONE)
    {
        LEDS_INVERT(BSP_LED_2_MASK);                                                                    //Toggle LED3 to indicate SAADC calibration complete
        
        err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAADC_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], SAADC_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);
        
#ifdef UART_PRINTING_ENABLED
        NRF_LOG_INFO("SAADC calibration complete ! \r\n");                                              //Print on UART
#endif //UART_PRINTING_ENABLED	
        
    }
}


void saadc_init(void)
{
    ret_code_t err_code;
    nrf_drv_saadc_config_t saadc_config;
    nrf_saadc_channel_config_t channel_config;


	
    //Configure SAADC
    saadc_config.low_power_mode = true;                                                   //Enable low power mode.
    saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT;                                 //Set SAADC resolution to 12-bit. This will make the SAADC output values from 0 (when input voltage is 0V) to 2^12=2048 (when input voltage is 3.6V for channel gain setting of 1/6).
    saadc_config.oversample = SAADC_OVERSAMPLE;                                           //Set oversample to 4x. This will make the SAADC output a single averaged value when the SAMPLE task is triggered 4 times.
    saadc_config.interrupt_priority = APP_IRQ_PRIORITY_LOW;                               //Set SAADC interrupt to low priority.
	
    //Initialize SAADC
    err_code = nrf_drv_saadc_init(&saadc_config, saadc_callback);                         //Initialize the SAADC with configuration and callback function. The application must then implement the saadc_callback function, which will be called when SAADC interrupt is triggered
    APP_ERROR_CHECK(err_code);
		
    //Configure SAADC channel
    channel_config.reference = NRF_SAADC_REFERENCE_INTERNAL;                              //Set internal reference of fixed 0.6 volts
    channel_config.gain = NRF_SAADC_GAIN1_6;                                              //Set input gain to 1/6. The maximum SAADC input voltage is then 0.6V/(1/6)=3.6V. The single ended input range is then 0V-3.6V
    channel_config.acq_time = NRF_SAADC_ACQTIME_10US;                                     //Set acquisition time. Set low acquisition time to enable maximum sampling frequency of 200kHz. Set high acquisition time to allow maximum source resistance up to 800 kohm, see the SAADC electrical specification in the PS. 
    channel_config.mode = NRF_SAADC_MODE_SINGLE_ENDED;                                    //Set SAADC as single ended. This means it will only have the positive pin as input, and the negative pin is shorted to ground (0V) internally.
    channel_config.pin_p = SAADC_CH_PSELP_PSELP_VDD;                                          //Select the input pin for the channel. AIN0 pin maps to physical pin P0.02.
    channel_config.pin_n = NRF_SAADC_INPUT_DISABLED;                                      //Since the SAADC is single ended, the negative pin is disabled. The negative pin is shorted to ground internally.
    channel_config.resistor_p = NRF_SAADC_RESISTOR_DISABLED;                              //Disable pullup resistor on the input pin
    channel_config.resistor_n = NRF_SAADC_RESISTOR_DISABLED;                              //Disable pulldown resistor on the input pin

	
    //Initialize SAADC channel
    err_code = nrf_drv_saadc_channel_init(0, &channel_config);                            //Initialize SAADC channel 0 with the channel configuration
    APP_ERROR_CHECK(err_code);
		
    if(SAADC_BURST_MODE)
    {
        NRF_SAADC->CH[0].CONFIG |= 0x01000000;                                            //Configure burst mode for channel 0. Burst is useful together with oversampling. When triggering the SAMPLE task in burst mode, the SAADC will sample "Oversample" number of times as fast as it can and then output a single averaged value to the RAM buffer. If burst mode is not enabled, the SAMPLE task needs to be triggered "Oversample" number of times to output a single averaged value to the RAM buffer.		
    }

    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0],SAADC_SAMPLES_IN_BUFFER);    //Set SAADC buffer 1. The SAADC will start to write to this buffer
    APP_ERROR_CHECK(err_code);

    
    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1],SAADC_SAMPLES_IN_BUFFER);    //Set SAADC buffer 2. The SAADC will write to this buffer when buffer 1 is full. This will give the applicaiton time to process data in buffer 1.
    APP_ERROR_CHECK(err_code);

}

Every minute I have timmer that starts the SAADC with function: nrf_drv_saadc_sample(). If my VDD is 3V I get the value ~2900mV. 
As you can see from the code snippet I added an OFFSET of 100mV to the macro ADC_RESULT_IN_MILLI_VOLTS. 

Is there a configuration problem or this is an internal hardware thing?

Parents
  • Hi,

    It looks like the code is correct. Do you have a stable supply voltage when it's sampled, or is it battery-powered, and Is the 3V VDD voltage measured on the input? The 52 DK has a reverse voltage protection diode that will drop the voltage by about 300 mV.

    Best regards,

    Vidar  

  • Hi Vidar,

    I am Otii solution for powering the project. I am quite sure it is providing the correct voltage and also I did the measurement and got 3V. 

    I am not using 52 DK to for testing and developing instead, I have custom board with a minimum number of components based on module ISP1507-AL (nRF52810-CAAA chipset). The power pins are directly connected to the power supply and in the picture below you can see the internal schematic of the module. 

    So from this, I do not see any "obstacles" that would lower the voltage before the MCU.
    I will also try this on the 52 DK to be sure that it is not hardware related. 

    Also, low power SAADC mode is enabled.

    Let me know what do you think.

    Best regards,
    Vojislav. 

  • Hi Vidar, 

    I run the same code on the 52 DK board and got good results. I measured 2.872V on the connector P22 when the USB is powering the dev board and got results (without my offset in the calculation): 2850 to 2862 (mV). I can assume this is correct because there is an error in SAADC due to many factors. 

    From this, it seems that the problem is with hardware (probably internal module components). 

    I will investigate this furthermore. 

  • Hi Vojislav,

    Thank you for the update. So it appears t be hw related as you said. Maybe the supply voltage drops under load? Maybe you have done it already, but I would have tried to verify with the voltage on the input, preferably with an oscilloscope. 

Reply Children
No Data
Related