Тестер для аккумуляторов 18650
- Войдите на сайт для отправки комментариев
Втр, 08/11/2016 - 15:25
Всем приве.
Сделал я вот такой тестер для аккумуляторов 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.
Понятно что китайци врут без божно но всётаки не настолько же.
Может у меня гдето ошибка в коде или в самом принцепе замера?
Ну 9800 mAh там не поместится физически, слишком маленький физический объем аккумулятора. В таком корпусе я встречал максимум 2500 mAh. Но обчно это 1500 - 3000 mAh.
Ну то что 9800mAh много для этих батареек я понимал. Я расчитывал хотябы на половину от этой цифры.
А по поводу кода кто нибуть может что то сказать?
Я бы для начала провел измерения для разных (например, десяти) значениях разрядного тока. И построил график емкости от разрядного тока. И только после этого пытался бы сделать какие-то выводы.
Я ведь написал что ток разряда постоянный 490mAh.
Нагрузка выполнена на микросхеме LM317. Поэтому вне зависимости от напряжения на батарейке, ток "постоянный" 490mAh.
Андриано хотел сказать, что при другом "постоянном" токе разряда, могуть быть другие значения ёмкости.
А при каком токе нужно проверять? Наверное есть какие то стандарты?
Zahar, тоже никак не соберусь сделать свою зарядку, хочется что б было круто -а сильно усложнять не охота.. воти не делаю)
По сути - На конечный результат влияют очень много факторов, например ацп. В меге и так паршивый АЦП, а вы резисторным делителем сделали ему LSB 10 мв. А если отбросить два бита, в которых трясётся мусор, так и вообще получается что точность у него порядка 40мв. Это не то что много -это просто ужас. Как вариант лечения -убрать резисторный делитель, включить напрямую , и делать сильный оверсемплинг, не менее 256 образцов, лучше 65 тыщ.. Вторая проблема -потери в контактах и соединителях. По картинке не очень видно, но если держатель из мягкой пластмассы и пружинками в качестве контактов, то это очень плохой разъём, в нём может потеряться до 100 миллиом запросто. Если у вас есть ещё соединения -то в них ещё теряется. В сумме может прилично набежать, а выльется это в то, что в конце замера вольтметр покажет предельно-минимальное напряжение, а на самом деле оно ещё не будет минимальным. Лечить качественным держателем для аккумулятора, и всеми остальными соединениями только на пайке, без разъёмов и толстым проводом. Ну и всё калибровать по точным приборам..
dimax почему 10мВ?
5В/1024=4,88мВ. Как делитель влияет на эту цифру? Конечно можно на AREF подать 2,5В тем самым уменьшить LSB, но надо ли это?
По поводу оверсемплинг вы наверное правы.
На держателе действительно есть просадка напряжения около 20мВ. Мне кажется это не критично.
Вопрос в другом, правильно ли я делаю сам принцип замера? Может нужно понизить ток.
Сколько искал в интернете не нашел как правильно надо мерить ёмкость этих аккумуляторов. А точнее как её меряют китайци.
Zahar, резисторный делитель увеличивает LSB пропорционально коэффициенту деления. Другими словами при подачи 5 вольт на вход вашей схемы АЦП должен был показать значение 1024 , а будет 512. 5000/512 = 10мв Подать на AREF 2,5 вольта очень хорошая мысль, тем более достаточно tl431 и одного резистора.
Принцип расчёта тока как я понял у вас канонический. Другое дело, что нужно понимать, что понятие "мАч" -это попугаи. Эта цифра не говорит не о чём, т.к. напрямую зависит от тока нагрузки и от внутреннего сопротивления аккмулятора. Будете разряжать током 50мА -будёт ёмкость к примеру 5000мАч, а током 500мА -1000мАч,(цифры с потолка, просто для примера).
У меня есть один рабочий проект -специализированный прибор самодельный, предназначенный для тестирования и измерения ёмкости -спайки трёх мизинцев Ni-Mh аккмуляторов. На этой спайке написана ёмкость 700mAh, когда я измеряю их ёмкость , разряжая током примерно 350mA то новые аккумы показывают цифру около 700мАч. Т.е. можно сделать вывод, что новый аккум должен показать свою ёмкость при токе разрядки равным половине его ёмкости. Разумеется это моё личное наблюдение, и ГОСТом оно не является :)
Но тут как я уже писал вас ждёт засада -при больших токах напряжение будет теряться везде, в каждом проводке, на каждом контакте. Поэтому нужно делать всё очень качественно, либо существенно снижать ток.
А если я включу встроенное опорное напряжение 1.1В и пересчитаю делитель так чтоб на его выходе было соответственное напряжение (4,2В->1,1В)?
"Принцип расчёта тока как я понял у вас канонический." - что такое канонический? Я в программе вообще не меряю ток. Ток у меня это константа которую я просто померил "Fluke 77".
""мАч" -это попугаи" - ну это не совсем так. Теоретически в идеале не должно быть разници каким током разряжать, если батарейку 1000мАч разряжать 1А 1ч = 1Ач или 0,5А 2ч =1Ач. Я понимаю что ничего идеального нет, но долже же быть какой то НЕ идеальный стандарт для подсчета.
"Поэтому нужно делать всё очень качественно" - все сделанно очень качественно. ПП сделанно на професиональном станке CNC "LPKF PROTOMAT C60"
К сожалению я не сфоткал саму печатку, вытаскивать с корпуса не хочеться.
"У меня есть один рабочий проект" - если не сложно скинь его пожалуйста. Хочеться сравнить
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 }Думается мне, что в схеме разряжающего генератора тока есть ошибка. Нужно бы поправить. Вдруг кто повторить попробует, а оно правильно работать не будет (а может и в железе что нибудь не так).
Есть у меня автомобильная зарядка с функцией замера ёмкости. Так овт заметил, что от тока разряда, меняется и ёмкость при прочих равных. И в чистом виде формула типа "ток на время" - упорно не хочет работать. Статистических данных пока не набрал. Цикл заряд-разряд у меня длится почти неделю.
dimax ,большое спасибо.
Я всё таки решил уменьшить ток разряда и изменить ИОН. Для этого пришлось разобрать приборчик.
Вот сделал более качественные фотки.
Закончу тестировать обязательно отпишусь.
gena я не вижу никакой ошибки. Если не трудно, в чём ошибка?
Как я понимаю, провод, идущий к LS1 (для подключения к земле через реле - для разряда заданным током) должен стартовать в вывода 1 (ADJ), а не так как по схеме.
Единственная ошибка которую я сейчас заметил, были закорочены 6,5 и 7 ножки у LS1, исправил.
Всё остальное правильно.
Может так понятнее будет http://s017.radikal.ru/i440/1611/b4/49267ef11cde.jpg Фрагмент даташита.
Я стараюсь рисовать на схеме такие детали как трёхвыводные стабилизаторы так, как они выглядят внешне, физически. Разбираться в цоколёвке по нумерации - заранее иметь возможность нахомутать. Что уже и наблюдаем дляU3, U4 LM7805 http://www.microcontroller.it/Tutorials/PIC/AD/Vref_3.htm
gena вы абсолютно правы, не понимаю как я так фраернулься.
Ошибка была только на схеме, на ПП всё правильно. :)
О. Да здесь Клондайк ошибок!
Ваши реле НИКОГДА не включатся.
Не мешало бы где то указать, какой ток заряда даёт модуль на TP4056. Ведь из Китая они настроены на 1 А . Думаю это много.
Резисторы R2, R3 я бы применил номиналом побольше - зачем напрягать микроконроллер лишним током?
А где в схеме R1? Наверно был подтяжкой для кнопки? (Это было бы правильно, а сейчас - не очень).
Почему светодиоды запитаны именно так? Возможно нагляднее был бы один двухцветный (трёхцветный).
А аккумуляторная батарея BT1 разве состоит из двух ячеек?
Жаль схема не очень просматривается. Может ещё что высмотрел бы (я тоношный в этом отношении). Проэкт походит на курсовую работу, значит надо сделать достойно. По нему студентам ещё защищаться и защищаться (:-)).
Если отказаться от U3, U4 то можно запитаться от пятивольтового источника питания (USB, зарядное устройство для мобильников). Это даёт бОльшую мобильность Вашему устройству. А при запитывании от USB, можно в РС посылать отчёты (через виртуальный СОМ порт). Строить график on-line, затем его сохранять. Есть весьма достойная программа для этого.
Имею ещё минимум три замечания, но это уже после отработки вышеизложенных.
Уже четыре.
gena вы меня опустили ниже пола :)
1) Всё включаеться и работает.
2) TP4056 действительно 1А, разве это много? Он ведь предназначен именно для этих аккумуляторов.
3) Изначально R2, R3 стояли 1к но реле не включалос пожтому опустил до 100Ом.
4)R1 резистор в нагрузке. Подтяжка кнопки програмная.
5) Светодиоды подключил так, потомучто изначально про них не подумал, а когда вспомнил про них просто не оказалоь земленного пина на ПП.
6) Просто не нашёл в OrCad нормального значка батарейки, применил что было. Хота наверное надобыло перерисовать этот значёк.
7) Честное пионерское, я уже давно не студент.
8) По поводу графиков. Если бы ток не был постоянным тогда имело бы смысл делать графики, а так не понятно какой график делать.
То, что всё работает, у Вас - я не сомневаюсь. А если собрать по схеме (а анализирую я ТОЛЬКО её) - работать не будет. Ищите, или "сдавайтесь" - подскажу. С улюбкой вспоминаю фразу из журнала "Радио" : "При заведомо исправных деталях и правильно собранной схеме устройсто в наладке не нуждается и работать начинает сразу". Ага. Щас. Сколько дней проходило пока удавалось его запустить.
п.2 Для этих аккумуляторов ток заряда вполне нормален. Я это написал для "последователей", использующих устройство для других аккумуляторов.
п.3 Номиналы желательно рассчитать, исходя из коэффициента усиления транзистора.
п. 4 Я не прав. Схема немного размыта + я не внимателен.
п. 5 "Хозяин - барин".
п.7 Вырисовывается неплохое устройство для курсовой. Пусть учатся хорошей схемотехнике и грамотному подходу к делу.
п. 8 Можно было бы смотреть график изменения напряжения (как заряда, так и разряда). Думаю это информативно.
gena я не понял, я исправил ошибку или нет?
Если нет то я "сдаюсь" , начертите пожалуйста что не так.
Посмотрите, от чего запитываются реле? (..Семён Семёныч..).
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"); }А у транзисторов 2N5551 при токе Ic = 50 мА коэффициент услиления по току, hfe, (минимальный) равен 30. Вот его то и нужно брать для рассчёта резистора R2 и R3. Допустим реле потребляет 60 мА. Тогда сопротивление резистора R2:
R2 = (5-0,7)/(0,06/30) = 4,3/(0,002) = 2150 Ом
Выбираю в меньшую сторону, т.е. 2 кОм.
Релле TQ2-12v потребляет 11,7мА, при этом токе hfe=100
Выскажу оставшиеся мысли по поводу схемы.
gena , хочу сказать вам огромное спасибо за вашу помощь и дельные советы.
1) Нумерацию исправил.
2) Кнопка NO, просто на картинке почему то это плохо видно.
3) По поводу генератора вы абсолютно правы. Под конец разряда ток действительно проседает хоть и не значительно но просадка есть.
Я также, думал сделать регулируемый генератор тока, но делать его на LM317 мне кажеться это не очень хорошая идея. Хотелось бы сделать на ОУ и электронным потенциометре MCP41010. Про схему надо ещё подумать.
4) Значок провода "V_battary" сместил.
Может посмотреть наподобие LM1117 800-mA Low-Dropout Linear Regulator. Для увеличения тока поставить параллельно несколько, на разные токи. Хотя и варианты с ОУ можно придумать.
Zahar, наш коллега а5021 давал тут схемку классического стабилизатора тока с шим-управлением. Вот прямо в самый раз в вашем случае.
Опередили меня. Тоже возникло такое же предложение. Вот только транзистор там мне не очень нравится - открывается после четырёх Вольт на затворе. И ОУ я поставил бы с полевиками на входе.
Интересны полевики от материнских плат РС.
Посоветуйте какой из этих ОУ применить LM358, RC4558, TL072, LM324, LM339.
Вместо RV1 будет стоять MCP41010.
При запитывающих напряжения как на схеме, думаю можно начать с LM358.
Zahar, LM339 это компаратор) А что вам эти потенциометры сдались?? Взять простой ЦАП, те-же деньги, а уже 4000 градаций. Хотя я бы сделал на ШИМ, на первом таймере хоть хоть 65 тыщ градаций можно получить...
dimax ШИМ я не хочу. Точность про которую вы говорили в первых постах мне кажеться не возможно добиться ШИМом.
"А что вам эти потенциометры сдались??" - У меня есть эти потенциометры.
Также у меня есть ЦАП MPC4921 12bit, может сделаю на нём, надо поэксперементировать. Правда я не знаю как лютше сделать, забить четкие значения для ЦАП или дать контроллеру самому доходить до нужного значения тока?
Я бы отдал предпочтение ЦАП. Практически подобрать пяток разрядных токов. Применяя самоконтроль контроллером точность будет зависить от его собственного АЦП. А вообще нужно что-то пробовать. Там разные результаты могут быть. И по точности и по термостабильности.
Zahar, точность важна при измерении напряжения и тока ,а в управлении стабилизатором тока точность не критична. Подогнать до примерно нужной величины, и всё. Можно в принципе и циф.потенциометром, коли он уже есть у вас. Но по сравнению с ШИМом вы не получите выигрыша не в чём.
По настовлению Гены я всё таки переделал приборчик.
//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мА. Но так как прграмма считает замеренный ток а не установленный то все расчёты верны.
а можно ли переделать под 3 лития?каждый тестируется отдельно
наверно нельзя...буду прорываться сам...