Тестер для аккумуляторов 18650

Zahar
Zahar аватар
Offline
Зарегистрирован: 16.11.2013

Всем приве.

Сделал я вот такой тестер для аккумуляторов 18650.

Вот его схема

 

 

//YWROBOT
//Compatible with the Arduino IDE 1.0
//Library version:1.1
#include <Wire.h>
//#include <LiquidCrystal_I2C.h>
//LiquidCrystal_I2C lcd(0x27, 20, 4); // set the LCD address to 0x27 for a 16 chars and 2 line display
#include <LCD5110_Basic.h>
LCD5110 myGLCD(7, 6, 5, 3, 4);
extern unsigned char SmallFont[];
#define load_on_off 8 //
#define charge_on_off 9 //
#define V_battery  A1//
// #define led  14//
#define led_red 16    // лед на панэли зарядника
#define led_green 17//лед на панэли зарядника
#define led_discharge_plus 10//лед
#define led_charge_plus 11//лед
#define led_discharge_minus 12//лед
#define led_charge_minus 13//лед
#define but_menu 2//Кнопка меню
byte menu = 1; //переменная для switch
float U_battery; // Напряжение на аккумуляторе
int current = 490;//mA
//float Time;
float charge_time;
float charge_time2;
float discharge_time;
long previousMillis = 0;
float discapacity;
int mark; 
boolean flag;  //флаг для кнопки меню
void setup()
{
  Serial.begin(9600);
  //lcd.init();                      // initialize the lcd
  myGLCD.InitLCD(60);
  myGLCD.setFont(SmallFont);
  pinMode(but_menu, INPUT);// кнопка меню
  digitalWrite(but_menu, HIGH); // подтяжка кнопки меню
  pinMode(led_charge_plus, OUTPUT);
  pinMode(led_charge_minus, OUTPUT);
  pinMode(led_discharge_plus, OUTPUT);
  pinMode(led_discharge_minus, OUTPUT);
  digitalWrite(led_charge_plus, LOW);
  digitalWrite(led_charge_minus, LOW);
  digitalWrite(led_discharge_plus, LOW);
  digitalWrite(led_discharge_minus, LOW);
  //pinMode(led, OUTPUT);
  pinMode(led_red, INPUT);
  pinMode(led_green, INPUT);
  pinMode(load_on_off, OUTPUT);
  digitalWrite(load_on_off, LOW);// Выключение нагрузки
  pinMode(charge_on_off, OUTPUT);
  digitalWrite(charge_on_off, LOW);// Выключение зарядки
  myGLCD.clrScr();
  myGLCD.print("Battery Tester", CENTER, 0);
  myGLCD.print("REV_2", CENTER, 16);
  delay (1000);
}
void loop()
{
  unsigned long currentMillis = millis();

 // ______________Упровление кнопкой МЕНЮ______________
  if (digitalRead(but_menu) == LOW && flag == 0) //если кнопка нажата и перемення flag равна 0 , то ...
  {
    menu++;
    flag = 1;
    if (menu > 4) //ограничение количества режимов
    {
      menu = 1; //так как мы используем только одну кнопку, то переключать режимы будем циклично
    }
  }
if (digitalRead(but_menu) == HIGH && flag == 1)flag = 0; //если кнопка НЕ нажата и переменная flag равна - 1 ,то обнуляем переменную "flag"
  // __________________________________________________

  switch (menu) {
    case 1:// заряд
      digitalWrite(load_on_off, LOW);// Выключение нагрузки
      digitalWrite(led_discharge_plus, LOW);//Выключение леда Разрядки
      delay(100);
      digitalWrite(charge_on_off, HIGH);// Включение Зарядки
      digitalWrite(led_charge_plus, HIGH);// Включение леда Зарядки
      delay(500);
      U_battery = ((analogRead(V_battery) * 5) / 1024.0) * 2.01; // Напряжение на аккумуляторе
      if (currentMillis - previousMillis >= 1000) { // Подсчет времени в секундах
        previousMillis = currentMillis;
        charge_time++; // прибовляем каждую секунду
        disp_param();
      }
      if (digitalRead(led_red) == HIGH && digitalRead(led_green) == LOW ) {
        delay(1000); // если показалось что заряд закончен то задержка 1сек и проверить есщё раз
        if (digitalRead(led_red) == HIGH && digitalRead(led_green) == LOW ) menu = 2;
      }
      break;
    case 2:// разряд
      digitalWrite(charge_on_off, LOW);// Выключение зарядки
      digitalWrite(led_charge_plus, LOW);// Выключение леда зарядки
      delay(500);
      digitalWrite(led_discharge_plus, HIGH);//Включение леда Разрядки
      digitalWrite(load_on_off, HIGH);//  Включение нагрузки
      U_battery = ((analogRead(V_battery) * 5) / 1024.0) * 2.01; // Напряжение на аккумуляторе
 if (currentMillis - previousMillis >= 1000) { // Подсчет времени в секундах
        previousMillis = currentMillis;
        discharge_time++; // прибовляем каждую секунду
        discapacity = current * (discharge_time / 3600); // Подсчет емкости аккумулятора
        disp_param();
      }
 if (U_battery <= 2.7) menu = 3; // переход на третий case
      break;
    case 3:
      digitalWrite(load_on_off, LOW);// Выключение нагрузки
      delay(500);
      digitalWrite(charge_on_off, HIGH);// Включение зарядки
      digitalWrite(led_discharge_plus, LOW);//Выключение леда Разрядки
      digitalWrite(led_charge_plus, HIGH);// Включение леда Зарядки
      U_battery = ((analogRead(V_battery) * 5) / 1024.0) * 2.01; // Напряжение на аккумуляторе
      if (currentMillis - previousMillis >= 1000) { // Подсчет времени в секундах
        previousMillis = currentMillis;
        charge_time2++; // прибовляем каждую секунду
        //discapacity = current * (discharge_time / 3600); // Подсчет емкости аккумулятора ?????????????????????
        disp_param();
      }
      if (digitalRead(led_red) == HIGH && digitalRead(led_green) == LOW ) {
        delay(1000); // если показалось что заряд закончен то задержка 1сек и проверить есщё раз
        if (digitalRead(led_red) == HIGH && digitalRead(led_green) == LOW ) menu = 4;
      }
      break;
    case 4:
      digitalWrite(load_on_off, LOW);// Выключение нагрузки
      delay(500);
      digitalWrite(charge_on_off, LOW);// Выключение зарядки
      digitalWrite(led_discharge_plus, LOW);//Выключение леда Разрядки
      digitalWrite(led_charge_plus, LOW);   // Выключение леда Зарядки
      U_battery = ((analogRead(V_battery) * 5) / 1024.0) * 2.01; // Напряжение на аккумуляторе
      disp_param_end();
      //while (1) {}
      break;
  }
}

void disp_param() {
  myGLCD.clrScr();
  if (menu == 1) myGLCD.print("CHARGE_1:", CENTER, 0);
  if (menu == 2) myGLCD.print("DISCHARGE:", CENTER, 0);
  if (menu == 3) myGLCD.print("CHARGE_2:", CENTER, 0);
  myGLCD.print("U=", LEFT, 16);
  myGLCD.printNumF(float(U_battery), 2, 10, 16);
  myGLCD.print("V", 35, 16);
  myGLCD.print("T=", LEFT, 24);
  if (menu == 1) {
    if (charge_time <= 9) mark = 17;
    if (charge_time >= 10) mark = 23;
    if (charge_time >= 100) mark = 29;
    if (charge_time >= 1000) mark = 35;
    if (charge_time >= 10000) mark = 41;
    myGLCD.printNumF(charge_time, 0, 10, 24);
    myGLCD.print("Sec", mark, 24);
  }
  if (menu == 2) {
    if (discharge_time <= 9) mark = 17;
    if (discharge_time >= 10) mark = 23;
    if (discharge_time >= 100) mark = 29;
    if (discharge_time >= 1000) mark = 35;
    if (discharge_time >= 10000) mark = 41;
    myGLCD.printNumF(discharge_time, 0, 10, 24);
    myGLCD.print("Sec", mark, 24);
  }
  if (menu == 3) {
    if (charge_time2 <= 9) mark = 17;
    if (charge_time2 >= 10) mark = 23;
    if (charge_time2 >= 100) mark = 29;
    if (charge_time2 >= 1000) mark = 35;
    if (charge_time2 >= 10000) mark = 41;
    myGLCD.printNumF(charge_time2, 0, 10, 24);
    myGLCD.print("Sec", mark, 24);
  }
  myGLCD.print("I=", LEFT, 32);
  if (menu == 1) myGLCD.printNumI(0, 10, 32);
  if (menu == 2) myGLCD.printNumI(current, 10, 32);
  if (menu == 3) myGLCD.printNumI(0, 10, 32);
  myGLCD.print("mA", 30, 32);
  myGLCD.print("CAP=", LEFT, 40);
  myGLCD.printNumF(discapacity, 1, 24, 40);
  if (discapacity <= 9) mark = 43;
  if (discapacity >= 10) mark = 49;
  if (discapacity >= 100) mark = 55;
  if (discapacity >= 1000) mark = 61;
  myGLCD.print("mAh", mark, 40);
  Serial.print("U_battery=");
  Serial.print(U_battery);
  Serial.print("V current=");
  Serial.print(current);
  Serial.print("mA");
  Serial.print(" TimeCharge=");
  if (menu == 1) Serial.print(charge_time);
  if (menu == 2) Serial.print(charge_time2);
  Serial.print("Sec TimeDischarge=");
  Serial.print(discharge_time);
  Serial.print("Sec discapacity=");
  Serial.print(discapacity);
  Serial.print("mAh");
  if (menu == 1) Serial.println(" CHARGE_1");
  if (menu == 2) Serial.println(" DISCHARGE");
  if (menu == 3) Serial.println(" CHARGE_2");
}
void disp_param_end() {
  myGLCD.clrScr();
  myGLCD.print("END TEST", CENTER, 0);
  myGLCD.print("U=", LEFT, 8);
  myGLCD.printNumF(float(U_battery), 2, 10, 8);
  myGLCD.print("V", 35, 8);
  myGLCD.print("Tcharg=", LEFT, 16);
  myGLCD.printNumF(charge_time, 0, 43, 16);
  if (charge_time <= 9) mark = 49;
  if (charge_time >= 10) mark = 55;
  if (charge_time >= 100) mark = 61;
  if (charge_time >= 1000) mark = 67;
  if (charge_time >= 10000) mark = 73;
  myGLCD.print("S", mark, 16);
  myGLCD.print("Tdisch=", LEFT, 24);
  myGLCD.printNumF(discharge_time, 0, 43, 24);
  if (discharge_time <= 9) mark = 49;
  if (discharge_time >= 10) mark = 55;
  if (discharge_time >= 100) mark = 61;
  if (discharge_time >= 1000) mark = 67;
  if (discharge_time >= 10000) mark = 73;
  myGLCD.print("S", mark, 24);
  myGLCD.print("Cdis=", LEFT, 32);
  myGLCD.printNumF(discapacity, 1, 31, 32);
  if (discapacity <= 9) mark = 49;
  if (discapacity >= 10) mark = 55;
  if (discapacity >= 100) mark = 61 ;
  if (discapacity >= 1000) mark = 67;
  myGLCD.print("mAh", mark, 32);
  Serial.print("U_battery=");
  Serial.print(U_battery);
  Serial.print("V TimeCharge=");
  Serial.print(charge_time);
  Serial.print("S TimeDischarge=");
  Serial.print(discharge_time);
  Serial.print("S discapacity=");
  Serial.print(discapacity);
  Serial.print("mAh");
  Serial.println(" END_TEST");
}

В loop есть switch, 

1) заряд аккумулятора, оконьчание заряда определяеться платой зарядки,тоесть по окончянию загораеться зелёный светодиод.

2) замер ёмкости. Разрежаю аккумулятор до 2,7В и считаю секунды за которые происходит разряд. Ток разряда постоянный 490мА, нагрузкой служит LM317. Всё это дело перемножаю и получаю мАч.

3) Снова зарядка.

4) Конец.

Вопрос, почему я получаю результат около 500mAh? Хотя на батарейках написано 9800mAh.

Понятно что китайци врут без божно но всётаки не настолько же.

Может у меня гдето ошибка в коде или в самом принцепе замера?

 

_mikka
Offline
Зарегистрирован: 01.11.2015

Ну 9800 mAh там не поместится физически, слишком маленький физический объем аккумулятора. В таком корпусе я встречал максимум 2500 mAh.  Но обчно это 1500 - 3000 mAh.

Zahar
Zahar аватар
Offline
Зарегистрирован: 16.11.2013

Ну то что 9800mAh много для этих батареек я понимал. Я расчитывал хотябы на половину от этой цифры.

А по поводу кода кто нибуть может что то сказать?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Я бы для начала провел измерения для разных (например, десяти) значениях разрядного тока. И построил график емкости от разрядного тока. И только после этого пытался бы сделать какие-то выводы.

Zahar
Zahar аватар
Offline
Зарегистрирован: 16.11.2013

Я ведь написал что ток разряда постоянный 490mAh.

Нагрузка выполнена на микросхеме LM317. Поэтому вне зависимости от напряжения на батарейке, ток "постоянный" 490mAh.

ADR
Offline
Зарегистрирован: 13.09.2016

Андриано хотел сказать, что при другом "постоянном" токе разряда, могуть быть другие значения ёмкости.

Zahar
Zahar аватар
Offline
Зарегистрирован: 16.11.2013

А при каком токе нужно проверять? Наверное есть какие то стандарты?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Zahar,  тоже никак не соберусь сделать свою зарядку, хочется что б было круто -а сильно усложнять не охота.. воти не делаю)

По сути  - На конечный результат влияют очень много факторов, например ацп. В меге и так паршивый АЦП, а вы резисторным делителем сделали ему LSB 10 мв. А если отбросить два бита, в которых трясётся мусор, так и вообще получается что точность у него порядка 40мв. Это не то что много -это просто ужас. Как вариант лечения -убрать резисторный делитель, включить напрямую , и делать сильный оверсемплинг, не менее 256 образцов, лучше 65 тыщ.. Вторая проблема -потери в контактах и соединителях. По картинке не очень видно, но если держатель из мягкой пластмассы и пружинками в качестве контактов, то это очень плохой разъём, в нём может потеряться до 100 миллиом запросто. Если у вас есть ещё соединения -то в них ещё теряется. В сумме может прилично набежать, а выльется это в то, что в конце замера вольтметр покажет предельно-минимальное напряжение, а на самом деле оно ещё не будет минимальным. Лечить качественным держателем для аккумулятора, и всеми остальными соединениями только на пайке, без разъёмов и толстым проводом. Ну и всё калибровать по точным приборам..

Zahar
Zahar аватар
Offline
Зарегистрирован: 16.11.2013

dimax почему 10мВ?

5В/1024=4,88мВ. Как делитель влияет на эту цифру? Конечно можно на AREF подать 2,5В тем самым уменьшить LSB, но надо ли это?

По поводу оверсемплинг вы наверное правы.

На держателе действительно есть просадка напряжения около 20мВ. Мне кажется это не критично.

Вопрос в другом, правильно ли я делаю сам принцип замера? Может нужно понизить ток.

Сколько искал в интернете не нашел как правильно надо мерить ёмкость этих аккумуляторов. А точнее как её меряют китайци.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Zahar, резисторный делитель увеличивает LSB  пропорционально коэффициенту деления. Другими словами при подачи 5 вольт на вход вашей схемы АЦП должен был показать значение 1024 , а будет 512.  5000/512 = 10мв  Подать на AREF 2,5 вольта очень хорошая мысль, тем более  достаточно tl431 и одного резистора.

Принцип расчёта тока как я понял у вас канонический. Другое дело, что нужно понимать, что понятие "мАч" -это попугаи. Эта цифра не говорит не о чём, т.к. напрямую зависит от тока нагрузки и от внутреннего сопротивления аккмулятора. Будете разряжать током 50мА -будёт ёмкость к примеру 5000мАч, а током 500мА -1000мАч,(цифры с потолка, просто для примера).

У меня есть один рабочий проект -специализированный прибор самодельный, предназначенный для тестирования и измерения ёмкости -спайки трёх мизинцев Ni-Mh аккмуляторов. На этой спайке написана ёмкость 700mAh, когда я измеряю их ёмкость , разряжая  током примерно 350mA то новые аккумы показывают цифру около 700мАч. Т.е. можно сделать вывод, что новый аккум должен показать свою ёмкость при токе разрядки равным половине его ёмкости. Разумеется это моё личное наблюдение, и ГОСТом оно не является :)

Но тут как я уже писал вас ждёт засада -при больших токах напряжение будет теряться везде, в каждом проводке, на каждом контакте. Поэтому нужно делать всё очень качественно, либо существенно снижать ток.

 

 

Zahar
Zahar аватар
Offline
Зарегистрирован: 16.11.2013

А если я включу встроенное опорное напряжение 1.1В и пересчитаю делитель так чтоб на его выходе было соответственное напряжение (4,2В->1,1В)?

"Принцип расчёта тока как я понял у вас канонический." - что такое канонический? Я в программе вообще не меряю ток. Ток у меня это константа которую я просто померил "Fluke 77".

""мАч" -это попугаи" - ну это не совсем так. Теоретически в идеале не должно быть разници каким током разряжать, если батарейку 1000мАч разряжать 1А 1ч = 1Ач или 0,5А 2ч =1Ач. Я понимаю что ничего идеального нет, но долже же быть какой то НЕ идеальный стандарт для подсчета.

"Поэтому нужно делать всё очень качественно" - все сделанно очень качественно. ПП сделанно на професиональном станке CNC "LPKF PROTOMAT C60"

К сожалению я не сфоткал саму печатку, вытаскивать с корпуса не хочеться.

"У меня есть один рабочий проект" - если не сложно скинь его пожалуйста. Хочеться сравнить

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Zahar, (1)можно и от внутреннего ИОН 1.1в, так проще. (2) канонический -разрядка фиксированным током до Vmin напряжения . (3) теоретически разницы нет, если нет ни в чём потерь. У изношенного аккумулятора бывает такая-же ёмкость как и у нового, только у него  внутреннее сопротивление вырастает скажем до 1 ома. Сответссно ёмкость уже невозможно измерить разряжая током 0,5 ампера, т.к. всё сьест внутреннее сопротивление, а на контактах аккума  останется всего (например) 2 вольта. А на нагрузке в 10ма он запросто отдаст все свои запасы.  Так что более важным параметром является внутреннее сопротивление. Если отбросить всякие подделки, а взять брендовые аккумы, скажем написано на нём 1000мАч, значит столько и есть, а пригодность его и изношеннность определять по внутреннему сопротивлению. (4) достаточно посмотреть на ваш держатель аккмума, что бы сделать вывод что качественно может многое, но не всё. Вот такие держатели существенно лучше, но тоже не идеальны.  Идеальный зажим где-то видел за адские деньги, он как тиски сжимал, и врезался в контакты в очень  жёсткую притирку.  А плата симпатичная судя по фотке, да. Схемы моей спец зарядки нету, впрочем она тоже каноническая, но на разрядке стоит стабилизатор тока на ОУ, управляется переменником.  Есть только скетч, написанный говнокодом (делал, когда ещё начинающим был). Могу конечно показать, но разобраться там сложно что к чему )) Переделывать код нет никакого резона, т.к. работает он хорошо, на новых аккумуляторах показывает то, что написано. Впрочем я особо и не измеряю саму ёмкость, т.к.  работаю только с одним видом аккумуляторов, и по измеренному сопротивлению сразу определяю степень пригодности. (аккумы эти стоят в сканерах штрих-кода). Вот код, в последний раз что-то подправил в нём год назад..

#define r1 71.78 // номинал не принципиален
#define r2 7.76 // важно измерить этот резистор точно вместе с открытым каналом мосфета
#define keyCharge 6 // кнопка запуска зарядки
#define keyZamer 7 // кнопка запуска процесса измерения ёмкости аккумулятора
#define keyRzamer 8 // кнопка запуска измерения внутреннего сопротивления аккумулятора
#define zamerpin 13 //пин включения нагрузки подсчёта емкости и изм. внут.сопротивления
#define chargepin 11 // пин включения цепи зарядки

#define in0 A3  //вход измерение напряжения на аккумуляторе
//#define in1 A2  // измерения напряжения -соответствия току в цепи стаб.тока (к 2ноге лм358)
//#define in2 A1  // измерения напряжения с выхода 2 ЛМ317 для определения падения на резисторе(силы тока зарядки)
//#define in3 A0  // измерения напряжения с выхода2 лм317( через резистор)
#include <LiquidCrystal.h>
#include <TimerOne.h>
LiquidCrystal lcd(12, 10, 5, 4, 3, 2);

float tokzar, volt, voltin2, voltin3;
unsigned int emkost=0, val0=0, val1=0, val2=0, val3=0, rezult, rezult2;
// *****val0 стал rezult!*****

//unsigned int myArray[ts][2], aread, firstsample, oldfirstsample, numbersamples, rezult; 
//unsigned int myArray2[ts][2], aread2, firstsample2, oldfirstsample2, numbersamples2, rezult2; 

unsigned long timesec=0;
unsigned long prevmillis = 0, prevmillis2 = 0, prevmillisA = 0;
//boolean waitbegin, waitbegin2; //флаг включённого счётчика ожидания повтора отсчёта

boolean zamer=false, charge=false, avtozamer=false; // флаги режимов работы

void setup(){
Serial.begin(9600);
Timer1.initialize();
Timer1.attachInterrupt(counter);
analogReference(INTERNAL);
lcd.begin(16, 2);

pinMode (keyZamer,INPUT); // пин кнопки включения режима подсчёта емкости
pinMode (keyRzamer,INPUT); // пин кнопки включения режима подсчёта внутреннего сопротивления
pinMode (keyCharge,INPUT); // пин кнопки включения режима зарядки аккума
pinMode (zamerpin, OUTPUT); //пин включения нагрузки подсчёта емкости
pinMode (chargepin, OUTPUT); //пин включения зарядки
pinMode(A4,OUTPUT); //ключ резистора r2 - 7 Om
pinMode(A5,OUTPUT); //ключ резистора r1 -70 Om
digitalWrite(zamerpin, LOW); // по умолчанию нагрузка отключена
digitalWrite(chargepin, LOW); // по умолчанию зарядка отключена

lcd.setCursor(0,1);
lcd.print("Press Key Start"); //предложение выбрать замер с автозарядом  или только заряд
}
void loop()
{   
//shumodav();
volt = (float)adc(3)/16368*5.99373;
//    volt = (rezult * 5.99373) /1023 ; //вычисление напряжения (6.1 коэффициент для входа)
     lcd.setCursor (10,0);  //в углу всегда текущее напряжение
     lcd.print (volt,3);
     lcd.write ("v");
    //----------------------------------------------------------------------------
    //проверка условий первого запуска замера ёмкости аккумулятора

if (digitalRead(keyZamer) == HIGH && zamer==false && charge==false && volt >3.6) { 
    while (digitalRead(keyZamer) == HIGH ); //ждать пока станет не high (отпустят кнопку)
    //если кнопка нажата, флаги сброшены и напряжение не менее 3.6 вольт можно запускать замер ёмкости в ручную
    emkost=0; //сбросить счётчик mah в начале цикла
    timesec=0; //сбросить счётчик времени
    zamer=true; //поставить флаг замера
    lcd.clear();
    controlZ();
    } //передать  функции контроля замера ёмкости
if (zamer){ controlZ();} //если не первый круг измерения, то передать функции иззмер ёмкости 
 
// Проверка условия повторного нажатия на кнопку замера ёмкости -отключение измер емкости//////
if (digitalRead(keyZamer) == HIGH && zamer==true && charge==false){
     while (digitalRead(keyZamer) == HIGH ); //ждать пока станет не high (отпустят кнопку)
     zamer=false;
     timesec=0;
     lcd.clear();
     tone (9,220,1000);
     if (digitalRead(zamerpin) == HIGH) digitalWrite(zamerpin, LOW); //отключить схему замера
     lcd.setCursor (0,1);
     lcd.print (" IZMERENIE STOP "); 
     } // конец блока обработки повторного нажатия на кнопку замера ёмкости  
 
    // особая ситуация если кнопка замера нажата во время зарядки
if (digitalRead(keyZamer) == HIGH && zamer==false && charge==true) {
        while (digitalRead(keyZamer) == HIGH ); //ждать пока станет не high (отпустят кнопку)
    //  delay(200); // пауза против повторного срабатывания
        if (avtozamer==false){ 
        avtozamer=true; // если автозамера нет, то включить
        lcd.setCursor (0,0);   
        lcd.print ("+izmer");
        tone (9,1320,50); //пискнуть что команда принята 
        }
             else if (avtozamer==true) {
             avtozamer =false; //если автозамер включен, то выключить
             lcd.setCursor (0,0);   
             lcd.print ("      "); //стереть с экрана +замер
             tone (9,1760,50); //пискнуть что команда принята
             }  
         } // конец блока особой ситуации по нажатию кнопки замер во время заряда
    

     // проверка условий запуска на зарядку
if (digitalRead(keyCharge) == HIGH && charge == false && zamer==false){ //если первый запуск на зарядку
       delay(300);  //
       timesec=0; //сбросить счётчик времени
       charge=true; //поставить флаг зарядки
       lcd.clear();
       controlC(); //передать функции контроля зарядки
       }
if (charge) {controlC();} //если не первый круг, или после замера то на контроль зарядки

//////// обработка повторного нажатия на заряд во время заряда

if (digitalRead(keyCharge) == HIGH && charge == true && zamer==false){ //если повтор нажатия заряда
       delay(100);                             //защита от дребезга
       if (digitalRead(keyCharge) == HIGH ){    //защита от дребезга
       do {} while (digitalRead(keyCharge) == HIGH );  //защита от дребезга
        timesec=0; //сбросить счётчик времени
       charge=false;  //поставить флаг запрета заряда
       tone (9,110,1000);
        lcd.clear();
        // потом убрать в отдельную функцию
            float volt1 =adc2();
           digitalWrite(chargepin, LOW); //отключить схему заряда  
           float volt2=adc2();
           float Rvn= (volt1-volt2)/(tokzar/1000);
           lcd.setCursor (0,1);
           lcd.print ("CHRGOFF R="); 
           lcd.print (Rvn,3);


//           if (digitalRead(chargepin) == HIGH) digitalWrite(chargepin, LOW); //отключить схему заряда 
//           lcd.clear();
//           lcd.setCursor (0,1);
//           lcd.print ("  ZARYADKA STOP "); 
           if (avtozamer) avtozamer=false; // если был включен автозамер, то выключить.
           }   
     }   //end if digital read
 
   /////// ИЗМЕРЕНИЕ ВНУТРЕННЕГО СОПРОТИВЛЕНИЯ АККУМУЛЯТОРА//////////////////
if (digitalRead(keyRzamer) == HIGH && charge == false && zamer==false) { r_measure(); }
} ////////////////////loop end////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////



void counter(){ // модуль счётчика секунд
  timesec++;  //прибавлять по секунде
}



   void controlZ(){  // контроль параметров замера ёмкости
   if (volt >3 && zamer==true) { //если напряжение не менее 3 вольт
   if (digitalRead(zamerpin) == LOW) digitalWrite(zamerpin, HIGH); //разрешить схему замера
   process();} 

   else { //  а если менее 3 вольт то начать всё завершать..
   zamer=false; 
   tone (9,220,1000);
   if (digitalRead(zamerpin) == HIGH) digitalWrite(zamerpin, LOW); //отключить схему замера
   lcd.setCursor (0,1);
   lcd.print (" ZAMER OKONCHEN "); 
 } // end else
      /////////// передача управления после замера на зарядку/////////////
   if(zamer==false && charge==false && volt >0 && volt <=3){
   charge=true; // флаг, что-б завернуло в лупе на зарядку
   timesec=0; //сбросить счётчик времени
   delay(5000); // время прочитать что замер окончен
   controlC(); // передать управление в контроль зарядки
   lcd.setCursor (0,1);
   lcd.print("                ");  // Отчистить срочку для других сообщений
   } // end if 
} //end void


void process(){   //основной блок замера ёмкости
//shumodav2(); // aread2=analogRead(A2);
rezult2= adc(2);
rezult2>>=4;
emkost = (float)rezult2 * timesec/3600  ; //mAH 
lcd.setCursor(0,0);
lcd.print (emkost); //вывести результат на первой строке
lcd.print ("maH ");
lcd.setCursor (0,1);
lcd.print ("Izmer ");
lcd.print (rezult2);
lcd.print ("ma "); 
chasiki();
} // уходим обратно в loop







       void controlC(){ // контроль параметров зарядки
       if (charge==true) { // если флаг зарядки включен, то
       if (digitalRead(chargepin) == LOW) { //если зарядка не включена
       digitalWrite(chargepin, HIGH); //включить схему заряда 
       delay(1000); //устаканивание переходных процессов
       }  
    // расчёт тока зарядки
       voltin2= (float)adc(1)/16368*6.02;
       voltin3= (float)adc(0)/16368*6.00;
       tokzar=(voltin2-voltin3)*1000/2.705; //расчёт тока зарядки через сопротивление и разницы напряжений
       // 2.705 ом -моё сопротивление на выходе лм317
   
          if (tokzar >150) {  // зарядка если ток более150ма
          lcd.setCursor (0,1);
          lcd.print ("CHRG ");  
          lcd.print (tokzar,0); // отладка
          lcd.print ("ma");
          chasiki(); }
           else { // а если ток менее 150 ма, значит зарядилось (или аккум отключили) 
           charge=false;  //поставить флаг отключенной зарядки
           tone (9,880,1000);
           float volt1 =adc2();
           digitalWrite(chargepin, LOW); //отключить схему заряда  
           float volt2=adc2();
           float Rvn= (volt1-volt2)/(tokzar/1000);
           lcd.setCursor (0,1);
           lcd.print ("CHRGOFF R="); 
           lcd.print (Rvn,3);
   
        delay(3000); 
        if (avtozamer){ //особый случай, когда была нажата кнопка замера во время заряда  
        emkost=0; //сбросить счётчик mah в начале цикла
        timesec=0; //сбросить счётчик времени
        zamer=true; //поставить флаг замера
        lcd.clear();
        avtozamer=false; // т.к. пришло по метке атозамера, то сразу сниманием её, больше ненужна
        controlZ(); // передать функции контроля замера ёмкости
       } // конец особого случая
     }  // конец блока  else заряд окончен
   } // закрытие если charge=tru  
 } //конец void


void chasiki(){  // вывод времени
lcd.setCursor (11,1);
if (timesec <= 60) { lcd.print(timesec); lcd.print("sec"); }

        else // if (timesec <3600) {
        {
        byte min=timesec/60;
        lcd.print(min);
        lcd.print("m");
        
        if (min <100 ){ // считать секунды если минут меньше 100 (что б влезло)
        byte sec=timesec%60;
        if (sec <10) lcd.print("0");
        lcd.print(sec); 
        }  
      }

  
  }
 
 
 


//////////////////////////////////////////////////////
void r_measure() {

//Serial.print("Podozhdite 5sec..  ");
digitalWrite(A5,HIGH); //подключить 70 ом
tone (9,55); //
lcd.clear();
lcd.setCursor (0,0);
lcd.print("WAIT..");
delay(5000);
float volt_start= adc2();
digitalWrite(A4,HIGH);// подключить 7 ом
float volt_end= adc2();
digitalWrite(A4,LOW);
digitalWrite(A5,LOW);
float Rvn= (volt_start-volt_end) / (volt_start/r2);

lcd.setCursor (0,1);
lcd.print("1=");
lcd.print (volt_start,3); // отладка
lcd.print (" 2="); // отладка
lcd.print (volt_end,3);   // отладка
lcd.setCursor (0,0);
lcd.print ("R=");
lcd.print (Rvn);
lcd.print (" Om");
noTone (9);  
}

float adc2(){
uint32_t adc_buff=0;
for (int n=0; n<=4095; n++ ) {
adc_buff+= analogRead(in0); }
adc_buff >>=6; // 65472 full scale 16bit
return (float)adc_buff/65472*5.99373;
}

uint16_t adc(byte in){
uint32_t adc_buff=0;
for (int n=0; n<=255; n++ ) {
adc_buff+= analogRead(in); }
return adc_buff >>=4; // 16386 full scale 16bit
}

 

gena
Offline
Зарегистрирован: 04.11.2012

  Думается мне, что в схеме разряжающего генератора тока есть ошибка. Нужно бы поправить. Вдруг кто повторить попробует, а оно правильно работать не будет (а может и в железе что нибудь не так).

ADR
Offline
Зарегистрирован: 13.09.2016

Есть у меня автомобильная зарядка с функцией замера ёмкости. Так овт заметил, что от тока разряда, меняется и ёмкость при прочих равных. И в чистом виде формула типа "ток на время" - упорно не хочет работать. Статистических данных пока не набрал. Цикл заряд-разряд у меня длится почти неделю.

Zahar
Zahar аватар
Offline
Зарегистрирован: 16.11.2013

dimax ,большое спасибо.

Я всё таки решил уменьшить ток разряда и изменить ИОН. Для этого пришлось разобрать приборчик.

Вот сделал более качественные фотки.

Закончу тестировать обязательно отпишусь.

gena я не вижу никакой ошибки. Если не трудно, в чём ошибка?

 

gena
Offline
Зарегистрирован: 04.11.2012

  Как я понимаю, провод,  идущий к LS1 (для подключения к земле через реле - для разряда заданным током) должен стартовать в вывода 1 (ADJ), а не так как по схеме.

Zahar
Zahar аватар
Offline
Зарегистрирован: 16.11.2013

Единственная ошибка которую я сейчас заметил, были закорочены 6,5 и 7 ножки у LS1, исправил.

Всё остальное правильно.

gena
Offline
Зарегистрирован: 04.11.2012

   Может так понятнее будет  http://s017.radikal.ru/i440/1611/b4/49267ef11cde.jpg   Фрагмент даташита.

Я стараюсь рисовать на схеме такие детали как трёхвыводные стабилизаторы так, как они выглядят внешне, физически.  Разбираться в цоколёвке по нумерации - заранее иметь возможность нахомутать. Что уже и наблюдаем дляU3,  U4  LM7805  http://www.microcontroller.it/Tutorials/PIC/AD/Vref_3.htm

Zahar
Zahar аватар
Offline
Зарегистрирован: 16.11.2013

gena вы абсолютно правы, не понимаю как я так фраернулься.

Ошибка была только на схеме, на ПП всё правильно. :)

gena
Offline
Зарегистрирован: 04.11.2012

  О. Да здесь Клондайк ошибок!

  Ваши реле НИКОГДА не включатся.

  Не мешало бы где то указать, какой ток заряда даёт модуль на TP4056. Ведь из Китая они настроены на 1 А . Думаю это много.

  Резисторы R2,  R3 я бы применил номиналом побольше - зачем напрягать микроконроллер лишним током?

   А где в схеме R1? Наверно был подтяжкой для кнопки?  (Это было бы правильно, а сейчас - не очень).

  Почему светодиоды запитаны именно так? Возможно нагляднее был бы один двухцветный (трёхцветный).

  А аккумуляторная батарея BT1 разве состоит из двух ячеек?

  Жаль схема не очень просматривается. Может ещё что высмотрел бы (я тоношный в этом отношении). Проэкт походит на курсовую работу, значит надо сделать достойно. По нему студентам ещё защищаться и защищаться (:-)).

  Если отказаться от U3, U4  то можно запитаться от пятивольтового источника питания (USB, зарядное устройство для мобильников). Это даёт бОльшую мобильность Вашему устройству. А при запитывании от USB, можно в РС посылать отчёты (через виртуальный СОМ порт). Строить график on-line, затем его сохранять. Есть весьма достойная программа для этого.

   Имею ещё минимум три замечания, но это уже после отработки вышеизложенных.

gena
Offline
Зарегистрирован: 04.11.2012

  Уже четыре.

Zahar
Zahar аватар
Offline
Зарегистрирован: 16.11.2013

gena вы меня опустили ниже пола :)

1) Всё включаеться и работает.

2) TP4056 действительно 1А, разве это много? Он ведь предназначен именно для этих аккумуляторов. 

3) Изначально R2, R3 стояли 1к но реле не включалос пожтому опустил до 100Ом.

4)R1 резистор в нагрузке. Подтяжка кнопки програмная.

5) Светодиоды подключил так, потомучто изначально про них не подумал, а когда вспомнил про них просто не оказалоь земленного пина на ПП.

6) Просто не нашёл в OrCad нормального значка батарейки, применил что было. Хота наверное надобыло перерисовать этот значёк.

7) Честное пионерское, я уже давно не студент.

8) По поводу графиков. Если бы ток не был постоянным тогда имело бы смысл делать графики, а так не понятно какой график делать.

gena
Offline
Зарегистрирован: 04.11.2012

  То, что всё работает, у Вас - я не сомневаюсь. А если собрать по схеме (а анализирую я ТОЛЬКО её) - работать не будет. Ищите, или "сдавайтесь" - подскажу. С улюбкой вспоминаю фразу из журнала "Радио" : "При заведомо исправных деталях и правильно собранной схеме устройсто в наладке не нуждается и работать начинает сразу". Ага. Щас. Сколько дней проходило пока удавалось его запустить. 

   п.2  Для этих аккумуляторов ток заряда вполне нормален. Я это написал для "последователей", использующих устройство для других аккумуляторов.

   п.3 Номиналы желательно рассчитать, исходя из коэффициента усиления транзистора.

   п. 4 Я не прав. Схема немного размыта + я не внимателен.

   п. 5 "Хозяин - барин".

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

  п. 8 Можно было бы смотреть график изменения напряжения (как заряда, так и разряда). Думаю это информативно.

Zahar
Zahar аватар
Offline
Зарегистрирован: 16.11.2013

gena я не понял, я исправил ошибку или нет?

Если нет то я "сдаюсь" , начертите пожалуйста что не так.

gena
Offline
Зарегистрирован: 04.11.2012

  Посмотрите, от чего запитываются реле? (..Семён Семёныч..).

Zahar
Zahar аватар
Offline
Зарегистрирован: 16.11.2013

gena мне стыдно :). Столько раз смотреть и пропустить...

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

 

Вот новый скетч

//YWROBOT
//Compatible with the Arduino IDE 1.0
//Library version:1.1
#include <Wire.h>
//#include <LiquidCrystal_I2C.h>
//LiquidCrystal_I2C lcd(0x27, 20, 4); // set the LCD address to 0x27 for a 16 chars and 2 line display
#include <LCD5110_Basic.h>
LCD5110 myGLCD(7, 6, 5, 3, 4);
extern unsigned char SmallFont[];
#define load_on_off 8 //
#define charge_on_off 9 //
#define V_battery  A1//
// #define led  14//
#define led_red 16    // лед на панэли зарядника
#define led_green 17//лед на панэли зарядника
#define led_discharge_plus 10//лед
#define led_charge_plus 11//лед
#define led_discharge_minus 12//лед
#define led_charge_minus 13//лед
#define but_menu 2//Кнопка меню
byte menu = 1; //переменная для switch
float U_battery; // Напряжение на аккумуляторе
int current = 245;//mA
//float Time;
float charge_time;
float charge_time2;
float discharge_time;
long previousMillis = 0;
float discapacity;
int mark;
boolean flag;  //флаг для кнопки меню
void setup()
{
  Serial.begin(9600);
  //lcd.init();                      // initialize the lcd
  analogReference(INTERNAL); // внутренний референс 1,1В
  myGLCD.InitLCD(60);
  myGLCD.setFont(SmallFont);
  pinMode(but_menu, INPUT);// кнопка меню
  digitalWrite(but_menu, HIGH); // подтяжка кнопки меню
  pinMode(led_charge_plus, OUTPUT);
  pinMode(led_charge_minus, OUTPUT);
  pinMode(led_discharge_plus, OUTPUT);
  pinMode(led_discharge_minus, OUTPUT);
  digitalWrite(led_charge_plus, LOW);
  digitalWrite(led_charge_minus, LOW);
  digitalWrite(led_discharge_plus, LOW);
  digitalWrite(led_discharge_minus, LOW);
  //pinMode(led, OUTPUT);
  pinMode(led_red, INPUT);
  pinMode(led_green, INPUT);
  pinMode(load_on_off, OUTPUT);
  digitalWrite(load_on_off, LOW);// Выключение нагрузки
  pinMode(charge_on_off, OUTPUT);
  digitalWrite(charge_on_off, LOW);// Выключение зарядки
  myGLCD.clrScr();
  myGLCD.print("Battery Tester", CENTER, 0);
  myGLCD.print("REV_2", CENTER, 16);
  delay (1000);
}
void loop()
{
  unsigned long currentMillis = millis();


  // ______________Упровление кнопкой МЕНЮ______________
  if (digitalRead(but_menu) == LOW && flag == 0) //если кнопка нажата и перемення flag равна 0 , то ...
  {
    menu++;
    flag = 1;
    if (menu > 4) //ограничение количества режимов
    {
      menu = 1; //так как мы используем только одну кнопку, то переключать режимы будем циклично
    }
  }
  if (digitalRead(but_menu) == HIGH && flag == 1)flag = 0; //если кнопка НЕ нажата и переменная flag равна - 1 ,то обнуляем переменную "flag"
  // __________________________________________________

  switch (menu) {
    case 1:// заряд
      digitalWrite(load_on_off, LOW);// Выключение нагрузки
      digitalWrite(led_discharge_plus, LOW);//Выключение леда Разрядки
      delay(100);
      digitalWrite(charge_on_off, HIGH);// Включение Зарядки
      digitalWrite(led_charge_plus, HIGH);// Включение леда Зарядки
      delay(500);
      U_battery = battery_voltage(); // Напряжение на аккумуляторе
      if (currentMillis - previousMillis >= 1000) { // Подсчет времени в секундах
        previousMillis = currentMillis;
        charge_time++; // прибовляем каждую секунду
        disp_param();
      }
      if (digitalRead(led_red) == HIGH && digitalRead(led_green) == LOW ) {
        delay(1000); // если показалось что заряд закончен то задержка 1сек и проверить есщё раз
        if (digitalRead(led_red) == HIGH && digitalRead(led_green) == LOW ) menu = 2;
      }
      break;
    case 2:// разряд
      digitalWrite(charge_on_off, LOW);// Выключение зарядки
      digitalWrite(led_charge_plus, LOW);// Выключение леда зарядки
      delay(500);
      digitalWrite(led_discharge_plus, HIGH);//Включение леда Разрядки
      digitalWrite(load_on_off, HIGH);//  Включение нагрузки
     U_battery = battery_voltage(); // Напряжение на аккумуляторе
      if (currentMillis - previousMillis >= 1000) { // Подсчет времени в секундах
        previousMillis = currentMillis;
        discharge_time++; // прибовляем каждую секунду
        discapacity = current * (discharge_time / 3600); // Подсчет емкости аккумулятора
        disp_param();
      }
      if (U_battery <= 2.7) menu = 3; // переход на третий case
      break;
    case 3:
      digitalWrite(load_on_off, LOW);// Выключение нагрузки
      delay(500);
      digitalWrite(charge_on_off, HIGH);// Включение зарядки
      digitalWrite(led_discharge_plus, LOW);//Выключение леда Разрядки
      digitalWrite(led_charge_plus, HIGH);// Включение леда Зарядки
      U_battery = battery_voltage(); // Напряжение на аккумуляторе
      if (currentMillis - previousMillis >= 1000) { // Подсчет времени в секундах
        previousMillis = currentMillis;
        charge_time2++; // прибовляем каждую секунду
        //discapacity = current * (discharge_time / 3600); // Подсчет емкости аккумулятора ?????????????????????
        disp_param();
      }
      if (digitalRead(led_red) == HIGH && digitalRead(led_green) == LOW ) {
        delay(1000); // если показалось что заряд закончен то задержка 1сек и проверить есщё раз
        if (digitalRead(led_red) == HIGH && digitalRead(led_green) == LOW ) menu = 4;
      }
      break;
    case 4:
      digitalWrite(load_on_off, LOW);// Выключение нагрузки
      delay(500);
      digitalWrite(charge_on_off, LOW);// Выключение зарядки
      digitalWrite(led_discharge_plus, LOW);//Выключение леда Разрядки
      digitalWrite(led_charge_plus, LOW);   // Выключение леда Зарядки
      U_battery = battery_voltage(); // Напряжение на аккумуляторе
      disp_param_end();
      //while (1) {}
      break;
  }
}
float battery_voltage() {
  float sval;
  float LSB = 1.1 / 1024.0;
  for (int i = 0; i < 256; i++) {
    sval = sval + analogRead(V_battery);
  }
  sval = sval / 256;    // среднее
  sval = sval * LSB * 4.92; // Напряжение на аккумуляторе
  return sval;
}


void disp_param() {
  myGLCD.clrScr();
  if (menu == 1) myGLCD.print("CHARGE_1:", CENTER, 0);
  if (menu == 2) myGLCD.print("DISCHARGE:", CENTER, 0);
  if (menu == 3) myGLCD.print("CHARGE_2:", CENTER, 0);
  myGLCD.print("U=", LEFT, 16);
  myGLCD.printNumF(float(U_battery), 2, 10, 16);
  myGLCD.print("V", 35, 16);
  myGLCD.print("T=", LEFT, 24);
  if (menu == 1) {
    if (charge_time <= 9) mark = 17;
    if (charge_time >= 10) mark = 23;
    if (charge_time >= 100) mark = 29;
    if (charge_time >= 1000) mark = 35;
    if (charge_time >= 10000) mark = 41;
    myGLCD.printNumF(charge_time, 0, 10, 24);
    myGLCD.print("Sec", mark, 24);
  }
  if (menu == 2) {
    if (discharge_time <= 9) mark = 17;
    if (discharge_time >= 10) mark = 23;
    if (discharge_time >= 100) mark = 29;
    if (discharge_time >= 1000) mark = 35;
    if (discharge_time >= 10000) mark = 41;
    myGLCD.printNumF(discharge_time, 0, 10, 24);
    myGLCD.print("Sec", mark, 24);
  }
  if (menu == 3) {
    if (charge_time2 <= 9) mark = 17;
    if (charge_time2 >= 10) mark = 23;
    if (charge_time2 >= 100) mark = 29;
    if (charge_time2 >= 1000) mark = 35;
    if (charge_time2 >= 10000) mark = 41;
    myGLCD.printNumF(charge_time2, 0, 10, 24);
    myGLCD.print("Sec", mark, 24);
  }
  myGLCD.print("I=", LEFT, 32);
  if (menu == 1) myGLCD.printNumI(0, 10, 32);
  if (menu == 2) myGLCD.printNumI(current, 10, 32);
  if (menu == 3) myGLCD.printNumI(0, 10, 32);
  myGLCD.print("mA", 30, 32);
  myGLCD.print("CAP=", LEFT, 40);
  myGLCD.printNumF(discapacity, 1, 24, 40);
  if (discapacity <= 9) mark = 43;
  if (discapacity >= 10) mark = 49;
  if (discapacity >= 100) mark = 55;
  if (discapacity >= 1000) mark = 61;
  myGLCD.print("mAh", mark, 40);
  Serial.print("U_battery=");
  Serial.print(U_battery);
  Serial.print("V current=");
  Serial.print(current);
  Serial.print("mA");
  Serial.print(" TimeCharge=");
  if (menu == 1) Serial.print(charge_time);
  if (menu == 2) Serial.print(charge_time2);
  Serial.print("Sec TimeDischarge=");
  Serial.print(discharge_time);
  Serial.print("Sec discapacity=");
  Serial.print(discapacity);
  Serial.print("mAh");
  if (menu == 1) Serial.println(" CHARGE_1");
  if (menu == 2) Serial.println(" DISCHARGE");
  if (menu == 3) Serial.println(" CHARGE_2");
}
void disp_param_end() {
  myGLCD.clrScr();
  myGLCD.print("END TEST", CENTER, 0);
  myGLCD.print("U=", LEFT, 8);
  myGLCD.printNumF(float(U_battery), 2, 10, 8);
  myGLCD.print("V", 35, 8);
  myGLCD.print("Tcharg=", LEFT, 16);
  myGLCD.printNumF(charge_time, 0, 43, 16);
  if (charge_time <= 9) mark = 49;
  if (charge_time >= 10) mark = 55;
  if (charge_time >= 100) mark = 61;
  if (charge_time >= 1000) mark = 67;
  if (charge_time >= 10000) mark = 73;
  myGLCD.print("S", mark, 16);
  myGLCD.print("Tdisch=", LEFT, 24);
  myGLCD.printNumF(discharge_time, 0, 43, 24);
  if (discharge_time <= 9) mark = 49;
  if (discharge_time >= 10) mark = 55;
  if (discharge_time >= 100) mark = 61;
  if (discharge_time >= 1000) mark = 67;
  if (discharge_time >= 10000) mark = 73;
  myGLCD.print("S", mark, 24);
  myGLCD.print("Cdis=", LEFT, 32);
  myGLCD.printNumF(discapacity, 1, 31, 32);
  if (discapacity <= 9) mark = 49;
  if (discapacity >= 10) mark = 55;
  if (discapacity >= 100) mark = 61 ;
  if (discapacity >= 1000) mark = 67;
  myGLCD.print("mAh", mark, 32);
  Serial.print("U_battery=");
  Serial.print(U_battery);
  Serial.print("V TimeCharge=");
  Serial.print(charge_time);
  Serial.print("S TimeDischarge=");
  Serial.print(discharge_time);
  Serial.print("S discapacity=");
  Serial.print(discapacity);
  Serial.print("mAh");
  Serial.println(" END_TEST");
}

 

gena
Offline
Зарегистрирован: 04.11.2012

  А у транзисторов 2N5551 при токе Ic = 50 мА коэффициент услиления по току, hfe,  (минимальный) равен 30. Вот его то и нужно брать для рассчёта резистора R2 и  R3. Допустим реле потребляет 60 мА. Тогда сопротивление резистора R2:

                        R2 =  (5-0,7)/(0,06/30) = 4,3/(0,002) = 2150 Ом

Выбираю в меньшую сторону, т.е. 2 кОм. 

Zahar
Zahar аватар
Offline
Зарегистрирован: 16.11.2013

Релле TQ2-12v потребляет 11,7мА, при этом токе hfe=100

gena
Offline
Зарегистрирован: 04.11.2012

     Выскажу оставшиеся мысли по поводу схемы.

   1. О нумерации элементов с схеме. Считается хорошим тоном присваивать позиционные номера радиокомпонентам, узлам, 
блокам в таком порядке:
    - слева - направо,
    - сверху - вниз.
Т.е. так, как мы привыкли читать. Допускается последовательное присваивание номеров деталям, составляющим
логически законченный узел (как например в Вашем случае -  С1, С2, С3, С4 вокруг U3). Такой порядок позволяет
быстрее находить нужный элемент в схеме, а не искать его как зайца в поле.
   2. По кнопке SW1.  Вижу её как нормально замкнутую. Лично я предпочитаю использовать кнопки работающие на замыкание и притягивающие вывод МК к земле. Вывод МК подтянуть к шине питания (+5 В) внешним резистором (или хотя бы оставить на плате место для него). Почему?
 - кнопки на замыкание распространены больше,
 - контакты кнопки со временем окисляются. Следовательно в какой то момент времени кнопка активирует схему и
догадаться, почему устройство само начало работать - сложно. Может показатьчя что схема глючит.
 - провода к кнопке, клеммы на кнопке, тоже могут оборваться, рассоединиться - снова устройство начинает жить
своей жизнью.
- когда идёт процесс отладки, можно ошибочно pin кнопки настроить как выход. Если ещё к тому же на выводе установить
логическую единицу - микроконтроллеру это не понравится.
- надеяться на помехозащищённую подтяжку внутренним резистором микроконтроллера я не стал бы. Рядом может
находиться мобильник, мотор с диммированным управлением,  .... Да и настроить подтяжку вывода тоже можно забыть.
   3. По генератору разрядного тока (U5, R1). Советую ПРАКТИЧЕСКИ проверить, обеспечивает ли эта схема задуманный разрядный ток при напряжении на аккумуляторе близком к минимально рабочему (2,7 В и менее). Согласно тех описанию на LM317 проходное напряжение (Dropout Voltage) на ней около 1,5...1,7 В плюс Reference voltage (1,25 В) -->  всё работает впритык.
 
   Немного мечтаний. Если вместо Q1 и реле LS1 применить полевые транзисторы коммутирующие разные по номиналам R1, то можно программно задавать ток разряда. Например, резисторы с отношением сопротивлений 1-2-4-8. Это даёт возможность задавать ток разряда, в частности, 100-200-400-800 мА. Плюс комбинации этих токов.
 
На схеме сместите немного влево информационный значок провода "V_battary", чтобы он присоединялся  небольшим проводом к точке соединения R7, R6 (сейчас он напоминает какую то петлю из проводника).
Zahar
Zahar аватар
Offline
Зарегистрирован: 16.11.2013

gena , хочу сказать вам огромное спасибо за вашу помощь и дельные советы.

1) Нумерацию исправил.

2) Кнопка NO, просто на картинке почему то это плохо видно.

3) По поводу генератора вы абсолютно правы. Под конец разряда ток действительно проседает хоть и не значительно но просадка есть.

Я также, думал сделать регулируемый генератор тока, но делать его на LM317 мне кажеться это не очень хорошая идея. Хотелось бы сделать на ОУ и электронным потенциометре MCP41010. Про схему надо ещё подумать.

4)  Значок провода "V_battary" сместил.

 

gena
Offline
Зарегистрирован: 04.11.2012

  Может посмотреть наподобие    LM1117 800-mA Low-Dropout Linear Regulator. Для увеличения тока поставить параллельно несколько, на разные токи. Хотя и варианты с ОУ можно придумать.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Zahar, наш коллега а5021 давал тут схемку классического стабилизатора тока с шим-управлением. Вот прямо в самый раз в вашем случае.

gena
Offline
Зарегистрирован: 04.11.2012

    Опередили меня. Тоже возникло такое же предложение. Вот только транзистор там мне не очень нравится - открывается после четырёх Вольт на затворе. И ОУ я поставил бы с полевиками на входе.

  Интересны полевики от материнских плат РС.

Zahar
Zahar аватар
Offline
Зарегистрирован: 16.11.2013

Посоветуйте какой из этих ОУ применить LM358, RC4558, TL072, LM324, LM339.

Вместо RV1 будет стоять MCP41010.

gena
Offline
Зарегистрирован: 04.11.2012

  При запитывающих напряжения как на схеме, думаю можно начать с LM358.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Zahar, LM339 это компаратор) А что вам эти потенциометры сдались?? Взять простой ЦАП, те-же деньги, а уже 4000 градаций. Хотя я бы сделал на ШИМ, на первом таймере хоть хоть 65 тыщ градаций можно получить...

Zahar
Zahar аватар
Offline
Зарегистрирован: 16.11.2013

dimax ШИМ я не хочу. Точность про которую вы говорили в первых постах мне кажеться не возможно добиться ШИМом.

"А что вам эти потенциометры сдались??" - У меня есть эти потенциометры.

Также у меня есть ЦАП MPC4921 12bit, может сделаю на нём, надо поэксперементировать. Правда я не знаю как лютше сделать, забить четкие значения для ЦАП или дать контроллеру самому доходить до нужного значения тока?

gena
Offline
Зарегистрирован: 04.11.2012

   Я бы отдал предпочтение ЦАП. Практически подобрать пяток разрядных токов. Применяя самоконтроль контроллером точность будет зависить от его собственного АЦП. А вообще нужно что-то  пробовать. Там разные результаты могут быть. И по точности и по термостабильности.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Zahar, точность важна при измерении напряжения и тока ,а в управлении стабилизатором тока точность не критична.  Подогнать до примерно нужной величины, и всё.  Можно в принципе и циф.потенциометром, коли он уже есть у вас. Но по сравнению с ШИМом вы не получите выигрыша не в чём.

Zahar
Zahar аватар
Offline
Зарегистрирован: 16.11.2013

По настовлению Гены я всё таки переделал приборчик.


//Battery_tester rev6
#include <Wire.h>
#include <SPI.h>
#include <LCD5110_Basic.h>
LCD5110 myGLCD(5, 6, 7, 9, 8);// CLK, Din, DC, RES, CE
extern unsigned char SmallFont[];
#define charge_on_off 18 //
#define V_battery  A0//
#define V_current  A1//
// #define led  14//
#define led_red 16    // лед на панэли зарядника
#define led_green 17//лед на панэли зарядника
#define led_discharge 19//лед
#define but_menu 4//Кнопка меню
#define slaveSelectPin 10// set pin 10 as the slave select
#define PIN_A 2
#define PIN_B 3
byte menu = 1; //переменная для switch
float U_battery; // Напряжение на аккумуляторе
float I_current;
float Setting_current = 200;
int current = 490;//mA
//float Time;
float charge_time;
float charge_time2;
float discharge_time;
long previousMillis = 0;
float discapacity;
int mark;
int volt = 0;
boolean flag;  //флаг для кнопки меню
byte data = 0;
volatile unsigned long enabledA_time = 0;
volatile bool fl = false; // флаг что нужно вывести
volatile bool value_b = 0;
void DAC_Write(int outputValue) {
  //Write to the DAC through SPI
  // take the SS pin low to select the chip:
  digitalWrite(slaveSelectPin, LOW);
  data = highByte(outputValue);
  data = 0b00001111 & data;
  data = 0b00110000 | data;
  SPI.transfer(data);
  data = lowByte(outputValue);
  SPI.transfer(data);
  // take the SS pin high to de-select the chip:
  digitalWrite(slaveSelectPin, HIGH);
}
float battery_voltage() {
  float sval;
  float current;
  float LSB = 1.1 / 1024.0;
  for (int i = 0; i < 256; i++) {
    sval = sval + analogRead(V_battery);
  }
  sval = sval / 256;    // среднее
  sval = sval * LSB * 4.92; // Напряжение на аккумуляторе
  return sval;
}
float load_current() {
  float sval;
  float LSB = 1.1 / 1024.0;
  for (int i = 0; i < 256; i++) {
    sval = sval + analogRead(V_current);
  }
  sval = sval / 256;    // среднее
  sval = sval * LSB * 4.78; // Напряжение на аккумуляторе
  sval = sval / 2.00;//Напряжение делить на сопротивление = ток
  sval = sval * 1000; //53
  return sval;
}
void disp_param() {
  myGLCD.clrScr();
  if (menu == 1) myGLCD.print("CHARGE_1:", CENTER, 0);
  if (menu == 2) myGLCD.print("DISCHARGE:", CENTER, 0);
  if (menu == 3) myGLCD.print("CHARGE_2:", CENTER, 0);
  myGLCD.print("U=", LEFT, 16);
  myGLCD.printNumF(float(U_battery), 2, 10, 16);
  myGLCD.print("V", 35, 16);
  myGLCD.print("T=", LEFT, 24);
  if (menu == 1) {
    if (charge_time <= 9) mark = 17;
    if (charge_time >= 10) mark = 23;
    if (charge_time >= 100) mark = 29;
    if (charge_time >= 1000) mark = 35;
    if (charge_time >= 10000) mark = 41;
    myGLCD.printNumF(charge_time, 0, 10, 24);
    myGLCD.print("Sec", mark, 24);
  }
  if (menu == 2) {
    if (discharge_time <= 9) mark = 17;
    if (discharge_time >= 10) mark = 23;
    if (discharge_time >= 100) mark = 29;
    if (discharge_time >= 1000) mark = 35;
    if (discharge_time >= 10000) mark = 41;
    myGLCD.printNumF(discharge_time, 0, 10, 24);
    myGLCD.print("Sec", mark, 24);
  }
  if (menu == 3) {
    if (charge_time2 <= 9) mark = 17;
    if (charge_time2 >= 10) mark = 23;
    if (charge_time2 >= 100) mark = 29;
    if (charge_time2 >= 1000) mark = 35;
    if (charge_time2 >= 10000) mark = 41;
    myGLCD.printNumF(charge_time2, 0, 10, 24);
    myGLCD.print("Sec", mark, 24);
  }
  myGLCD.print("I=", LEFT, 32);
  if (menu == 1) myGLCD.printNumI(0, 10, 32);
  if (menu == 2) myGLCD.printNumF(float(I_current), 2, 10, 32);
  if (menu == 3) myGLCD.printNumI(0, 10, 32);
  myGLCD.print("mA", 54, 32);
  myGLCD.print("CAP=", LEFT, 40);
  myGLCD.printNumF(discapacity, 1, 24, 40);
  if (discapacity <= 9) mark = 43;
  if (discapacity >= 10) mark = 49;
  if (discapacity >= 100) mark = 55;
  if (discapacity >= 1000) mark = 61;
  myGLCD.print("mAh", mark, 40);
  Serial.print("U_battery=");
  Serial.print(U_battery);
  Serial.print("V current=");
  Serial.print(I_current);
  Serial.print("mA");
  Serial.print(" TimeCharge=");
  if (menu == 1) Serial.print(charge_time);
  if (menu == 2) Serial.print(charge_time2);
  Serial.print("Sec TimeDischarge=");
  Serial.print(discharge_time);
  Serial.print("Sec discapacity=");
  Serial.print(discapacity);
  Serial.print("mAh");
  if (menu == 1) Serial.println(" CHARGE_1");
  if (menu == 2) Serial.println(" DISCHARGE");
  if (menu == 3) Serial.println(" CHARGE_2");
}
void disp_param_end() {
  myGLCD.clrScr();
  myGLCD.print("END TEST", CENTER, 0);
  myGLCD.print("U=", LEFT, 8);
  myGLCD.printNumF(float(U_battery), 2, 10, 8);
  myGLCD.print("V", 35, 8);
  myGLCD.print("Tcharg=", LEFT, 16);
  myGLCD.printNumF(charge_time, 0, 43, 16);
  if (charge_time <= 9) mark = 49;
  if (charge_time >= 10) mark = 55;
  if (charge_time >= 100) mark = 61;
  if (charge_time >= 1000) mark = 67;
  if (charge_time >= 10000) mark = 73;
  myGLCD.print("S", mark, 16);
  myGLCD.print("Tdisch=", LEFT, 24);
  myGLCD.printNumF(discharge_time, 0, 43, 24);
  if (discharge_time <= 9) mark = 49;
  if (discharge_time >= 10) mark = 55;
  if (discharge_time >= 100) mark = 61;
  if (discharge_time >= 1000) mark = 67;
  if (discharge_time >= 10000) mark = 73;
  myGLCD.print("S", mark, 24);
  myGLCD.print("Cdis=", LEFT, 32);
  myGLCD.printNumF(discapacity, 1, 31, 32);
  if (discapacity <= 9) mark = 49;
  if (discapacity >= 10) mark = 55;
  if (discapacity >= 100) mark = 61 ;
  if (discapacity >= 1000) mark = 67;
  myGLCD.print("mAh", mark, 32);
  Serial.print("U_battery=");
  Serial.print(U_battery);
  Serial.print("V TimeCharge=");
  Serial.print(charge_time);
  Serial.print("S TimeDischarge=");
  Serial.print(discharge_time);
  Serial.print("S discapacity=");
  Serial.print(discapacity);
  Serial.print("mAh");
  Serial.println(" END_TEST");
}
void handler_a() {
  if (enabledA_time < millis()) {
    value_b = digitalRead(PIN_B);
    if (value_b == 1) Setting_current = Setting_current + 10;
    if (value_b == 0) Setting_current = Setting_current - 10;
    if (Setting_current > 1000) Setting_current = 0;
    if (Setting_current < 0) Setting_current = 1000;
    myGLCD.clrScr();
    myGLCD.print("I_Discharge", CENTER, 0);
    myGLCD.printNumF(float(Setting_current), 0, 20, 24);
    myGLCD.print("mA", 44, 24);
    fl = true;
    enabledA_time = millis() + 400; // уж включили антидребезг так включили
  }
}
void setup()
{
  Serial.begin(9600);
  pinMode (slaveSelectPin, OUTPUT);
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  analogReference(INTERNAL); // внутренний референс 1,1В
  myGLCD.InitLCD(60);
  myGLCD.setFont(SmallFont);
  pinMode(but_menu, INPUT);// кнопка меню
  digitalWrite(but_menu, HIGH); // подтяжка кнопки меню
  pinMode(led_discharge, OUTPUT);
  digitalWrite(led_discharge, LOW);
  pinMode(led_red, INPUT);
  pinMode(led_green, INPUT);
  pinMode(charge_on_off, OUTPUT);
  digitalWrite(charge_on_off, LOW);// Выключение зарядки
  DAC_Write(0); // Выключение нагрузки,установка MCP4921 в 0В
  digitalWrite(PIN_A, HIGH);
  digitalWrite(PIN_B, HIGH);
  attachInterrupt(0, handler_a, RISING);
  myGLCD.clrScr();
  myGLCD.print("Battery Tester", CENTER, 0);
  myGLCD.print("REV_2", CENTER, 16);
  delay (1000);
}
void loop()
{
  unsigned long currentMillis = millis();
  float min_set_current = Setting_current - 0.1;
  float max_set_current = Setting_current + 0.1;
  if (fl) {
    Serial.println(Setting_current);
    fl = false;
  }
  // ______________Упровление кнопкой МЕНЮ______________
  if (digitalRead(but_menu) == LOW && flag == 0) //если кнопка нажата и перемення flag равна 0 , то ...
  {
    menu++;
    flag = 1;
    if (menu > 4) //ограничение количества режимов
    {
      menu = 1; //так как мы используем только одну кнопку, то переключать режимы будем циклично
    }
  }
  if (digitalRead(but_menu) == HIGH && flag == 1)flag = 0; //если кнопка НЕ нажата и переменная flag равна - 1 ,то обнуляем переменную "flag"
  // __________________________________________________
  switch (menu) {
    case 1:// заряд
      //digitalWrite(load_on_off, LOW);// Выключение нагрузки
      DAC_Write(0); // установка MCP4921 в 0В
      digitalWrite(led_discharge, LOW);//Выключение леда Разрядки
      delay(100);
      digitalWrite(charge_on_off, HIGH);// Включение Зарядки
      delay(500);
      U_battery = battery_voltage(); // Напряжение на аккумуляторе
      if (currentMillis - previousMillis >= 1000) { // Подсчет времени в секундах
        previousMillis = currentMillis;
        charge_time++; // прибовляем каждую секунду
        disp_param();
      }
      if (digitalRead(led_red) == HIGH && digitalRead(led_green) == LOW ) {
        delay(1000); // если показалось что заряд закончен то задержка 1сек и проверить есщё раз
        if (digitalRead(led_red) == HIGH && digitalRead(led_green) == LOW ) menu = 2;
      }
      break;
    case 2:// разряд
      digitalWrite(charge_on_off, LOW);// Выключение зарядки
      delay(500);
      digitalWrite(led_discharge, HIGH);//Включение леда Разрядки
      //digitalWrite(load_on_off, HIGH);//  Включение нагрузки
      U_battery = battery_voltage(); // Напряжение на аккумуляторе
      I_current = load_current();
      MesureCurrent(I_current, min_set_current, max_set_current);
      if (currentMillis - previousMillis >= 1000) { // Подсчет времени в секундах
        previousMillis = currentMillis;
        discharge_time++; // прибовляем каждую секунду
        discapacity = I_current * (discharge_time / 3600); // Подсчет емкости аккумулятора
        disp_param();
      }
      if (U_battery <= 2.75) menu = 3; // переход на третий case
      break;
    case 3:
      DAC_Write(0); // Выключение нагрузки,установка MCP4921 в 0В
      delay(500);
      digitalWrite(charge_on_off, HIGH);// Включение зарядки
      digitalWrite(led_discharge, LOW);//Выключение леда Разрядки
      U_battery = battery_voltage(); // Напряжение на аккумуляторе
      if (currentMillis - previousMillis >= 1000) { // Подсчет времени в секундах
        previousMillis = currentMillis;
        charge_time2++; // прибовляем каждую секунду
        //discapacity = current * (discharge_time / 3600); // Подсчет емкости аккумулятора ?????????????????????
        disp_param();
      }
      if (digitalRead(led_red) == HIGH && digitalRead(led_green) == LOW ) {
        delay(1000); // если показалось что заряд закончен то задержка 1сек и проверить есщё раз
        if (digitalRead(led_red) == HIGH && digitalRead(led_green) == LOW ) menu = 4;
      }
      break;
    case 4:
      DAC_Write(0); // Выключение нагрузки,установка MCP4921 в 0В
      delay(500);
      digitalWrite(charge_on_off, LOW);// Выключение зарядки
      digitalWrite(led_discharge, LOW);//Выключение леда Разрядки
      U_battery = battery_voltage(); // Напряжение на аккумуляторе
      disp_param_end();
      //while (1) {}
      break;
  }
}
void MesureCurrent(float fCurrent, float fMinCurrent, float fMaxCurrent)
{
  if (fCurrent < fMinCurrent)
  {
    while (fCurrent < Setting_current)
    {
      fCurrent = load_current();
      DAC_Write(volt);
      // delay(1);
      if (volt >= 4095) volt = 4095;
      volt++;
      Serial.print("volt++=");
      Serial.println(volt);
    }
  }
  else if (fCurrent > fMaxCurrent)
  {
    while (fCurrent > Setting_current)
    {
      fCurrent = load_current();
      DAC_Write(volt);
      //delay(1);
      if (volt <= 0) volt = 0;
      volt--;
      Serial.print("volt--=");
      Serial.println(volt);
    }
  }
}

 

 

В данной версии при помощи энкодера можно регулировать ток разряда от 0мА до 1000мА.

Есть две вещи которые мне не очень нравяться, но они не чему не мешают.

Первое, это не очень красивая работа энкодера, если крутить быстро то он сбиваетья.

Второе, ток разряда чуть чуть калеблица в пределах + - 30мА. Но так как прграмма считает замеренный ток а не установленный то все расчёты верны.

Novice User
Offline
Зарегистрирован: 25.09.2017

а можно ли переделать под 3 лития?каждый тестируется отдельно

Novice User
Offline
Зарегистрирован: 25.09.2017

наверно нельзя...буду прорываться сам...