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

Battery level measurement without SAADC

Hi!

The nRF52820 chip does not have the SAADC module used to estimate the battery life in its larger counterparts (nrf52840 and nrf52833).
Is there a different way to estimate battery life without an SAADC?
I'm hoping for a method which does not include adding external components, and is measurement based rather then estimation based.

We had considered an estimation method which is a summation of the current estimate per 'time used', 'advertisement cycles' passed, 'connection cycles passed' etc. The problem here is that a. I would need to estimate all possible parameters (TX power for example, time until advertisement is caught etc)
b. I would not be able to catch leaking parts, as the leak is not part of my estimation.

Adding an external ADC is a possibility, but would require us to change our PCB which is not something we want.

we're using the VDD tied to VDDH and expect the Vin to be 1.8-3.3V.

Any other ideas?

  • Actually there is no external circuity required. One can do an approximation using the hysteresis ladder feature of the COMP peripheral. The nrf52820 can compare VDDH/5 against fractions of the internal 1.2 V reference per the crude unoptimized approach below

    uint32_t get_battery_voltage1()
    {
          uint32_t ladder = 1; //approximate from below
          nrfx_comp_config_t config = {
              .reference = NRF_COMP_REF_Int1V2, // use 1.2 V reference
              .ext_ref = NRF_COMP_EXT_REF_0,
              .main_mode = NRF_COMP_MAIN_MODE_SE,
              .threshold = {.th_up = ladder, .th_down = ladder-1},
              .speed_mode = NRF_COMP_SP_MODE_Normal,
              .hyst = NRF_COMP_HYST_NoHyst,
              .input = NRF_COMP_VDDH_DIV5, // 
              .interrupt_priority = NRFX_COMP_CONFIG_IRQ_PRIORITY};
          nrfx_comp_init(&config, comp_handler1); // no handler is used but the API requires a blank handler
    
          nrf_comp_event_clear(NRF_COMP_EVENT_READY);
          nrfx_comp_start(0, 0);
          while(nrf_comp_event_check(NRF_COMP_EVENT_READY) == 0);
          uint32_t sample = nrfx_comp_sample();
          while (sample == 1) {   // VDDH/5 is higher than VUP
              ladder++;           // increase ladder until VDDH/5 is larger than VUP
              nrf_comp_th_t th = {
                .th_up = ladder,
                .th_down = ladder -1
              };
              nrfx_comp_stop();   // update the COMP
              nrf_comp_disable();
              nrf_comp_th_set(th);
              nrf_comp_enable();
              nrf_comp_event_clear(NRF_COMP_EVENT_READY);
              nrfx_comp_start(0, 0);
              while(nrf_comp_event_check(NRF_COMP_EVENT_READY) == 0);
              sample = nrfx_comp_sample();  // sample again
          }
          nrfx_comp_uninit();
          uint32_t volt = (3000 * (ladder+1))/32;       // the hysteresis ladder is (th_up+1)*VREF (1.2)
          return volt;
    }

    With this you can approximate VDDH from 0 to 6V in0.09375V increments

  • Use the following code to measure the battery voltage on the nRF52820 without any external components

    It essentially implements a successive approximation of Vin using the COMP device with the internal VDDH/5 source and modifying the hysteresis threshold:

    void comp_handler1(nrf_comp_event_t event)
    {
    }
    
    uint32_t get_battery_voltage()
    {
          uint32_t ladder = 0; //approximate from below
          uint32_t current = 32;
          nrfx_comp_config_t config = {
              .reference = NRF_COMP_REF_Int1V2, // use 1.2 V reference
              .ext_ref = NRF_COMP_EXT_REF_0,
              .main_mode = NRF_COMP_MAIN_MODE_SE,
              .threshold = {.th_up = ladder+1, .th_down = ladder},
              .speed_mode = NRF_COMP_SP_MODE_Normal,
              .hyst = NRF_COMP_HYST_NoHyst,
              .input = NRF_COMP_VDDH_DIV5, // this mode is unique to the 52820
              .interrupt_priority = NRFX_COMP_CONFIG_IRQ_PRIORITY};
          nrfx_comp_init(&config, comp_handler1); // no handler is used but the API requires a blank handler
    
          while (current > 0) {   
              ladder = ladder | current;         // successive approximation
              nrf_comp_th_t th = {
                .th_up = ladder,
                .th_down = ladder 
              };
              nrfx_comp_stop();   // update the COMP
              nrf_comp_disable();
              nrf_comp_th_set(th);
              nrf_comp_enable();
              nrf_comp_event_clear(NRF_COMP_EVENT_READY);
              nrfx_comp_start(0, 0);
              while(nrf_comp_event_check(NRF_COMP_EVENT_READY) == 0);
              uint32_t sample = nrfx_comp_sample();  // sample 
              if (!sample) { // VDDH/5 is less than VUP
                  ladder = ladder & (~current);  // reset the MSB and continue with lower bits
              }
              current = current >> 1;
          }
          nrfx_comp_uninit();
          uint32_t volt = (3000 * (ladder+3))/32;       // the hysteresis ladder is (th_up+1)*VREF (1.2)
                                                        // theoretically this should be latter+1 - +3 is a fudge factor adjustment
          return volt;
    }

  • Thank you Johannes!

    I'll have a look and see if I can make these suggestions work.

  • 逐次逼近法是否需要修改?因为我用了之后不准确,请问uint32_t volt = (3000 * (ladder+3))/32有什么解释吗?

  • How accurate do you need it? The resolution of this ladder is only 64 bit so it will be rather coarse.

    The formula is explained like this:

    the input is VDDH/5. so 3 V VDDH will be 0.6 V. the comparison value is 1.2 V so at 3 V the approximation should stop yield 32 (as 1.2 V will be 63). so (3000 * ladder) /32 should give the approximate VDDH in mV

    As we are approximating from the bottom up, one should add +1 do the value. I added +3. as empirically this gave me the closest match to the 3V reference power. Adjusting the VDD up and down from there will definitely change the output of the approximation. I cannot say that it is particularly linear or accurate. But in my application it is enough to give a reasonable battery gauge.

Related