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

SAADC to measure battery voltage not getting proper result

Hi,

I am new to the SAADC concept in nrf52. I have to measure the battery voltage using SAADC over ANI0 pin. I using battery with voltage 4.1 volt.

This voltage is divided using voltage divider with two resistances of 10 kohm and 3.5 kohm . I am applying 1 volt to the ANI0 pin through this network.

SAADC configuration is the default one with single ended. reference = 0.6volt gain = 1/6

So as per the formula given in datasheet,under this condition I should get ADC output as

((1*(1/6))*256)/0.6=71.11

but i am getting 4 or 5 or 6 as output on UART. following is the code,

    /* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved.
 *
 * The information contained herein is property of Nordic Semiconductor ASA.
 * Terms and conditions of usage are described in detail in NORDIC
 * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
 *
 * Licensees are granted free, non-transferable use of the information. NO
 * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
 * the file.
 *
 */

/** @file
 * @defgroup nrf_adc_example main.c
 * @{
 * @ingroup nrf_adc_example
 * @brief ADC Example Application main file.
 *
 * This file contains the source code for a sample application using ADC.
 *
 * @image html example_board_setup_a.jpg "Use board setup A for this example."
 */

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nrf.h"
#include "nrf_drv_saadc.h"
#include "boards.h"
#include "app_uart.h"
#include "app_error.h"
#include "app_util_platform.h"
#include "nrf_drv_clock.h"
#include "nrf_drv_rtc.h"
#include "nrf_delay.h"

#define UART_TX_BUF_SIZE 256 /**< UART TX buffer size. */
#define UART_RX_BUF_SIZE 1   /**< UART RX buffer size. */

#define SAMPLES_IN_BUFFER 1               //Number of SAADC samples in RAM before returning a SAADC event. Do not change this constant for low power SAADC
#define UART_PRINTING_ENABLED							//Enable to see SAADC output on UART. Comment out for low power operation.

void saadc_init(void);

volatile uint8_t state = 1;

const nrf_drv_rtc_t            rtc = NRF_DRV_RTC_INSTANCE(0); /**< Declaring an instance of nrf_drv_rtc for RTC0. */
static nrf_saadc_value_t       m_buffer_pool[2][SAMPLES_IN_BUFFER];
//static uint32_t                m_adc_evt_counter;
/**
 * @brief UART events handler.
 */
void uart_events_handler(app_uart_evt_t * p_event)
{
}

/**
 * @brief UART initialization.
 */
void uart_config(void)
{
    uint32_t                     err_code;
    const app_uart_comm_params_t comm_params =
    {
        RX_PIN_NUMBER,
        TX_PIN_NUMBER,
        RTS_PIN_NUMBER,
        CTS_PIN_NUMBER,
        APP_UART_FLOW_CONTROL_DISABLED,
        false,
        UART_BAUDRATE_BAUDRATE_Baud115200
    };

    APP_UART_FIFO_INIT(&comm_params,
                       UART_RX_BUF_SIZE,
                       UART_TX_BUF_SIZE,
                       uart_events_handler,
                       APP_IRQ_PRIORITY_LOW,
                       err_code);

    APP_ERROR_CHECK(err_code);
}

static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
{
                uint32_t err_code;
    if (int_type == NRF_DRV_RTC_INT_COMPARE0)
    {
                                saadc_init();
                                nrf_drv_saadc_sample();
                        
                                LEDS_INVERT(BSP_LED_0_MASK);        //Toggle LED1 to indicate SAADC sampling start
                        
                                err_code = nrf_drv_rtc_cc_set(&rtc,0,2000,true);
                                APP_ERROR_CHECK(err_code);
                                nrf_drv_rtc_counter_clear(&rtc);
    }
}

static void lfclk_config(void)
{
    ret_code_t err_code = nrf_drv_clock_init();
    APP_ERROR_CHECK(err_code);

    nrf_drv_clock_lfclk_request(NULL);
}

static void rtc_config(void)
{
    uint32_t err_code;

    //Initialize RTC instance
    err_code = nrf_drv_rtc_init(&rtc, NULL, rtc_handler);
    APP_ERROR_CHECK(err_code);

    //Set compare channel to trigger interrupt after COMPARE_COUNTERTIME seconds
    err_code = nrf_drv_rtc_cc_set(&rtc,0,2000,true);
    APP_ERROR_CHECK(err_code);

    //Power on RTC instance
    nrf_drv_rtc_enable(&rtc);
}


void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
    {
        ret_code_t err_code;
                        
        LEDS_INVERT(BSP_LED_1_MASK);        //Toggle LED2 to indicate SAADC sampling start			
     
        err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);
#ifdef UART_PRINTING_ENABLED
        //printf("ADC event number: %d\r\n",(int)m_adc_evt_counter);
        for (int i = 0; i < SAMPLES_IN_BUFFER; i++)
        {
            printf("%x\r\n", p_event->data.done.p_buffer[i]);
        }
        //m_adc_evt_counter++;
#endif //UART_PRINTING_ENABLED				
                                
        nrf_drv_saadc_uninit();
        NRF_SAADC->INTENCLR = (SAADC_INTENCLR_END_Clear << SAADC_INTENCLR_END_Pos);
        NVIC_ClearPendingIRQ(SAADC_IRQn);
    }
}

void saadc_init(void)
{
    ret_code_t err_code;
     //	nrf_drv_saadc_config_t saadc_config;
    nrf_saadc_channel_config_t channel_config =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);
                //Configure SAADC
    channel_config.reference = NRF_SAADC_REFERENCE_VDD4;
    err_code = nrf_drv_saadc_init(NULL, saadc_callback);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_channel_init(0, &channel_config);
    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);
}

/**
 * @brief Function for main application entry.
 */
int main(void)
{	
  LEDS_CONFIGURE(LEDS_MASK);     //Configure all leds
  LEDS_OFF(LEDS_MASK);           //Turn off all leds
  //NRF_POWER->DCDCEN = 1;         //Enabling the DCDC converter
        
#ifdef UART_PRINTING_ENABLED
    uart_config();                 //Configure UART. UART is used to show the SAADC sampled result.
    printf("\n\rSAADC HAL simple example.\r\n");	
#endif //UART_PRINTING_ENABLED	

                lfclk_config();                //Configure low frequency 32kHz clock
                rtc_config();									 //Configure RTC. The RTC will generate periodic interrupts. Requires 32kHz clock to operate.

    while(1)
    {
        nrf_delay_ms(100);
        //saadc_init();
        //nrf_drv_saadc_sample();
        __WFE();                   //These three commands disable the CPU. CPU will wake up again on any event or interrupt.
        __SEV();
        __WFE();
    }
}


/** @} */

Does i am making any mistake while doing this?? Please guide me in this regards...

  • Reference is VDD/4, so your VDD is 2.4V since the reference is 0.6V? Why don’t you use the internal reference which is 0.6V, in case the VDD is not stable. The resolution is also 10 bit so you should expect a measurement of 1 V * (1/6) / 0.6 V * 1023 ~= 284.

    Have you measured that the voltage at the AIN0 pin is indeed 1V?

    A voltage divider of 10 kohm and 3.5kohm will result in a constant current draw of 4.1 V / 13 kohm = 315uA, which is quite high for a BLE/low power application. We have a blog describing how to measure li-ion battery reliably with low power consumption (high resistor values).

  • Hi Ole,

    First of all thanks for your reply it makes me to understand concept of SAADC and the use of internal and external reference.

    Measuring 1 volt is just for understanding purpose, actually in my application I have to measure the battery voltage from 3.3 volt (below 3.3 volt my device will not work) to 4.1 volt. 3.3 volt indicates 0% and 4.1 indicates 100%.

    for 10 bit resolution if I measure the ADC value for 3.3 volt and 4.1 volt, ideally I should get following value at ADC output 3.3 * (1/6)/0.75 * 1023 ~= 750 and 4.1 * (1/6)/0.75 * 1023 ~= 932

    this is with VDD = 3 volt(hence reference = 3/4 = 0.75). That is the reason I have taken external VDD. But, Here I am getting some strange behaviour. As I goes on increasing resistance, voltage is increasing but, converted value by ADC starts decreasing. That means if I increase input voltage above 3.5 volt ADC values are decreasing. Why This is so? Is it having some connection with voltage reference chosen?

  • Maximum voltage on I/O pins on nRF52 is VDD+0.3V, see here, so you have to use voltage divider for voltages above this. If you go over VDD+0.3, ESD protection diodes starts to conduct (do a google search for ESD protection diodes) an the VDD will rise, which is probably why your readings starts to decrease.

  • Yes, you are right thanks for your reply now i am using voltage divider and getting proper results..... Thanks once again!!!

  • Hi Ole,

    When i am combining same SAADC code with BLE , callback function of SAADC is not being called... Can you suggest me the solution to this....or any thread related to this

Related