Всем доброго дня!
Случилась как то у меня ситуация, когда в полевых условиях понадобился блок питания, поэтому и возникла идея сооружения даного устройства.
Будет много радиолюбительства, самодельщины и полезных для DIY ссылок.
Конструктивно устройство состоит из блока аккумуляторов, DC-DC преобразователя, AC-DC преобразователя, плат управления, зарядки и индикации.
Принципиальная схема устройства:
Аккумуляторы, 4 шт, были приобретены более 5 лет назад, еще на живом тогда интернет-аукционе aukro.ua. Они должны были питать переносную паяльную станцию на Т12, но проект так и до сих пор нереализован и утратил актуальность.
Аккумулятор:
Позже, они были прогнаны на купленой
здесь еще старой версии Liitokala 500, избавлены от «родных» плат защиты и смотаны в единый блок. Баночки показали емкость чть больше 6 А/ч.
Разделитель — стеклотекстолит 0,5мм, покупался
в Украине. Продают, правда, минимум 2 м.кв. Ну и плата BMS, типа
такой.
Я решил использовать польский корпус Z-2A производства Kradex.
Корпус был приобретен в
украинском магазине. У нас же в стране они есть в
замечательном магазинчике, который занимается корпусами, и, дешевле всего,
здесь
Алюминиевый уголок — из строительного магазина. Он очень легко обрабатывается ножовкой, напильником и слабенькой
дрелькой. Кернер для алюминия —
такой, тоже сильно помог.
Аккумуляторы закрепил стяжкой к корпусу.
Преобразователь
DPH5005 был куплен уже давно, по какой то акции.
Kirich сделал на него очень хороший
обзор. Есть еще хороший
обзор от
Lexus---08. Я брал версию с USB. Модуль преобразователя установлен над аккумулятором, на металлических стойках М3. Я покупал в
местном магазине, но их много и на
али,
Сверху еще на 2-х стойках расположилась плата управления
Плата управления меряет напряжение батареи, температуры батареи, радиаторов силового транзистора и выпрямителя, дросселя блока питания, плавно регулирует скорость вентилятора и коммутирует питание преобразователя. еще обеспечивается включение вентилятора на постоянную небольшую мощность во время зарядки и когда преобразователь включает свой обдув. Индикация заряда батареи и температур греющихся узлов осуществляется 8-ю светодиодами и переключается кнопкой.
В закромах был найден древний PIC16F73, по современным меркам — мало на что пригодный. Решил дать ему шанс на жизнь.
Плата управления
Программа для контроллера:
//PIC16F73
//UPS power source measurer
//Defines
#define CHARGING PORTC.F0
#define POW_SW PORTC.F1
#define POW_CONT PORTC.F3
#define BUTTON PORTC.F4
#define FAN PORTC.F5
#define TEMP_LO 3592.0 // Resistance for low temp, 50'C
#define TEMP_AL_OFF 1735 // Resistance for alarm off, 70'C
#define TEMP_HI 1243.0 // Resistance for high temp, 80'C
#define TEMP_AL_ON 782 // Resistance for alarm on, 95'C
#define INITIAL_FAN_SPEED 50 // Minimal PWM for proper fan operate, 20%
#define CHARGE_FAN_SPEED 75 // PWM for fan if charging, 30%
#define DPH_COOLING_FAN_SPEED 180 // PWM for fan if DPH fan is on, 70%
//Variables
char *digittext = "00000";
int battery_ADC, T_bat_ADC, T_coil_ADC, T_mos_ADC, T_rec_ADC;
unsigned long T_bat_res, T_coil_res, T_mos_res, T_rec_res;
char ADC_channel;
char count_resistance;
float flt_val;
long int fan_task, fan_speed;
bit bat_overH, coil_overH, mos_overH, rec_overH, overheat;
bit bat_hot, coil_hot, mos_hot, rec_hot;
bit batt_ok;
bit blink;
bit send_uart;
bit alarm_off;
char alarm_timer;
bit blink_long;
char blink_long_timer;
char display_mode;
char display_data;
bit button_pressed;
//*************************************************************************************
//battery charge
//Level 0 1 2 3 4 5 6 7 8
//voltage 3.1 3.2 3.4 3.5 3.6 3.7 3.8 3.9 4
//voltage 12.4 12.8 13.6 14 14.4 14.8 15.2 15.6 16
//ADC 182 190 202 208 214 220 226 233 239
const char battery_levels[9] = {182, 190, 202, 208, 214, 220, 226, 233, 239};
//*************************************************************************************
//temperature resistances
//level 0 1 2 3 4 5 6 7
//deg 55 60 65 70 75 80 85 95
//resist 2972 2472 2066 1735 1465 1243 1059 782
const int temper_resistances[8] = {2972, 2472, 2066, 1735, 1465, 1243, 1059, 782};
void WriteInt(long number){
digittext[0] = (number/10000)%10 + 48;
digittext[1] = (number/1000)%10 + 48;
digittext[2] = (number/100)%10 + 48;
digittext[3] = (number/10)%10 + 48;
digittext[4] = number%10 + 48;
UART1_Write_Text(digittext);
}
void Display() {
if (display_mode == 0)
{
WriteInt(Battery_ADC);
UART1_Write_Text("; ");
WriteInt(fan_speed);
if (~(batt_OK)) UART1_Write_Text(" B");
if (overheat) UART1_Write_Text(" H");
}
if (display_mode == 1) WriteInt(T_mos_res);
if (display_mode == 2) WriteInt(T_rec_res);
if (display_mode == 3) WriteInt(T_coil_res);
if (display_mode == 4) WriteInt(T_bat_res);
UART1_Write('\r');
}
void TemperatureGauge(long value) {
display_data.F1 = (value < temper_resistances[0]);
display_data.F0 = (value < temper_resistances[1]);
display_data.F3 = (value < temper_resistances[2]);
display_data.F2 = (value < temper_resistances[3]);
display_data.F5 = (value < temper_resistances[4]);
display_data.F4 = (value < temper_resistances[5]);
display_data.F7 = (value < temper_resistances[6]);
display_data.F6 = (value < temper_resistances[7]);
}
int Fan_power(unsigned long resistanse) {
int result = (((INITIAL_FAN_SPEED - 255)*(resistanse - TEMP_HI))/(TEMP_LO - TEMP_HI)) + 255;
return result;
}
unsigned long Get_resistanse(int value) {
unsigned long result;
{flt_val = 255.0 / value; flt_val = flt_val + (-1); result = 10000.0 / flt_val;}
return result;
}
void interrupt() {
if (INTCON.TMR0IF) {
if (ADC_channel == 0) Battery_ADC = ADRES;
if (ADC_channel == 1) T_mos_ADC = ADRES;
if (ADC_channel == 2) T_rec_ADC = ADRES;
if (ADC_channel == 3) T_coil_ADC = ADRES;
if (ADC_channel == 4) T_bat_ADC = ADRES;
count_resistance = ADC_channel;
ADC_channel ++;
if (ADC_channel > 4) ADC_channel = 0;
if (ADC_channel == 0) ADCON0 = 0b01000001;
if (ADC_channel == 1) ADCON0 = 0b01001001;
if (ADC_channel == 2) ADCON0 = 0b01010001;
if (ADC_channel == 3) ADCON0 = 0b01011001;
if (ADC_channel == 4) ADCON0 = 0b01100001;
//ADCON0 = 0b10000001 + (ADC_channel << 3);
//ADCON0.GO = 1;
Delay_ms(2);
ADCON0 = ADCON0 + 0b00000100;
INTCON.TMR0IF = 0;
}
if (PIR1.TMR1IF) {
//Test = ~(Test);
if ( (batt_OK) && (~(overheat)) ) alarm_timer = 0; else {if (alarm_timer < 100) alarm_timer++;}
if (alarm_timer > 38) alarm_off = 1; // 10 000 / 262 (mS)
blink = ~(blink);
blink_long_timer++;
if (blink_long_timer > 4) // 4
{
blink_long_timer = 0;
//blink_long = 0;
//blink_long = 1;
blink_long = ~(blink_long);
}
send_uart = 1; //Display();
PIR1.TMR1IF = 0;
}
}
void main() {
//OPTION_REG = 0b10000101; //pull_ups off, TMR0 = 1*64*256=16384mkS
OPTION_REG = 0b10000110; //pull_ups off, TMR0 = 1*128*256=32768mkS
T1CON = 0b00100001; //Enable timer 1, ovf = 1*4*65535 = 262140 mS
ADCON0 = 0; //0b10000001; //Fosc/32, ADC enable
ADCON1 = 0b00000000; //All for ADC, left alligment
//CMCON = 0b00000111;
TRISA = 0b11111111;
TRISB = 0b00000000;
TRISC = 0b10110011;
// INTCON = 0; //interrupt stop
INTCON = 0b11100000; //interrupt on periphery and TMR0
PIE1 = 0b00000001; // TMR1 ovf interrupt enable
CCP1CON = 0b00001100; //CCP1 as PWM mode
PR2 = 255; //4096 mks
CCPR1L = 0;
T2CON = 0b00000110; //Tmr2 on, prescale 16, for 244Hz PWM
PortA = 0;
PortB = 0;
PortC = 0;
UART1_Init(9600);
// UART1_Write_Text("Hello !");
alarm_off = 0;
alarm_timer = 0;
ADC_channel = 0;
display_mode = 0;
while(1)
{
batt_ok = (Battery_ADC >= battery_levels[0]);
//**************************************************************************************
if (display_mode == 0)
{
if (~(batt_ok)) display_data.F1 = blink;
else {if (~(CHARGING)) display_data.F1 = blink_long; else display_data.F1 = (Battery_ADC >= battery_levels[1]);}
display_data.F0 = (Battery_ADC >= battery_levels[2]);
display_data.F3 = (Battery_ADC >= battery_levels[3]);
if ((mos_hot)|(T_mos_res>80000)) display_data.F2 = blink; else display_data.F2 = (Battery_ADC >= battery_levels[4]);
if ((rec_hot)|(T_rec_res>80000)) display_data.F5 = blink; else display_data.F5 = (Battery_ADC >= battery_levels[5]);
if ((coil_hot)|(T_coil_res>80000)) display_data.F4 = blink; else display_data.F4 = (Battery_ADC >= battery_levels[6]);
if ((bat_hot)|(T_bat_res>80000)) display_data.F7 = blink; else display_data.F7 = (Battery_ADC >= battery_levels[7]);
if (~(FAN)) display_data.F6 = blink; else display_data.F6 = (Battery_ADC >= battery_levels[8]);
}
if (display_mode == 1)
{
if (blink_long) display_data = 0b00000100; else TemperatureGauge(T_mos_res);
}
if (display_mode == 2)
{
if (blink_long) display_data = 0b00100000; else TemperatureGauge(T_rec_res);
}
if (display_mode == 3)
{
if (blink_long) display_data = 0b00010000; else TemperatureGauge(T_coil_res);
}
if (display_mode == 4)
{
if (blink_long) display_data = 0b10000000; else TemperatureGauge(T_bat_res);
}
//**************************************************************************************
PORTB = display_data;
//**************************************************************************************
if ( (~(BUTTON)) && (~(button_pressed)) )
{
Delay_ms(10);
if (~(BUTTON))
{
display_mode ++;
if (display_mode > 4) display_mode = 0;
blink_long = 1;
blink_long_timer = 0;
button_pressed = 1;
}
}
if (BUTTON) button_pressed = 0;
//**************************************************************************************
if (count_resistance > 0)
{
if (count_resistance == 1)
{
//if (T_mos_ADC > 228) T_mos_res = 0; else
//{flt_val = 255.0 / T_mos_ADC; flt_val = flt_val + (-1); T_mos_res = 10000.0 / flt_val;}
T_mos_res = Get_resistanse(T_mos_ADC);
}
if (count_resistance == 2)
{
//if (T_rec_ADC > 228) T_rec_res = 0; else
//{flt_val = 255.0 / T_rec_ADC; flt_val = flt_val + (-1); T_rec_res = 10000.0 / flt_val;}
T_rec_res = Get_resistanse(T_rec_ADC);
}
if (count_resistance == 3)
{
//if (T_coil_ADC > 228) T_coil_res = 0; else
//{flt_val = 255.0 / T_coil_ADC; flt_val = flt_val + (-1); T_coil_res = 10000.0 / flt_val;}
T_coil_res = Get_resistanse(T_coil_ADC);
}
if (count_resistance == 4)
{
//if (T_bat_ADC > 228) T_bat_res = 0; else
//{flt_val = 255.0 / T_bat_ADC; flt_val = flt_val + (-1); T_bat_res = 10000.0 / flt_val;}
T_bat_res = Get_resistanse(T_bat_ADC);
}
count_resistance = 0;
}
//**************************************************************************************
fan_speed = 0;
if (T_mos_res > TEMP_LO) fan_task = 0;
else if (T_mos_res < TEMP_HI) fan_task = 255;
else fan_task = Fan_power(T_mos_res);//(((INITIAL_FAN_SPEED - 255)*(T_mos_res - TEMP_HI))/(TEMP_LO - TEMP_HI)) + 255;
if (fan_task > fan_speed) fan_speed = fan_task;
if (T_rec_res > TEMP_LO) fan_task = 0;
else if (T_rec_res < TEMP_HI) fan_task = 255;
else fan_task = Fan_power(T_rec_res);//(((INITIAL_FAN_SPEED - 255)*(T_rec_res - TEMP_HI))/(TEMP_LO - TEMP_HI)) + 255;
if (fan_task > fan_speed) fan_speed = fan_task;
if (T_coil_res > TEMP_LO) fan_task = 0;
else if (T_coil_res < TEMP_HI) fan_task = 255;
else fan_task = Fan_power(T_coil_res);//(((INITIAL_FAN_SPEED - 255)*(T_coil_res - TEMP_HI))/(TEMP_LO - TEMP_HI)) + 255;
if (fan_task > fan_speed) fan_speed = fan_task;
if (T_bat_res > TEMP_LO) fan_task = 0;
else if (T_bat_res < TEMP_HI) fan_task = 255;
else fan_task = Fan_power(T_bat_res);//(((INITIAL_FAN_SPEED - 255)*(T_bat_res - TEMP_HI))/(TEMP_LO - TEMP_HI)) + 255;
if (fan_task > fan_speed) fan_speed = fan_task;
if (~(CHARGING)) fan_task = CHARGE_FAN_SPEED; else fan_task = 0;
if (fan_task > fan_speed) fan_speed = fan_task;
if (~(FAN)) fan_task = DPH_COOLING_FAN_SPEED; else fan_task = 0;
if (fan_task > fan_speed) fan_speed = fan_task;
CCPR1L = fan_speed;
//***************************************************************************************
if (T_mos_res < TEMP_AL_ON) mos_overH = 1;
if (T_mos_res < TEMP_HI) mos_hot = 1;
if (T_mos_res > TEMP_AL_OFF) { mos_overH = 0; mos_hot = 0;}
if (T_rec_res < TEMP_AL_ON) rec_overH = 1;
if (T_rec_res < TEMP_HI) rec_hot = 1;
if (T_rec_res > TEMP_AL_OFF) { rec_overH = 0; rec_hot = 0;}
if (T_coil_res < TEMP_AL_ON) coil_overH = 1;
if (T_coil_res < TEMP_HI) coil_hot = 1;
if (T_coil_res > TEMP_AL_OFF) { coil_overH = 0; coil_hot = 0;}
if (T_bat_res < TEMP_AL_ON) bat_overH = 1;
if (T_bat_res < TEMP_HI) bat_hot = 1;
if (T_bat_res > TEMP_AL_OFF) { bat_overH = 0; bat_hot = 0;}
overheat = mos_overH | rec_overH | coil_overH | bat_overH;
//***************************************************************************************
POW_CONT = ( (POW_SW) && (~(alarm_off)) );
if ( (~(POW_SW)) && (batt_OK) && (~(overheat)) ) alarm_off = 0;
//***************************************************************************************
if (send_uart)
{
send_uart = 0;
//{WriteInt(T_mos_res); UART1_Write_Text("; "); WriteInt(fan_speed); UART1_Write('\r');}
Display();
}
}
}
Параметр для индикации определяется значением переменной display_mode.
При 0 — отображается шкала напряжения. Иначе — перемигиваются шкала температуры и индикатор выбраного узла.
Для измерения температуры используются NTC термисторы на 10к и характеристикой B3600. Он включен последовательно с резистором на 10к. Общая точка их соединения — подключается к входу МК.
Сопротивление термистора вычисляется по формуле
resistance = REF_VALUE / ( (ADC_RECOLUTION / ADC_value) — 1), где
REF_VALUE — сопротивление постоянного резистора;
ADC_RECOLUTION — максимальное значение АЦП;
ADC_value — поточное значение АЦП;
Если постоянный резистор подключить на сторону минуса, а термистор на плюс, формула примет вид:
resistance = REF_VALUE * ( (ADC_RECOLUTION / ADC_value) — 1)
Зная сопротивление термистора — вычисляем температуру:
temperature = ((BETA * DEF_TEMP) / (BETA + (DEF_TEMP * log(resistance / DEF_RES)))) — 273, где
BETA — характеристика термистора, в даном случае 3600;
DEF_TEMP — температура по умолчанию, 25.0 + 273.15;
DEF_RES — сопротивление при температуре по умолчанию, 10000 Ом
Правда, в моем случае — PICу оказалось не по силам считать температуру, так что в моей программе все установки на срабатывание сигналов перегрева и регулировки скорости вентилятора — привязаны к сопротивлению термисторов.
Хотелось бы еще обратить внимание на ШИМ микросхему XL4001. Она выдерживает до 40В на входе, может работать как стабилизатор тока. Из минусов — ключ на биполярнике и, соответственно, нагрев при нагрузке более 1А. я ее успешно применяю для питания микроконтроллеров и как драйвер светодиодов в автомобильной технике.
Задняя крышка
Здесь расположен сетевой разьем, вентилятор
Sunon, и USB преобразователь интерфейса преобразователя. Добавлено крепление разьема USB-B.
Передняя панель
Использованы два
тумблера, одна
кнопка и
эти клеммы.
Пульт управления модуля DPH пришлось чуть укоротить. Здесь еще видно плату индикации.
Выкройка отверстий нарисована в Corel Draw. Резка пластика произведена лазером на
этом ЧПУ.
Блок питания AC-DC я использовал на
24В 12,5А
На него — уже
есть обзор от
Kirich
Y-конденсаторы — переехали на разьем питания.
Добавлен разьем
3.96, были перенесены на противоположную сторону диодный мост и выходные конденсаторы, чтобы все поместилось в корпус.
В моем экземпляре радиатор силовых транзисторов не был соединен ни с чем. Сами же силовые транзисторы — в пластиковых корпусах. Я соединил радиатор с минусом, иначе из за помех контроллер заряда постоянно включался на заряд.
Общая компоновка
Между блоком питания и аккумуляторами — на уголках висит плата зарядки. Она обеспечивает заряд батареи током 2,4А. Отдельным тумблером можно понизить напряжение заряда до, примерно, 3,7В на банку, для лучшей сохранности последних. Если ожидается длительное использование прибора в автономном режиме — аккумулятор заряжается на полную.
Контроллер заряда — микросхема CN3765. Вот на нее
обзор. Я их в свое время брал на ТаоБао. Ссылка — в обзоре.
Плата зарядки
Медные лепестки находятся в потоке от вентилятора. Охлаждения — хватает.
На платах использованы
штекера и розетки XH2.54
угловые штекера XH2.54,
обжатые провода с гнездами
Для сильнотоковых цепей использованы разъемы
XT30 и ХТ60
При 50Вт нагрузки от аккумулятора, напряжение на банках сразу же упало до 4 Вольт. Они низкоточные и, видимо, более 3А — для них многовато. Но, до напряжения 3В на банку — батарейка протянула час и 10 мин. Потом плата управления отключила преобразователь, и напряжение плавно поднялось до 3,6В. Еще при 5 Вт нагрузки — аккумулятор тянул 20мин, дальше я включил зарядку.
Часовой тест на 100Вт (больше пока нечем нагрузить) — тоже успешен. Оценить нагрев полупроводниковых компонентов не получилось, так как при нагрузке более 3,5А модуль DPH включает свой вентилятор, следовательно плата управления включила на малые обороты и вентилятор корпуса. Так что радиатор выпрямителя был абсолютно холодный. Чуть теплыми были радиатор силовых транзисторов, магнитопровод трансформатора и дроссель, но все равно менее 55 градусов.
1.Такие аккумы нельзя тулить в легковоспламеняющийся корпус в целях безопасности;
2.Я так понял контроллер управляет обдувом.Эта функция легко реализуется по подобию
БП АТХ при помощи терморезистора(ов) и одного (двух, трех и т д по количеству банок)
транзистора для упрвления работой кулера.
3. Вместо стеклотекстолита лучше бы воздушные промежутки для охлаждения и
возможного надувания.
Я бы такие пакеты даже дома не хранил.
На выезды «без розетки» себе тоже собрал, только карманный вариант.
Скрутил из мелкого модуля с алихи и пары 18650 шоколадок.
Месяц пользуюсь, за исключением изначально дурного управления, одни плюсы. Удобная штукенция!
Только немного смущают надписи на передней панели на трёх языках :)
Это конечно мелочи по сравнению со всем остальным, но наверное надо было выбрать «наш родной» и единственный англицкий.
Может я и не прав. Не могу судить, так как сам бы такой агрегат не собрал бы точно. Ну и до надписей у меня тем более дело и не дошло бы…
Просто это первое, что «бросилось в глаза» при первом беглом просмотре статьи.
На двух — украинском и английском, русского (если вьі имели его ввиду как третий) — там нет
Но вот к реализации есть вопросы, как по мне то слишком плотная компоновка, ухудшает тепловой режим и усложняет обдув, хотя я сам так иногда делаю :)
Аккумуляторы в устройстве это интересно, но опять же, как они поведут себя со временем, лучше бы классические цилиндрические, безопасность была бы повыше, у них в крайнем случае просто клапан разомкнется. Кстати заряжать их лучше в режиме, когда блок наименее нагружен, аккумуляторы плохо относятся к перегреву во время заряда.
Функционал. Здесь фактически его обеспечивает сам преобразователь, остальное выполняет больше сервисные функции, так что вопросов нет
Управление вентилятором это хорошо, но индикация как по мне, излишня, хотя это дело вкуса. Я бы наверное делал так, чтобы оно «само работало», не отвлекая внимание. Либо как вариант, под надписями контролируемых узлов поставил по двухцветному светодиоды и выводил в режиме зеленый-оранжевый-красный :)
Насчет передней панели поддержу комментатора victorz, лучше при оформлении придерживаться какого-то одного языка, смотрелось бы гармоничнее.
Также вопрос к размещению выходных клемм, почему не поставили слева, Вам они не мешают при регулировке энкодером?
Если в общем, то интересно и необычно, автору плюс, видно что старался, да и оформил все красиво и аккуратно, а до этого этапа далеко не у всех доходит.
P.S. Чтобы фотка на заглавной смотрелась аккуратно, она должна быть квадратной, т.е. с одинаковым соотношением сторон, иначе муська искажает изображение.
Крутой комбайн. Реально — крутой.
Но повторить его, уверен, никто не захочет.
Я поступил проще, тот же DPH5005, в стандартном корпусе, а к нему хвостик с ХТ60, на который и блок питания от ноута 19В 3.5А можно, и блок LiPo 3S, и коробочку с 4S 18650, и крокодилы, и прикуриватель.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.