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

Battery Voltage Calculation on pins Other than analog pins.

Hello

I am working on nrf52382,sdk11,softdevice 2.0.

/cfs-file/__key/support-attachments/beef5d1b77644c448dabff31668f3a47-fd3fce63fa6d4ddfa73158194e4d87c3/battery_5F00_pins.pdf

/cfs-file/__key/support-attachments/beef5d1b77644c448dabff31668f3a47-fd3fce63fa6d4ddfa73158194e4d87c3/battery_5F00_protection_5F00_circuit.pdf

/cfs-file/__key/support-attachments/beef5d1b77644c448dabff31668f3a47-fd3fce63fa6d4ddfa73158194e4d87c3/battery_5F00_pins_5F00_connection.pdf

 

Schematics for connection of battery with a protection circuit are in these links, Not allowed to share whole Schematics.

Using  SAADC I tried to get battery voltage on BAT_MON_EN pin with Single Ended, 10-bit resolution, internal reference and gain 1/6, observe that a constant value 02DB(784) which according to calculation gives 3.0 v

Calculation : batt_voltage = saadc_reading/((1023*(1/6))/0.6)

With the same configuration on analogs free pins AIN0 and AIN1, I was getting 96,95 as saadc_reading.

I changed the configuration to  Differential Taking BAT_MON_EN pin as positive and BATTERY pin as negative I got the result like 400 around.

Please look at the battery schematics and explain how can I get readings out of it.Which pins I need to consider.

My code snippet:

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;

        err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER);

        APP_ERROR_CHECK(err_code);

       // printf("ADC event number: %d\r\n",(int)m_adc_evt_counter);

        for (int i = 0; i < SAMPLES_IN_BUFFER; i++)

        {

        test_c.number = p_event->data.done.p_buffer[i];

        string_send(test_c.ch,4);

            //printf("%d\r\n", p_event->data.done.p_buffer[i]);

        }

//        m_adc_evt_counter++;

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_saadc_channel_config_t channel_config =

     NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(TX_PIN_NUMBER);

    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);

}

Calling of these are done when ever required with 

saadc_init();

nrf_drv_saadc_sample();

Parents Reply Children
  • Hi Ketiljo.

    As per above answer, no pins other than analog pins can give me voltage information.

    but as per schematics, Hardware engineer attached p0.08 to the voltage divider and MOSFET.  \

    On Pin BATTERY (Which is P0.08) I am trying to get voltage information.

  • P0.08 is not a analog input, there are only 8 analog inputs, AIN0 to AIN7. See the pin assignment.

  • You can get a reasonable battery measurement without an ADC, but you'll have to do some characterisation and possibly some temperature compensation; better than nothing though. Simply enable the (complicated looking) FET switches to provide the battery voltage to the pin, which is set as an input, and the battery voltage charges any distributed capacitance on the circuit connected to the pin. Then turn off the FET switches and time how long it takes for the voltage on the pin to decay to the digital input low voltage threshold (ie, 1 -> 0). Voila, an indication of battery voltage. If the numbers are too small (tiny distributed capacitance say 5pF) simply add some more - say 100pF across R29. Better still, make R29 much bigger and add some extra capacitance. In fact you could swap R28 and R29, since you no longer require a voltage divider unless you have a very high battery voltage. The slower the discharge, the more counts for a given voltage.

    // Define the on/off and sample pin and sense mask
    #define BATT_MON_ENABLE_PIN 9
    #define BATTERY_VOLTAGE_PIN 8
    #define BATTERY_VOLTAGE_PIN_MASK  (1 << BATTERY_VOLTAGE_PIN)
    
    uint32_t SampleBatteryVoltageViaChargeDischarge(uint32_t Timeout)
    {
       uint32_t myCounter = 0;
    #if 1 // Measure mode, time how long the capacitance takes to discharge
       nrf_gpio_cfg_input(BATTERY_VOLTAGE_PIN, NRF_GPIO_PIN_NOPULL);
    #else // Test timeout mode, characterise the max count
       nrf_gpio_cfg_input(BATTERY_VOLTAGE_PIN, NRF_GPIO_PIN_PULLUP);
    #endif
       // Enable the battery voltage onto the sense pin 8
       nrf_gpio_pin_set(BATT_MON_ENABLE_PIN);
       nrf_gpio_cfg_output(BATT_MON_ENABLE_PIN);
       // Charge any capacitance on the pin - there might be enough, or add few 100 pF
       nrf_delay_us(2);
       // Remove the battery voltage from the sense pin 8
       nrf_gpio_pin_clear(BATT_MON_ENABLE_PIN);
       // Wait until discharge or timeout
       while ( ((NRF_GPIO->IN & BATTERY_VOLTAGE_PIN_MASK) == BATTERY_VOLTAGE_PIN_MASK) && (myCounter < Timeout) )
       {
          myCounter++;
       }
       return myCounter;
    }
    

    For best count resolution, you need cpu cache enabled and fast clocks running.

       // If cache not enabled enable cache and hit/miss registers
       if ((NRF_NVMC->ICACHECNF & NVMC_ICACHECNF_CACHEEN_Msk) != (NVMC_ICACHECNF_CACHEEN_Enabled << NVMC_ICACHECNF_CACHEEN_Pos)){
          // Enable instruction cache (I-Cache) and cache hit & miss profiling (the latter two if required)
          NRF_NVMC->ICACHECNF = (NVMC_ICACHECNF_CACHEEN_Enabled << NVMC_ICACHECNF_CACHEEN_Pos) | (NVMC_ICACHECNF_CACHEPROFEN_Enabled << NVMC_ICACHECNF_CACHEPROFEN_Pos);
          while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
          // Enable Read-only to allow cache to operate correctly
          NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
          while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
       }
    

  • @ketiljo - I'm interested in your statement "you can measure the battery voltage directly by using the internal reference.". 

    Any chance you cold expand on that, perhaps with a code snippet or a pointer to an example? 

  • You can set the ADC directly to the VDD pin, see the figure in the SAAD overview. Set the gain to 1/6 and use the internal reference. Take a look at the proximity example in the SDK, it's used there. 

Related