Измерение уровня заряда аккумулятора на микроконтроллере

           При разработке устройств с аккумуляторным питанием важно предусмотреть возможность отслеживать степень заряженности батареи. В рамках этой статьи рассмотрим один из вариантов контроля заряда литий-полимерного (Li-Pol) аккумулятора при помощи микроконтроллера Arduino.

           Предположим, что разрабатывается портативное устройство с аккумуляторным питанием (литий-полимерный 3,7 В) на базе микроконтроллера (МК) ATmega328P-PU в корпусе DIP-28 (как на некоторых версиях отладочных плат Arduino UNO). В соответствии с документацией к микросхеме, у неё широкий диапазон напряжения питания (1,8…5,5 В). Таким образом, МК можно запитывать напрямую от батареи.

           Тут стоит обратить внимание, что, согласно datasheet’у на МК ATmega328P, при уровне питания 2,7…5,5 вольт необходимо применить внешний кварцевый резонатор с тактовой частотой не выше 10 МГц.

           При работе со встроенным в контроллер аналого-цифровым преобразователем (АЦП) есть вариант использовать внутренний источник опорного напряжения (ИОН), уровень которого, как правило, равен величине питания. Но в нашем случае этот вариант не подходит, поскольку питание всё время меняется в зависимости от текущего вольтажа на батарее.

           Поэтому нужно использовать другой вариант ИОН — внешний. Для этого у МК есть специальный вывод — AREF (пин №21 для ATmega328P). Для того, чтобы его активировать, в коде программы необходимо прописать следующую строчку в рамках функции setup:

// активация входа для внешнего источника опорного напряжения
analogReference(EXTERNAL);

           Так как питание зависит от заряда батареи (в диапазоне 3,2…4,2 В), то логично выбрать значение опорного напряжения Uопорное заведомо более низкое — величиной 2,5 В. А в качестве ИОН на такой уровень можно воспользоваться специализированными микросхемами, которые представляют собой прецизионные стабилизаторы (например, REF192 или AD680). Но для эксперимента мы воспользуемся более простым вариантом — программируемым стабилитроном TL431.

Рисунок 1 — Условное графическое обозначение и цоколёвка программируемого стабилитрона TL431

           Он отличается от обычных тем, что при подключении к нему подстроечного резистора можно менять величину напряжения стабилизации. Но в нашем случае достаточно базовой схемы включения, которая по умолчанию выдаёт как раз 2,5 В:

Рисунок 2 — Базовая схема включения программируемого стабилитрона TL431

           Как видно из схемы, при объединении управляющего электрода и катода (1-я и 3-я ножки микросхемы) на выходе всегда стабильные 2,5 вольта. Токозадающий резистор подбирается исходя из того, что для надёжной работы микросхемы нужен ток стабилизации примерно 10 мА.

           Далее необходимо подключить вход АЦП к батарее. В нашем случае просто так напрямую подключать нельзя, поскольку мы выбрали уровень Uопорное величиной 2,5 В (то есть мы сможем измерять только в диапазоне 0…2,5 В). Следовательно, нам нужно масштабировать напряжение с батареи, приемлемое для АЦП. То есть максимальный вольтаж на элементе питания при 100 % заряде (4,2 В) должно соответствовать величине не выше Uопорное = 2,5 В. Чтобы достичь этого, воспользуемся резистивным делителем. Номиналы сопротивления резисторов — десятки килоом, чтобы излишне не нагружать элемент питания:

Рисунок 3 — Измерительный резистивный делитель

           Выбранные номиналы (10 кОм и 13 кОм) дают расчётный коэффициент деления 1,769. То есть, когда на батарее 4,2 вольта, на входе АЦП будет примерно 2,4 вольта.

           Теперь необходимо определиться с тем, как соотнести напряжение на Li-Pol аккумуляторе с его степенью заряженности. Ниже в табличной форме представлена данная зависимость:

           Эти значения были определены эмпирическим путём, поэтому могут быть не совсем точными: в разных открытых источниках данные несколько отличаются друг от друга. Но в приведённой таблице довольно много значений, по которым можно построить график зависимости. Соответственно, представим эту зависимость в виде формулы, которую в дальнейшем будем использовать для пересчёта напряжения на батарее в величину заряда.

измерение уровня заряда аккумулятора на микроконтроллере Arduino
Рисунок 4 — График зависимости уровня заряда от вольтажа на Li-Pol аккумуляторе

           Как видно по графику, зависимость нелинейная, особенно на малых уровнях заряда. Поэтому, чтобы функция как можно точнее преобразовывала напряжение в проценты (в наиболее интересующем нас диапазоне 20…100 %), представим её в виде полинома 4-й степени:

где y —  степень заряженности в процентах;

x — вольтаж на аккумуляторе.


Схема

           Ниже представлены тестовая схема и фотография собранного макета. Для наглядности индикация заряда осуществляется на OLED-дисплее.

измерение уровня заряда аккумулятора на микроконтроллере Arduino
Рисунок 5 — Тестовая схема контроля и индикации заряда аккумулятора на Arduino UNO
измерение уровня заряда аккумулятора на микроконтроллере Arduino
Рисунок 6 — Собранный макет

           Разумеется, коэффициент преобразования резистивного делителя необходимо определять опытным путём (строить калибровочную характеристику). Но для эксперимента калибровка была произведена по одной точке, и получен коэффициент 1,778.


Скетч для Arduino

           Ниже представлен подробно прокомментированный полный листинг скетча. Индикация степени заряженности осуществляется через OLED-дисплей и монитор порта.

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);


float voltage = 0;		// напряжение на выходе резистивного делителя напряжения
float battery_voltage = 0;	// напряжение на аккумуляторе
float koeff_devider = 1.778;	// коэффициент деления резистивного делителя напряжения
float volt_ref = 2.49;		// уровень опорного напряжения с выхода TL431
float pwr = 0;			// уровень заряда аккумулятора в процентах (%)



void setup() {
  
  Serial.begin(9600);
  analogReference(EXTERNAL);	// выбор внешнего источника опорного напряжения для АЦП

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
    Serial.println("SSD1306 allocation failed");
    for(;;);
  }

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);	// указываем адрес устройства на шине
  display.clearDisplay();			// очистка дисплея
  display.setTextSize(2, 2);			// указываем размер шрифта
  display.setTextColor(SSD1306_WHITE); 		// указываем цвет надписи

  Serial.println("================================================");
  
}



void loop() {

  delay(1000);


  // измерение напряжения с повышенной точностью 
  // с использованием метода вычисления среднего арифметического
  int analog_data = 0;	// данные от АЦП
  long amount = 0;	// сумма всех данных, поучаемых от АЦП
  long arith_mean = 0;	// среднее арифметическое
  int i_size = 100;	// количество измерений

  for (int i = 0; i < i_size; i++)
  {
    analog_data = analogRead(0);	// считывание данных с канала A0 АЦП
    amount = amount + analog_data;		// суммирование всех данных от АЦП
  }

  arith_mean = amount/i_size;	// вычисление среднего арифметического по всем измерениям

  voltage = (arith_mean * volt_ref)/1023;	// вычисляем напряжение на выходе резистивного делителя	


  // умножаем измеренное напряжение на коэффициент резистивного делителя
  // получаем напряжение на входе делителя - то есть на самом аккумуляторе
  battery_voltage = voltage * koeff_devider;
  

  Serial.print("Voltage on battery: ");
  Serial.print(battery_voltage);
  Serial.println(" V");


  // рассчёт уровня заряда аккумулятора (в процентах) по формуле
  // каждое слагаемое вычислим как отдельную переменную

  float summand_1 = 441.59 * battery_voltage * battery_voltage * battery_voltage * battery_voltage;
  float summand_2 = 7175.9 * battery_voltage * battery_voltage * battery_voltage;
  float summand_3 = 43463 * battery_voltage * battery_voltage;
  float summand_4 = 116155 * battery_voltage;
  float summand_5 = 115504;

  // формула	y=(441,59∙x^4 )-(7175,9∙x^3 )+(43463∙x^2 )-(116155∙x)+115504
  pwr = summand_1 - summand_2 + summand_3 - summand_4 + summand_5;

  
  Serial.print("Battery charge level: ");
  Serial.print(pwr);
  Serial.println(" %");
  Serial.println(" ");

  // индикация уровня заряда аккумулятора на OLED-дисплее
  display.clearDisplay();
  display.setCursor(0, 0);
  
  int pwr_round = round(pwr);	// округляем значение уровня заряда до целого числа
 
  display.print("pwr: ");
  display.print(pwr_round); 
  display.println(" %");
  display.display();

}

           Теперь можно прошивать отладочную плату и наблюдать за показаниями:

измерение уровня заряда аккумулятора на микроконтроллере Arduino

           На фото выше мультиметр показывает вольтаж на самой батарее. При этом на OLED-дисплее отображается 57 %. Можно проверить точность нашей измерительной системы, подставив значение напряжения, измеренного мультиметром, в формулу. В результате получим примерно 55 %. Такое отклонение, скорее всего, связано с не совсем точными коэффициентом преобразования резистивного делителя и величиной Uопорное,  а также неидеальной формулой пересчёта.


Выводы

           Предложенное схемотехническое и программное решение позволяет просто и эффективно контролировать состояние элемента питания в портативных микроконтроллерных устройствах.