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