Контроль аккумуляторной батареи от которой питается плата Arduino и остальная нагрузка!

Тема в разделе "Силовая электроника", создана пользователем issaom, 10 дек 2016.

  1. issaom

    issaom Гик

    Возникла потребность контролировать напряжение аккумуляторной батареи у автономно работающей конструкции под управлением Arduino. Применять дополнительные модули и платы мне не захотелось а методы типа "собираем вольтметр" на ардуино в которых напряжение подавалась на аналоговый вход через делитель - очень неточные потому что и нагрузка и Arduino питались от одного и того-же аккумулятора и таким образом любое включение/выключение нагрузки приводило к скачкам напряжения и искажало показания. В качестве выхода из ситуации было решено собрать стабилизированный источник опорного напряжения на 5V, подключить его на вход AREF и использовать функцию analogReference(EXTERNAL). Готовых примеров с применением этой функции я не нашел - поэтому собрал и оттестировал свой и хочу поделиться с другими. Данный вариант подойдет для контроля батареи любого робота, р/у модели который работает на базе любой платы Arduino и возможно кому-то еще окажется полезным для решения такой задачи.
    Собственно схема:

    Схема электропитания Arduino от АКБ.jpg

    Код (C++):
    #define SDI 12
    #define CLK 11
    #define LE 10
    #define analogPin 0
    #define LOAD 3
    #define buttonLOAD 2
    byte loadST = 0;             //Состояние нагрузки
    byte oldloadST = 1;        //Последнее состояние нагрузки, для исключения ложных переключений
    long previousMillis = 0;  //Время последнего нажатия кнопки
    int value = 0;
    float vout = 0.0;
    float vbat = 0.0;
    //поправка на маленькое внутренне сопротивление аналогового входа Arduino
    float kpop = 0.1;

    void setup() {
      // при измерении напряжения будем отталкиваться от значения на входе AREF
      analogReference(EXTERNAL);
      pinMode(SDI, OUTPUT);
      pinMode(CLK, OUTPUT);
      pinMode(LE, OUTPUT);
      pinMode(LOAD, OUTPUT);
      pinMode(buttonLOAD, INPUT);
    }

    void loop()
    {

    //Метка разряженой батареи - никакой другой код выполняться не должен !!!
    battery_low:
      // считываем аналоговое значение
      value = analogRead(analogPin);
      // рассчитываем напряжение на аналоговом входе
      vout = (value * 4.32) / 1024.0;
      // рассчитываем напряжение на батарее (4.065 коэффициент делителя)
      vbat = vout * 4.065 + kpop;

      // Если напряжение на батарее <12V отключаем всю нагрузку и переходим на battery_low:
      if (vbat <= 12) {
        //проверяем не было ли вызвано падения напряжения скачком потребления в нагрузке !?
        delay(250);
        // снова считываем аналоговое значение
        value = analogRead(analogPin);
        // снова рассчитываем напряжение на аналоговом входе
        vout = (value * 4.32) / 1024.0;
        // снова рассчитываем напряжение на батарее (4.065 коэффициент делителя)
        vbat = vout * 4.065 + kpop;
        if (vbat > 12) goto battery_low;  //да это просто был скачок потребления батарея еще живая
        //Нет батарея села - отключаем всю нагрузку
        loadST = 0;
        digitalWrite(LOAD, loadST);
        Out(B00000001, B00000000); // Мигаем 1-м деленим
        delay (500);
        Out(B00000000, B00000000); // Мигаем 1-м деленим
        delay (500);
        //Переходим на метку ожидания заряженной батареи
        goto battery_low;
      }

      if (vbat > 12.0 && vbat <= 12.5) Out(B00000011, B00000000); // Рисуем 2 деления
      if (vbat > 12.5 && vbat <= 13.0) Out(B00000111, B00000000); // Рисуем 3 деления
      if (vbat > 13.0 && vbat <= 13.5) Out(B00001111, B00000000); // Рисуем 4 деления
      if (vbat > 13.5 && vbat <= 14.0) Out(B00011111, B00000000); // Рисуем 5 делений
      if (vbat > 14.0 && vbat <= 14.5) Out(B00111111, B00000000); // Рисуем 6 делений
      if (vbat > 14.5 && vbat <= 15.0) Out(B01111111, B00000000); // Рисуем 7 делений
      if (vbat > 15.0 && vbat <= 15.5) Out(B11111111, B00000000); // Рисуем 8 делений
      if (vbat > 15.5 && vbat <= 16.0) Out(B11111111, B00000001); // Рисуем 9 делений
      if (vbat > 16.0) Out(B11111111, B00000011); // Рисуем 10 делений (свежая батарея)

      //Программная стабилизация кнопки
      if (digitalRead(buttonLOAD) == HIGH) { //Когда нажата кнопка
        //Если от предыдущего нажатия прошло больше 100 миллисекунд
        if (millis() - previousMillis > 100) {
          //Запоминаем время первого срабатывания
          previousMillis = millis();
          if (loadST == oldloadST) { //Проверка, что состояние кнопки изменилось
            loadST = !loadST;
          }
        }
      } else {
        oldloadST = loadST;
      }

    //Включаем / выключаем нагрузку
    digitalWrite(LOAD, loadST);

    }

    void Out(byte data1, byte data2)
    {
      shiftOut(SDI, CLK, MSBFIRST, data2);
      shiftOut(SDI, CLK, MSBFIRST, data1);
      digitalWrite(LE, HIGH);
      delayMicroseconds(1);
      digitalWrite(LE, LOW);
    }
    Видео где показано как это все работает



    Съемка процесса разряда батареи выполнена в формате timelapce
    Погрешность данного метода измерения находится в сотых вольта....
     
    Последнее редактирование: 29 дек 2016
  2. rkit

    rkit Гуру

    Не понял. Вам не нравилось, что ардуино и нагрузка питались от одного аккумулятора, поэтому вы оставили всё так же один аккумулятор, но поставили второй стабилизатор на aref?
     
  3. issaom

    issaom Гик

    Если использовать analogReference(DEFAULT) - любое включение выключение нагрузки будет влиять на опорное напряжение по умолчанию (5V) в этом очень легко убедиться воткнув вольтметр, а все измерения на аналоговом входе ардуино производит сравнивая опорное напряжение с измеряемой величиной. Если к аналоговому входу подключить переменный резистор и измерять его сопротивление то там разницы не будет так как напряжение будет изменяться пропорционально - а если измерять что-то внешнее - пропорция будет нарушена и точности не будет никакой.
    Скажет так при analogReference(DEFAULT)
    натяжение на пине (+5V) Arduino = напряжению на пине (AREF) поэтому для измерения внешних величин метод бесполезный
     
  4. rkit

    rkit Гуру

    Так у вас что 5V, что aref подаются чрез два одинаковых стабилизатора от одинакового источника. Что влияет на 5V, то и на aref будет влиять, разве нет? Ну по 5V что больше тока течь будет ладно, этот стабилизатор будет чуть горячее и выдаст чуть другой результат. Но разница все равно будет в милливольтах исчисляться, по-моему..
     
  5. issaom

    issaom Гик

    одинаковый стабилизатор был использован потому что под рукой не было маломощного (туда подойдет что-угодно лишь бы оно было способно выдавать ровно 5V в широком диапазоне входных напряжений). У любого источника питания есть такая неприятная вещь как ВНУТРЕННЕ СОПРОТИВЛЕНИЕ и микросхема 7805 и внутренний стабилизатор самой платы ARDUINO не исключение - при включении моей мощной люстры от 5V там что то около 4.2 с копейками оставалось - а если цепи индикации зацепить на сам м.к. от 5V там тоже будет достаточно далеко..... спорить не буду, но не один вариант описанный в интернете нормальных показаний не выдавал а чтобы понять почему не выдавал рекомендую погуглить Закон Ома для полной цепи
     
    Последнее редактирование: 10 дек 2016
  6. mcureenab

    mcureenab Гуру

    Как пример или для точных измерений - интересно. А конкретно для задачи контроля питания, достаточно использовать внутренний источник опорного напряжения INTERNALxVy.
     
  7. issaom

    issaom Гик

    если вы про analogReference(INTERNAL) то этот метод для решения данной задачи также не подойдет....
     
  8. mcureenab

    mcureenab Гуру

    Почему? У всех подходит.
    Если вы включаете силовую нагрузку на выход 5V Arduino, то понятно, что набортный стабилизатор не справится.
     
  9. issaom

    issaom Гик

    метод (INTERNAL) хорош для измерения небольших интервалов напряжений - например напряжение поданное через делитель напряжения 1.1V или 1.0V вы измерите с очень большой точностью - а если подадите туда напряжение 0.5 метод выдаст полную охинею потому-что сам аналоговый вход при проведении измерений тоже потребляет некоторый ток (отъедает часть измеряемого напряжения - недостаточно высокоомный) и зависимость там при таких измерения ооооочень нелинейная - свою первую схему я собрал именно по такому принципу делитель был 150\10 kom
    т.е. 17-12V преобразовывались в 1.063....0.750V - так вот при приближении на батареи к 12V точность измерений становилась уже неприемлимая (диапазон 17-15V измерялся без проблем) (возможно конечно ардуина слишком Китайская) но я не думаю что в АЦП в разных Ардуинах собран на разных компонентах
     
  10. mcureenab

    mcureenab Гуру

    Сделайте делитель напряжения не 10кОм, а 1кОм.
     
  11. issaom

    issaom Гик

    10\1 если измерять до 12V ? попробую сегодня - такая мысль у меня была....
     
  12. mcureenab

    mcureenab Гуру

    В примерах я видел делители напряжения с общим сопротивлением 2кОм. Десятки и сотни кОм это многовато.
    Попробуйте так же в формуле расчета напряжения учесть сопротивление самого АЦП.
     
  13. issaom

    issaom Гик

    поправка на АЦП (float kpop =0.1;) и в моем примере была )))) она была именно с этим и связана что при падении напряжения к 12V схема начинала подвирать кода напряжение приближалось к 12V спасибо за совет
     
  14. mcureenab

    mcureenab Гуру

    В общем на номинал резисторов делителя напряжения под нагрузкой есть ограничение сверху.

    Из даташита ATmega128
    Из википедии
    Z1 и Z2 это R1 и R2 в нашем случае.

    Т.е. для нормальной работы АЦП требуется величина R1R2/(R1+R2) < 10кОм.

    Резисторы следует брать с одинаковым тепловым коэффициентом и располагать так, чтобы они имели одинаковую температуру.
     
    9xA59kK и issaom нравится это.
  15. issaom

    issaom Гик

    При INTERNAL другая проблема больше актуальна - для измерения напряжения до 11V придется брать делитель с коэффициентом 10, а это означает что если напряжение на внутреннем опорном стабилизаторе гульнет всего на 0,01V то показания гульнут на 0,1V - если ARDUINO используется только для измерения напряжения то метод достаточно точный, а вот если даже просто мигать светодиодиками которые подключены к цифровым пинам во время изменений - они оказывают влияние на работу внутреннего стабилизатора. Со своим "Китайцем" погрешность в 0,2....0,5V я думаю точно обеспечена ))) ну + на разных платах оно изначально разное т.е. придется подгонять код..... (сняв всего около 20ma c 8 цифровых пинов у меня напряжение на AREF съехало с 1.10 до 1.08). За инфу спасибо.
     
  16. mcureenab

    mcureenab Гуру

    Понятно. На внутренний стабилизатор многие жалуются. Для повышения точности даташит рекомендует отправлять МК в сон и т.п. В главе про ADC много полезного есть.
    Номинал резисторов в первую очередь влияет на скорость замеров, поскольку цепочка из резисторного делителя напряжения и внутреннего резистора и конденсатора создает RC фильтр. Он срезает быстрые изменения сигнала. В данном случае это даже хорошо.
    Собственное сопротивление порта составляет 100МОм, и на делитель в сотни кОм не повлияет.
    Выходит, что нужно добиться стабильности от стабилизатора питания Ардуино.
     
  17. mcureenab

    mcureenab Гуру

    Есть и хорошая "новость". МК умеет замерять разность напряжений на двух входах АЦП с усилением сигнала. Раз вам нужно точно измерить напряжение вблизи 12Вольт, то стоит сравнивать его не с нулем вольт, а с 12Вольт. Режим программируется через регистры МК.
     
  18. issaom

    issaom Гик

    можно какую-нибудь ссылку.... где про это можно почитать.... ?
     
  19. Arkad_snz

    Arkad_snz Гик

    Схема не открывается, повторите пожалуйста.
    Други! Научите как заставить робоплатформу Няшу или miniQ кроме руления движками, сенсорами контролировать свою АКБ "не пора ли менять/заряжать?".
    Платформа на Strela или Iskra+T-Shsild
     
  20. issaom

    issaom Гик

    Что-то у меня вылетели все ссылки с Яндекс диска....
    https://yadi.sk/i/6EW-8PE432trgC