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

I would like to measure lithium batteries more accurately.

Hello,

I'm measuring the value of a lithium battery (3.7V 110mAh).
I used SAADC to measure the battery power and it seems to be quite accurate. The charge IC also attempts to measure the remaining battery power even when the battery is being charged.

Similarly, although measured by SAADC, the adc value is almost constant, making it difficult to measure the remaining battery capacity.

This is a measure of battery value from 0% to 100%.

1) Battery charging: 580~600 (ADC value)
2) Battery not charging: 360~420 (ADC value)

The battery charges at 5V and initially the value is around 580 but quickly rises to 600 and almost stays at that value.

Code to measure the value of the battery.

void timer_handler(nrf_timer_event_t event_type, void * p_context)
{

}

#define ADC_TIME    250 //1sec bat led timer

void saadc_sampling_event_init(void) //SAADC example Reference
{
    ret_code_t err_code;

    //timer setting
    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
    err_code = nrf_drv_timer_init(&m_timer, &timer_cfg, timer_handler);
    APP_ERROR_CHECK(err_code);

    /* setup m_timer for compare event every 250ms */
    uint32_t ticks = nrf_drv_timer_ms_to_ticks(&m_timer, ADC_TIME);
    nrf_drv_timer_extended_compare(&m_timer,
                                   NRF_TIMER_CC_CHANNEL0, //timer channel
                                   ticks, //ADC_TIME
                                   NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, //Shortcut for clearing the timer based on compare 0.
                                   false);
    nrf_drv_timer_enable(&m_timer); // <-> nrf_drv_timer_disable(&m_timer);

    uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer,
                                                                                NRF_TIMER_CC_CHANNEL0);
    uint32_t saadc_sample_task_addr   = nrf_drv_saadc_sample_task_get();


    /* setup ppi channel so that timer compare event is triggering sample task in SAADC */
    err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_ppi_channel_assign(m_ppi_channel,
                                          timer_compare_event_addr,
                                          saadc_sample_task_addr);
    APP_ERROR_CHECK(err_code);
}


void saadc_sampling_event_enable(void)
{
    ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);

    APP_ERROR_CHECK(err_code);
}


//battery level range(%) (TEST)

//Charging (TEST),  Measurements are not clear.
#define BAT_ADC0_100 600 //MAX 
#define BAT_ADC0_75  595
#define BAT_ADC0_50  590
#define BAT_ADC0_0   580 //MIN 


//No charging 
#define BAT_ADC1_100 420 //MAX
#define BAT_ADC1_75  405  
#define BAT_ADC1_50  390  
#define BAT_ADC1_0   360 //MIN 

int charging_state = 0; //Used to prevent repeated output of CenterLED_timers_start.

void turn_on_batter_level_led(int16_t bat_level) //battery LED
{ 
   //int charging_state = 0;
   ret_code_t err_code;

   int charge_state = nrf_gpio_pin_read(bat_state); //read charge state
   //printf("%d\n", charge_state); //charging : 0, no charging : 1
   printf("battery : %u \n", bat_level); //%u : unsigned decimal number

   if(App_Left == 1 || App_Right == 1 || App_Crush == 1) //LED is runnning
   {
     printf("Wait!! \n");
   }
  
   else //LED is no runnning
   {

   //int charge_state = nrf_gpio_pin_read(bat_state); //read charge state

   //printf("%d\n", charge_state); //charging : 1, no charging : 0

   if(charge_state == 0) //charging
   {   
      charging_state = 0;

      printf("Charging...\n");

      //stop center LED, start Battery LED
      CenterLED_timers_stop();

      if(bat_level >= BAT_ADC0_75) { // over 75%, 595
        printf("over 75persent \n"); //led1,2,3 on

        nrf_gpio_pin_toggle(BAT_LED_1);
        nrf_gpio_pin_toggle(BAT_LED_2);
        nrf_gpio_pin_toggle(BAT_LED_3);


      else if (BAT_ADC0_75 > bat_level >= BAT_ADC0_50) { //over 50%, 590~595
        printf("over 50persent \n"); //led1,2 on

        nrf_gpio_pin_toggle(BAT_LED_1);
        nrf_gpio_pin_toggle(BAT_LED_2);

        nrf_gpio_pin_clear(BAT_LED_3); //bat_led3 off
      }
  
      else if (bat_level < BAT_ADC0_50) { //under 50%, 580
       printf("under 50persent \n"); //led1 on

       nrf_gpio_pin_toggle(BAT_LED_1);

       nrf_gpio_pin_clear(BAT_LED_2); //bat_led2 off
       nrf_gpio_pin_clear(BAT_LED_3);  //bat_led3 off
      }
   }


    else //no charging
    {
      printf("Full charge or No charging\n");

      if(bat_level >= BAT_ADC1_75) 
      {
        printf("over 75persent \n");
      }

      else if(BAT_ADC1_75 > bat_level >= BAT_ADC1_50) 
      {
        printf("over 50persent \n");
      }
           
      else if(bat_level < BAT_ADC1_50) 
      {
        printf("under 50persent \n");
      }
              
                  
      //charging_state = 0;
      if(charging_state == 0)
      {
        All_Led_Off(); //BAT_LED 1~5 Off

        if(pairing_state == true) //In bluetooth pairing
        {
          CenterLED_timers_start(); //LED1,2,3 toggle repeate timer
        }

          charging_state = 1; //Used to prevent repeated output of CenterLED_timers_start.
          //nrf_delay_ms(100);
          printf("charging_state : %d\n", charging_state); //check charging_state

        if(charging_state == 1) //No LED activity before Bluetooth connection
        {
          //printf("All_Stop\n");
          //CenterLED_timers_stop();
          All_Stop(); //center timer stop & LED Off
        }
      }
    }


    //App battery in charging
   if(charge_state == 0) //charging
   { 
        if(bat_level >= BAT_ADC0_75) { 
          char bat_array1[] = "over 75";
          uint16_t bat_length1 = sizeof(bat_array1);

           err_code = ble_nus_data_send(&m_nus, &bat_array1, &bat_length1, m_conn_handle);
           if ((err_code != NRF_ERROR_INVALID_STATE) && 
                              (err_code != NRF_ERROR_RESOURCES) &&
                              (err_code != NRF_ERROR_NOT_FOUND))
                             {
                                APP_ERROR_CHECK(err_code);
                             }
      }


      if(BAT_ADC0_75 > bat_level >= BAT_ADC0_50) { 
          char bat_array2[] = "over 50";
          uint16_t bat_length2 = sizeof(bat_array2);

           err_code = ble_nus_data_send(&m_nus, &bat_array2, &bat_length2, m_conn_handle);
           if ((err_code != NRF_ERROR_INVALID_STATE) && 
                             (err_code != NRF_ERROR_RESOURCES) &&
                             (err_code != NRF_ERROR_NOT_FOUND))
                             {
                               APP_ERROR_CHECK(err_code);
                             }
     }

        if(bat_level < BAT_ADC0_50) {  
        char bat_array3[] = "under 50";
        uint16_t bat_length3 = sizeof(bat_array3);

          err_code = ble_nus_data_send(&m_nus, &bat_array3, &bat_length3, m_conn_handle);
           if ((err_code != NRF_ERROR_INVALID_STATE) && 
                              (err_code != NRF_ERROR_RESOURCES) &&
                              (err_code != NRF_ERROR_NOT_FOUND))
                             {
                                APP_ERROR_CHECK(err_code);
                             }
       }
  }


    //App battery in No charging
    else
    {
      if(bat_level >= BAT_ADC1_75) { //405
          char bat_array1[] = "over 75";
          uint16_t bat_length1 = sizeof(bat_array1);

           err_code = ble_nus_data_send(&m_nus, &bat_array1, &bat_length1, m_conn_handle);
           if ((err_code != NRF_ERROR_INVALID_STATE) && 
                              (err_code != NRF_ERROR_RESOURCES) &&
                              (err_code != NRF_ERROR_NOT_FOUND))
                             {
                                APP_ERROR_CHECK(err_code);
                             }
      }


      if(BAT_ADC1_75 > bat_level >= BAT_ADC1_50) { //390
          char bat_array2[] = "over 50";
          uint16_t bat_length2 = sizeof(bat_array2);

           err_code = ble_nus_data_send(&m_nus, &bat_array2, &bat_length2, m_conn_handle);
           if ((err_code != NRF_ERROR_INVALID_STATE) && 
                             (err_code != NRF_ERROR_RESOURCES) &&
                             (err_code != NRF_ERROR_NOT_FOUND))
                             {
                               APP_ERROR_CHECK(err_code);
                             }
     }

        if(bat_level < BAT_ADC1_50) {   //360 
        char bat_array3[] = "under 50";
        uint16_t bat_length3 = sizeof(bat_array3);

          err_code = ble_nus_data_send(&m_nus, &bat_array3, &bat_length3, m_conn_handle);
           if ((err_code != NRF_ERROR_INVALID_STATE) && 
                              (err_code != NRF_ERROR_RESOURCES) &&
                              (err_code != NRF_ERROR_NOT_FOUND))
                             {
                                APP_ERROR_CHECK(err_code);
                             }
       }
    }
  }
}


void saadc_callback(nrf_drv_saadc_evt_t const * p_event) //battery level
{ //battery A0 pin (002)
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
    {
        ret_code_t err_code;
        int16_t bat_level = 0;

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

        int i;
        NRF_LOG_INFO("ADC event number: %d", (int)m_adc_evt_counter);

        for (i = 0; i < SAMPLES_IN_BUFFER; i++)
        {
            NRF_LOG_INFO("%d", p_event->data.done.p_buffer[i]);
            bat_level += p_event->data.done.p_buffer[i];
        }
        m_adc_evt_counter++;
        bat_level /= SAMPLES_IN_BUFFER; //bat_level = bat_level / SAMPLES_IN_BUFFER

        turn_on_batter_level_led(bat_level);
    }
}


void saadc_init(void)
{
    ret_code_t err_code;
    nrf_saadc_channel_config_t channel_config =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0); //A0 pin

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

static void adc_configure(void)
{
    saadc_init();
    saadc_sampling_event_init();
    saadc_sampling_event_enable();
}


int main()
{
    adc_configure();
}

Is there a way to measure the remaining battery value accurately when charging the battery through the charging IC?


Thank you!

Parents
  • The charge IC also attempts to measure the remaining battery power even when the battery is being charged.

    Similarly, although measured by SAADC, the adc value is almost constant, making it difficult to measure the remaining battery capacity.

     Can you rephrase this statement, I did not understand. 
     

    This is a measure of battery value from 0% to 100%.

    1) Battery charging: 580~600 (ADC value)
    2) Battery not charging: 360~420 (ADC value)

     With your SAADC configuration, I assume you have a gain of 1/6:

    Battery charging: (7.2V/1024)* 600 = 4.2V, typical charging voltage. 

    Battery not charging: (7.2V/1024)*420 = 3.0V, way less than the expected 3.7-4.2V. Do you have the datasheet of the battery? 

    Your schematic does not show what it is that you're actually measuring, but I assume Vbat and Vin are connected somewhere else in your schematics. 

     

    Is there a way to measure the remaining battery value accurately when charging the battery through the charging IC?

    I suggest you ask TI that question as well, given that they are the ones supporting their PMIC. 


Reply
  • The charge IC also attempts to measure the remaining battery power even when the battery is being charged.

    Similarly, although measured by SAADC, the adc value is almost constant, making it difficult to measure the remaining battery capacity.

     Can you rephrase this statement, I did not understand. 
     

    This is a measure of battery value from 0% to 100%.

    1) Battery charging: 580~600 (ADC value)
    2) Battery not charging: 360~420 (ADC value)

     With your SAADC configuration, I assume you have a gain of 1/6:

    Battery charging: (7.2V/1024)* 600 = 4.2V, typical charging voltage. 

    Battery not charging: (7.2V/1024)*420 = 3.0V, way less than the expected 3.7-4.2V. Do you have the datasheet of the battery? 

    Your schematic does not show what it is that you're actually measuring, but I assume Vbat and Vin are connected somewhere else in your schematics. 

     

    Is there a way to measure the remaining battery value accurately when charging the battery through the charging IC?

    I suggest you ask TI that question as well, given that they are the ones supporting their PMIC. 


Children
  • Hello, haakonsh.

    The person who made this board says that because of the addition of MOSFET to the battery connection, the power from the battery could be lower. (I don't know much about hardware, so I don't understand.)
    So if run the board with this battery, I can see that the LEDs on the board are definitely weak.

    Is the battery voltage inhibited affecting the battery value?

    Thank you.

  • If there's a 0.7-0.8V diode drop between the battery and the analog input then that would explain a lot. Do you mind sharing schematics for anything related to this issue?

    Either way you're measuring two different voltage sources, battery voltage under load and the battery's charging voltage. You probably can't measure the state of charge by sampling the battery's charging voltage, that's why you need to talk to TI. Most likely you will need to monitor one or more pins of the PMIC, if at all possible. 

Related