Зарядка на ардуино

Arduino.ru

Привет, умные люди! Вообщем эта тема еще не умерла) Просто свободного времени не так много, как хотелось бы. Я много читал зарисовки Сороки, часто засыпая. Сейчас я приведу цитаты из его записей, наиболее интересные, на мой взгляд, и сразу сделаю выводы, что не так страшен чёрт!)

Вся «логика» пишется на AVRe — а если и AVRу написать простенький ШИМ-программу (есть примеры в инете) — то все упрощается до безобразия: ТОЛЬКО выходной каскад и пара сопротивлений — И ВСЕ !!!
Решение 21 века — просто, дешево, функциональность гибкая и изменяемая как угодно. Контроль тока хочется ? на одном операционнике собирается из 4-х элементов — два кондера + сопротивление + микросхемка на 8 выводов …собирается с перекурами за 30 минут…

…это очень сложный вопрос… Я вот столкнулся с интересным фактом — моя зарядка использует ШИМ с частотой примерно (на слух) до 50Гц — так вот, при токах в 1.5А (для моих 12Ач акков) заполнение ШИМа получается примерно до 20%.

Конечно, о сути только догадываться приходится, конкретно его разработки это комерческая тайна, как я понял, и цена ей на этот момент 50$. Сейчас алгоритм работы, исходя из слов того, кто ей пользовался.

Так вот — наблюдения показали, что акки оно заряжает до «первой отсечки» примерно за 3.5-4 часа, а потом переходит в режим «добивки».
Причем паузы между подачами тока — от 20сек до минут (в конце добивки).
По всем «мануалам и рекомендациям» получается что надо тупо 12-15 часов «дуть» в акк 13.5В не смотря на то что он «уже все» вот и получается что мое детище не только экономит эл.энергию и не допускает перезаряда акков, но еще и работает в наиболее оптимальном для акков режиме.
С точки зрения электрохимии — как раз все красиво укладывается: при подаче импульса тока от ШИМ, потом следует пауза — в течении которой «усваивается» ток, т.е. завершается хим реакция и выравнивается (диффузия) плотность электролита. Т.е. низкие частоты ШИМа + низкая скважность = полное «усвоение» подведенной энергии.

Процесс зарядки (полный) выглядит примерно так: первые 3-4 часа идет постоянная подача выставленного вами тока, потом «первая отсечка»(напряжение аккума достигло 14.4В) и потом с разной частотой идет адаптивная добивка — еще примерно 2-3часа. При постановке «на ночь» в зарядку — просто с утра посмотрели на индикатор — напруга не падает ? значит можно выключать.

И еще несколько интересных предложений по теме:

Aлгоритмы «умного заряда», согласно рекомендаций DIN — «капельная зарядка» если напряжение аккума ниже 12В, то на него подаются импульсы чуть менее 1сек с повышением скважности при приближении к 12В.

А так как формула оценки напряжения аккума (одной банки) пишется так: 0.84+плотность электролита, то мы имеем неслабый разброс параметров И «формовка АКБ».на самом деле приводит к тому что часть аккумов хронически недозаряжена, а другая часть тупо кипятит электролит …
Например: в аккуме у нас плотность электролита 1.24. Тогда ЭДС будет: 0.84+1.24=2.08*6=12.48_В и это 100% его зарядки !!!

Большинство производителей СА рекомендуют 20 часовой разряд токами в 0.05С до 1.8В/элемент (т.е. до 10.8В на 12Вольтовом СА, измеренные под нагрузкой, или не ниже 12В без нагрузки). 10-и часовой разряд будет примерно при 0.1С. ТОЛЬКО НЕ забывайте что нельзя разряжать 12В СА ниже 10.8В !!!

Вчера я сделал код. Кратко расскажу о нем, дабы быстрее ввести Вас в суть. В нем есть 5 режимов работы: режим блока питания (регулируется напряжение), режим стабилизации заданного тока, режим заряда по заданному времени с заданным током, режим разряда до заданного напряжения, и режим заряд\разряд. Режимы переключаются кнопкой reset или выключением\включением блока. Используется 10 битный ШИМ — может это излишне? Сорока пишет, что необходима низкая скважность. Включайтесь, друзья, в обсуждение. Буду рад любому комментарию и разумной критике. Ниже приведен код.

#include <LiquidCrystalRus.h>; #include <EEPROM.h> LiquidCrystalRus lcd(7, 8, 6, 10, 11, 12); //rs, e, d4, d5, d6, d7 float umax = 15.5; //максимальное напряжение byte Imax = 2; byte umin = 0; //минимальное напряжение #define pwm 9 #define power 2 long maxpwm; //циклы поддержки максимального ШИМ float level, Iz, Uz, Uout, Iout, average; // переменная хранит заданное напряжение int incomingByte; byte i; long prevmicros = 0;//переменная для хранения значений таймера byte sek, minu, chas;//значение часов boolean counter = false; // счетчик для полусекунд boolean zr; //для работы в режиме заряд/разряд byte zz; void EEPROM_float_write(int addr, float val) // запись в ЕЕПРОМ {byte *x = (byte *)&val; for(byte i = 0; i < 4; i++) EEPROM.write(i+addr, x);} float EEPROM_float_read(int addr) // чтение из ЕЕПРОМ {byte x; for(byte i = 0; i < 4; i++) x = EEPROM.read(i+addr); float *y = (float *)&x; return y;} void setup() { cli(); DDRB |= 1<<1 | 1<<2; PORTB &= ~(1<<1 | 1<<2); TCCR1A = 0b00000010; //TCCR1A = 0b10100010; TCCR1B = 0b00011001; ICR1H = 255; ICR1L = 255; sei(); int pwm_rez = 13; pwm_rez = pow(2, pwm_rez); ICR1H = highByte(pwm_rez); ICR1L = lowByte(pwm_rez); pinMode(3, INPUT_PULLUP); pinMode(5, INPUT_PULLUP); pinMode(4, INPUT_PULLUP); pinMode(pwm, OUTPUT); pinMode(power, OUTPUT); pinMode(A4, OUTPUT); pinMode(A5, OUTPUT); lcd.begin(16, 2); //lcd.print(» Нелла Котик! «); delay(1000); lcd.clear(); Uz = EEPROM_float_read(0); Iz = EEPROM_float_read(4); EEPROM.write(10, i = EEPROM.read(10) + 1); if (i > 4) EEPROM.write(10, 0); chas = EEPROM.read(11); minu = EEPROM.read(12); } void loop() { switch (i) { case 1: average = 0; for(int i = 0; i < 1000; i++) {average = average + (.0264 * analogRead(A3) — 13.61) / 1000; //измерение тока if (digitalRead(3) == 0) {delay(200); uup();} if (digitalRead(5) == 0) {delay(500); save();} if (digitalRead(4) == 0) {delay(200); udn();} float Ucorr = -0.09; Uout = analogRead(A1) * ((5.0 + Ucorr) / 1023.0) * 5.0; //измерение напряжения if (Iout > Imax + 1) {digitalWrite(power, 0); level = 0; digitalWrite(A4, 1); Uz = 0; Iz = 0; delay(5000); digitalWrite(A4, 0);} //защита от перегруза по току if (level == 8190) {maxpwm++; digitalWrite(A4, 1);} else {maxpwm—; if(maxpwm < 0) {maxpwm = 0; digitalWrite(A4, 0);}} //о максимальном шим if(Uout > Uz) {float raz = Uout — Uz; if(raz > 0.05) {level = level — raz * 20;} else {if(raz > 0.015) level = level — raz * 3;} regulirovka();} //регулировка напряжения if(Uout < Uz) {float raz = Uz — Uout; if(raz > 0.05) {level = level + raz * 20;} else {if(raz > 0.015) level = level + raz * 3;} regulirovka();} Iout = average; lcd.setCursor(1, 0); lcd.print(«Рег. U «); lcd.print(ceil(level),0); lcd.setCursor(0, 1); lcd.print(«U = «); if(Uout < 10) lcd.print(» «); lcd.print(Uout,1); lcd.print(«B»); lcd.setCursor(8, 1); lcd.print(«I = «); lcd.print(Iout,1); lcd.print(«A»); } break; case 2: average = 0; for(int i = 0; i < 1000; i++) {average = average + (.0264 * analogRead(A3) — 13.61) / 1000; //измерение тока if (digitalRead(3) == 0) {delay(200); uup();} if (digitalRead(5) == 0) {delay(500); save();} if (digitalRead(4) == 0) {delay(200); udn();} float Ucorr = -0.09; Uout = analogRead(A1) * ((5.0 + Ucorr) / 1023.0) * 5.0; //измерение напряжения Iout = average; lcd.setCursor(1, 0); lcd.print(«Стаб. I «); lcd.print(ceil(level),0); lcd.setCursor(0, 1); lcd.print(«U = «); if(Uout < 10) lcd.print(» «); lcd.print(Uout,1); lcd.print(«B»); lcd.setCursor(8, 1); lcd.print(«I = «); lcd.print(Iz,1); lcd.print(«A»); } if (Iout > Imax + 1) {digitalWrite(power, 0); level = 0; digitalWrite(A4, 1); Uz = 0; Iz = 0; delay(5000); digitalWrite(A4, 0);} //защита от перегруза по току if (level == 8190) {maxpwm++; digitalWrite(A4, 1);} else {maxpwm—; if(maxpwm < 0) {maxpwm = 0; digitalWrite(A4, 0);}} //о максимальном шим if (Iout > Iz) {float raz = Iout — Iz; if(raz > 0.05) {level = level — raz * 20;} else {if(raz > 0.015) level = level — raz * 3;} regulirovka();} //регулировка напряжения if ((Uout < 14.4) && (Iout < Iz)) {float raz = Iz — Iout; if(raz > 0.05) {level = level + raz * 20;} else {if(raz > 0.015) level = level + raz * 3;} regulirovka();} break; case 3: lcd.setCursor(0, 0); lcd.print(«Зарядка по врем»); lcd.setCursor(0, 1); lcd.print(«Заряжать «); lcd.print(chas); lcd.print(» час»); if (digitalRead(3) == 0) {delay(200); uup();} if (digitalRead(5) == 0) {delay(500); save();} if (digitalRead(4) == 0) {delay(200); udn();} break; case 10: lcd.setCursor(0, 0); lcd.print(«Ток заряда «); lcd.setCursor(5, 1); lcd.print(Iz); lcd.print(» A»); if (digitalRead(3) == 0) {delay(200); uup();} if (digitalRead(5) == 0) {delay(500); save();} if (digitalRead(4) == 0) {delay(200); udn();} break; case 11: lcd.setCursor(0, 0); lcd.print(«Напр не более «); lcd.setCursor(5, 1); lcd.print(Uz); lcd.print(» В»); if (digitalRead(3) == 0) {delay(200); uup();} if (digitalRead(5) == 0) {delay(500); save();} if (digitalRead(4) == 0) {delay(200); udn();} break; case 12: //работа зарядного заданное время с заданными параметрами тока и наряжения average = 0; for(int i = 0; i < 1000; i++) {average = average + (.0264 * analogRead(A3) — 13.61) / 1000; //измерение тока if (micros() — prevmicros > 500000) {prevmicros = micros(); counter=!counter; if (counter == false) {sek—; zz—; lcd.setCursor(3,0); lcd.print(«:»);} else {lcd.setCursor(3, 0); lcd.print(» «);} if(sek > 59) {sek = 59; minu—;} if(minu > 59) {minu = 59; chas—;} } lcd.setCursor(1, 0); if (chas < 10) lcd.print(«0»); lcd.print(chas); lcd.setCursor(4, 0); if (minu < 10) lcd.print(«0»); lcd.print(minu); lcd.setCursor(10, 0); lcd.print(ceil(level),0); float Ucorr = -0.09; Uout = analogRead(A1) * ((5.0 + Ucorr) / 1023.0) * 5.0; //измерение напряжения Iout = average; lcd.setCursor(0, 1); lcd.print(«U = «); if(Uout < 10) lcd.print(» «); lcd.print(Uout,1); lcd.print(«B»); lcd.setCursor(8, 1); lcd.print(«I = «); lcd.print(Iz,1); lcd.print(«A»); } if (Iout > 3) {digitalWrite(power, 0); level = 0; digitalWrite(A4, 1); Uz = 0; Iz = 0; delay(5000); digitalWrite(A4, 0);} //защита от перегруза по току if (level == 8190) {maxpwm++; digitalWrite(A4, 1);} else {maxpwm—; if(maxpwm < 0) {maxpwm = 0; digitalWrite(A4, 0);}} //о максимальном шим if (Iout > Iz) {float raz = Iout — Iz; if(raz > 0.05) {level = level — raz * 20;} else {if(raz > 0.015) level = level — raz * 3;} regulirovka();} //регулировка напряжения if ((Uout < 14.4) && (Iout < Iz)) {float raz = Iz — Iout; if(raz > 0.05) {level = level + raz * 20;} else {if(raz > 0.015) level = level + raz * 3;} regulirovka();} if ((chas == 0) && (minu == 0)) {digitalWrite(power, 0); level = 0; i = 13;} //для работы в режиме заряд/разряд if ((zr == 1) && (zz == 0)) {digitalWrite(power, 0); delay(1000); digitalWrite(power, 1); zz = 20;} break; case 13: if (micros() — prevmicros > 500000) {prevmicros = micros(); counter=!counter; if (counter == false) {sek++; lcd.setCursor(3, 0); lcd.print(«:»); lcd.setCursor(6, 0); lcd.print(«:»);} else {lcd.setCursor(3, 0); lcd.print(» «); lcd.setCursor(6, 0); lcd.print(» «);} if(sek > 59) {sek = 0; minu++;} if(minu > 59) {minu = 0; chas++;} } lcd.setCursor(1, 0); if (chas < 10) lcd.print(«0»); lcd.print(chas); lcd.setCursor(4, 0); if (minu < 10) lcd.print(«0»); lcd.print(minu); lcd.setCursor(7, 0); if (sek < 10) lcd.print(«0»); lcd.print(sek); lcd.setCursor(4, 1); lcd.print(«выполнено»); break; case 4: lcd.setCursor(0, 0); lcd.print(«Разаряд до «); lcd.print(Uz,1); lcd.print(» В»); lcd.setCursor(0, 1); lcd.print(«разр-лся «); lcd.print(chas); lcd.print(«:»); lcd.print(minu); if (digitalRead(3) == 0) {delay(200); uup();} if (digitalRead(5) == 0) {delay(500); save();} if (digitalRead(4) == 0) {delay(200); udn();} break; case 5: lcd.setCursor(0, 0); lcd.print(«Заряд/Разаряд»); lcd.setCursor(4, 1); lcd.print(«Цикл 10/1 «); if (digitalRead(3) == 0) {delay(200); uup();} if (digitalRead(5) == 0) {delay(500); save();} if (digitalRead(4) == 0) {delay(200); udn();} break; case 15: if (micros() — prevmicros > 500000) {prevmicros = micros(); counter=!counter; if (counter == false) {sek++; lcd.setCursor(3,0); lcd.print(«:»); lcd.setCursor(6,0); lcd.print(«:»);} else {lcd.setCursor(3,0); lcd.print(» «); lcd.setCursor(6,0); lcd.print(» «);} if(sek > 59) {sek = 0; minu++;} if(minu > 59) {minu = 0; chas++;} } lcd.setCursor(1, 0); if (chas < 10) lcd.print(«0»); lcd.print(chas); lcd.setCursor(4, 0); if (minu < 10) lcd.print(«0»); lcd.print(minu); lcd.setCursor(7, 0); if (sek < 10) lcd.print(«0»); lcd.print(sek); float Ucorr = -0.09; Uout = analogRead(A1) * ((5.0 + Ucorr) / 1023.0) * 5.0; //измерение напряжения lcd.setCursor(0, 1); lcd.print(«Разарядка «); lcd.print(Uout,1); lcd.print(» В»); if (Uout <= Uz) {EEPROM.write(11, chas); EEPROM.write(12, minu); delay(1500); digitalWrite(power, 0);} break; } } void regulirovka(){ if(level < 0) level = 0; if(level > 8190) level = 8190; if(ceil(level)!= 255) analogWrite(pwm, ceil(level));} void save(){ switch(i){ case 1: lcd.clear(); lcd.setCursor (4, 0); lcd.print(«сохраняю»); EEPROM_float_write(0, Uz); delay(1500); lcd.print(«сохранил»); delay(1500); lcd.clear(); break; case 2: lcd.clear(); lcd.setCursor (4, 0); lcd.print(«сохраняю»); EEPROM_float_write(4, Iz); delay(1500); lcd.print(«сохранил»); delay(1500); lcd.clear(); break; case 11: lcd.clear(); lcd.setCursor (4, 0); lcd.print(«сохраняю»); EEPROM_float_write(0, Uz); delay(1500); lcd.print(«сохранил»); delay(1500); lcd.clear(); i = 12; digitalWrite(power, 1); break; case 10: lcd.clear(); lcd.setCursor (4, 0); lcd.print(«сохраняю»); EEPROM_float_write(4, Iz); delay(1500); lcd.print(«сохранил»); delay(1500); lcd.clear(); i = 11; break; case 3: lcd.clear(); lcd.setCursor (4, 0); lcd.print(«сохраняю»); EEPROM.write(11, chas); delay(1500); lcd.print(«сохранил»); delay(1500); lcd.clear(); i = 10; break; case 4: lcd.clear(); lcd.setCursor (4, 0); lcd.print(«сохраняю»); EEPROM_float_write(0, Uz); delay(1500); lcd.print(«сохранил»); delay(1500); lcd.clear(); i = 15; chas = 0; minu = 0; EEPROM.write(11, chas); EEPROM.write(12, minu); digitalWrite(power, 1); break; case 5: zr = 1; zz = 20; i = 3; break; } } void uup() { switch(i){ case 1: Uz = Uz + 0.1; if(Uz > umax) Uz = umax; if (digitalRead(power) == 0) digitalWrite(power, 1); break; case 2: Iz = Iz + 0.1; if(Iz > Imax) Iz = Imax; if (digitalRead(power) == 0) digitalWrite(power, 1); break; case 3: chas++; if (chas > 20) chas = 20; break; case 10: Iz = Iz + 0.1; if(Iz > 2) Iz = 2; break; case 11: Uz = Uz + 0.1; if(Uz > umax) Uz = umax; break; case 4: Uz = Uz + 0.1; if(Uz > umax) Uz = umax; break; case 5: zr = 1; zz = 20; i = 3; break; } } void udn() { switch(i){ case 1: Uz = Uz — 0.1; if(Uz < umin) Uz = umin; break; case 2: Iz = Iz — 0.1; if(Iz < 0.1) Iz = 0.1; break; case 3: chas—; if (chas > 20) chas = 0; break; case 10: Iz = Iz — 0.1; if(Iz < 0.1) Iz = 0.1; break; case 11: Uz = Uz — 0.1; if(Uz > umax) Uz = umax; break; case 4: Uz = Uz — 0.1; if(Uz > umax) Uz = umax; break; case 5: zr = 1; zz = 20; i = 3; break; } }

Arduino тестер емкости батареи своими руками
Проект очень простой и основан на законе Ома.

Шаг 1: Элементы и необходимые инструменты
1. Arduino Nano
2. 0,96 «OLED-дисплей
3. МОП-транзистор — IRLZ44
4. Резисторы (4 х 10кОм, 1/4W)
5. Резисторы (10кОм, 10Вт)
6. Винтовые клеммы (3 NOS)
7. Зуммер
8. 18650 Батарея
Необходимые инструменты:
1. Кусачки
2. Припой
Шаг 2: Схема и принцип работы
Схема:
Конструкция очень проста, который основан на Arduino Nano. OLED-дисплей используется для отображения параметров. Винтовые клеммы используются для подключения аккумуляторной батареи и нагрузки сопротивления. Зуммер используется для подачи сигнала тревоги. Два делители напряжения цепи используются для контроля напряжения на сопротивлении нагрузки. МОП-транзистор для возможности подключать или отключать сопротивление нагрузки с батареей.
Принцип работы
Arduino проверяет состояние батареи, если батарея нормальная, дает команду на ВКЛЮЧЕНИЕ полевого транзистора. Это позволяет току проходить от положительной клеммы батареи, через резистор, МОП-транзистори затем завершает путь обратно к отрицательному полюсу. Это разряжает батарею в течение периода времени. Arduino измеряет напряжение на нагрузке, а затем разделяет на сопротивление, чтобы выяснить, ток разряда. Умножаем это на время, чтобы получить значение миллиампер-час (мощности).
Шаг 3: Напряжение, ток и емкость измерения
Измерение напряжения
Мы должны найти напряжение на нагрузке. Напряжения измеряются с использованием двух делителей напряжения цепи. Он состоит из двух резисторов со значениями 10кОм каждый. Он подключен к Arduino аналоговый пин A0 и A1.
Измерение тока:
Ток (I) = Напряжение (V) — падение напряжения на MOSFET / сопротивления (R)
Делим на 1000, чтобы преобразовать его в миллиамперы.
Таким образом, максимальный ток разряда = 4,2 / 10 = 0.42A = 420mA
Емкость измерения:
Накапливаемый заряд (Q) = Ток (I) X Время (T).
Шаг 4: Выбор нагрузочного резистора
Выбор нагрузки резистора зависит от величины разрядного тока. Допустим, вы хотите разрядить аккумулятор на 500 мА, то значение резистора
Сопротивление (R) = Макс напряжение батареи / ток разряда = 4,2/ 0,5 = 8,4 Ом
Резистор должен рассеивать немного мощности, так что размер действительно имеет значение в данном случае.
Потери тепла = I ^ 2 х R = 0,5 ^ 2 х 8,4 = 2,1 Вт
Поддерживая некоторый запас вы можете выбрать 5W. Если вы хотите больше пользы, берите 10Вт.
Шаг 5: Выбор МОП-транзистора
Когда сигнал 5В (высокий) подается на затвор полевого МОП-транзистора, что позволяет току проходить от положительной клеммы батареи, через резистор, а МОП-транзистор затем завершает путь обратно к отрицательному полюсу. Это разряжает батарею в течение определенного периода времени. Тоисть МОП-транзистор должен быть выбран таким образом, что он может обрабатывать максимальный разрядный ток без перегрева.
Тут использован N-канальный MOSFET-IRLZ44. L показывает, что это логический уровень МОП-транзистор. Усилитель MOSFET логического уровня означает, что он предназначен для включения полностью от логического уровня микроконтроллера. Стандартный МОП-транзистор предназначен для работы от 10V.
Если вы используете IRF сери. МОП-транзисторf, то ононе будет полностью включен путем применения 5V от Arduino. Я имею в виду МОП-транзистор не будет нести номинальный ток. Необходима настройка на этих МОП-транзисторах, дополнительная схема для повышения напряжения на затворе.
Шаг 6: OLED-дисплей
Чтобы отобразить напряжение аккумуляторной батареи, ток разряда и мощность, тут использованл 0,96 «OLED display. Он имеет разрешение 128×64 и использует шину I2C для связи с Arduino. Два контакта SCL (А5), СПУ (А4) в Arduino Uno используются для связи.
Используется библиотека U8glib для отображения parameters.First вы должны загрузить библиотеку U8glib .Затем установил его.
Соединения должны быть следующими
OLED»>Arduino -> OLED
Vcc»>5V —-> Vcc
GND»>GND —> GND
SDA»>A4 —-> SDA
SCL»>A5 —-> SCL
Шаг 7: Звуковой сигнал для предупреждения
Для того, чтобы обеспечить различные предупреждения или оповещения, использован пьезоэлектрический зуммер.
Для различных предупреждений:
1. Низкое напряжение батареи
2. Высокое напряжения батареи
3. Отсутствие батареи
Соединения должны быть следующими
Buzzer»>Arduino -> Зуммер
Positive terminal»>D9 -> Положительный полюс
Negative terminal»>GND -> Отрицательный полюс
Шаг 8: Монтаж стоек
После пайки и монтажа, монтировать распорки на 4 угла. Это обеспечит достаточный зазор для пайки и проводов от земли.
Шаг 9: Программное обеспечение
Программное обеспечение делает следующие задачи
1. Измеряет напряжение
2. Проверяет состояние батареи, чтобы дать сигнал тревоги или начать цикл разряда
3. Отображение параметров на OLED
4. Запись данных на монитор последовательного порта
Код программы Скачать файл: battery_capacity_tester-v1.0.ino.zip (cкачиваний: 1150)
Отказ от ответственности: Пожалуйста, обратите внимание, что вы работаете с литий-ионными аккумуляторами, которые являются взрывоопасными и опасными. Я не могу нести ответственность за любую потерю имущества, повреждения или утраты жизни, если дело дойдет до этого.
Источник