Универсальный солнечный хаб: контроллер заряда, инвертер, пауэрбанк и LED-лампа в одном устройстве
Компоненты и материалы
| Микроконтроллер Atmega8 (Microchip Technology) | 1 |
| Step-down (Buck) преобразователь | 1 |
| SparkFun LED 3W алюминиевая плата (5 шт., тёплый белый) | 1 |
| Дисплей Nokia 5110 | 1 |
| Радиатор SparkFun | 1 |
| N-канальный MOSFET (SparkFun) | 4 |
| P-канальный MOSFET (SparkFun) | 1 |
| Солнечная панель 40 Вт или менее для 12V системы | 1 |
| Li-ion аккумулятор 18650 (SparkFun) | 1 |
| DC-DC 0.9V-5V → USB модуль 5V Boost Step-up | 1 |
| NPN транзистор общего назначения | 1 |
| Трансформатор с центральным отводом 12-0-12/220 | 1 |
| Диод 1N4007 (высокое напряжение, большой ток) | 1 |
| Пин-хедер 36 позиций | 1 |
| Тумблер | 1 |
| Кнопка 12mm (SparkFun) | 1 |
| Arduino Proto Shield | 1 |
| Печатная/веро/макетная плата 8x11 см | 1 |
| Arduino UNO | 1 |
| Arduino Nano R3 | 1 |
| Arduino Mega 2560 | 1 |
| Шоттки диод 5822 (низкое прямое напряжение) | 1 |
| Конденсатор 10 мкФ | 1 |
| Конденсатор 100 нФ | 1 |
Необходимые инструменты и оборудование
| Паяльник |
| Дрель |
| Клеевой пистолет |
Приложения и онлайн-сервисы
| Arduino IDE |
| AutoDesk 123D |
О проекте
О проекте
У меня было несколько солнечных панелей, 12-вольтовых батарей, трансформаторов и прочих деталей, лежавших без дела. Так родилось это устройство — компактное PV-решение на одной плате. Разберём, что за что отвечает:
- Контроллер заряда — регулирует зарядку DC-аккумулятора от солнечной панели и разрядку нагрузкой для продления жизни батареи.
- Инвертер — преобразует DC в AC для питания переменного тока.
- Пауэрбанк — выдаёт 5V DC через USB для зарядки гаджетов/мобильных устройств в автономных условиях.
- Аварийная лампа — сверхяркий LED-источник света для отключений электричества, кемпинга, уличных активностей ночью.
В этом устройстве все эти функции. Последние две работают независимо от встроенного Li-ion аккумулятора. С солнечной панелью (до 100 Вт), 12V свинцово-кислотной батареей и трансформатором можно использовать все возможности.
Прототип на Arduino UNO
Первая версия на плате Uno с минимальным железом и функциями. Дисплей Nokia смонтирован прямо на плате, разработан щит с MOSFET для заряда/разряда. Нет меню, инвертера, ШИМ-заряда и прочих фишек! Только вкл/выкл зарядки и показ напряжений батареи и панели. Работает, но без изюминки.

Полнофункциональная версия на макетной плате
Затем разработана новая версия со следующими возможностями:
- PWM солнечный контроллер заряда до 100 Вт панели
- Мини-инвертер прямоугольного сигнала 60 Вт
- Управление до тремя 12V DC нагрузками
- Основная DC нагрузка с авто вкл/выкл
- Независимый USB пауэрбанк
- Buck-модуль (понижающий преобразователь)
- Аварийная LED-лампа на плате с регулировкой яркости и мигания
Управление двумя кнопками/переключателями: прокрутка и выбор в меню на дисплее Nokia 5110. Очень удобно! Обратная сторона изолирована термоклеем от коротких замыканий на металлических предметах.
Дополнительные удобства: выключатель подсветки дисплея, отделение buck для автономной работы от внутренней батареи.




Меню на Nokia 5110 с кнопками:
Технические детали
Контроллеры заряда бывают On/Off, PWM или MPPT. On/Off — простейшая форма (моя v1) — не дросселирует ток заряда при приближении к полному заряду.
PWM постепенно уменьшает ток заряда по мере наполнения батареи. Преимущества PWM:
- Восстанавливает потерянную ёмкость и десульфатирует батарею
- Повышает способность принимать заряд
- Поддерживает высокие средние ёмкости до 95%
- Выравнивает дрейфящие ячейки — внутренние элементы достигают одинакового потенциала
- Снижает нагрев и газирование, предотвращая потерю электролита
- Замедляет старение и продлевает срок службы системы
Но PWM не извлекает максимум мощности из солнечных панелей, так как тянет панель к напряжению батареи. MPPT решает это — это регулируемый buck-boost преобразователь, извлекающий больше солнечной энергии.
Инвертеры: прямоугольный, модифицированный синус и чистый синус. Прямоугольный — простой, подходит для малых нагрузок (лампы, вентиляторы, паяльники), но не для индуктивных двигателей и чувствительной электроники из-за гармонических искажений.
Модифицированный синус — улучшенный прямоугольный. Чистый синус — лучший для всех нагрузок, но сложен в железе, алгоритмах и дорог.
Buck-преобразователь — понижающий DC-DC. Здесь buck-модуль заряжает 4.2V Li-ion батарею, питание контроллера (Arduino + дисплей), независимый USB пауэрбанк, LED-лампу.

USB пауэрбанк — это DC-DC boost, преобразующий 3.3–4.2V от Li-ion/LiPo в 5V USB для зарядки мобильных устройств. Также имеет buck для заряда своей батареи. В проекте buck-модуль берёт энергию от панели для заряда внутренней батареи.
Принцип работы железа
Схема всех подключений:

Мозг — AVR Atmega8A (можно Atmega328P с небольшими правками), запрограммирован в Arduino.

Свежий Atmega8A прошит загрузчиком Arduino Optiboot8. Можно купить загруженный Arduino Mini/Atmega328, чтобы избежать сложностей.
Контроллер, дисплей, LED-лампа и пауэрбанк питаются от внутреннего Li-ion. Две тактовые кнопки — для меню на дисплее. Кнопки аппаратно антидребезжены конденсаторами параллельно. Тумблер — вкл/выкл устройства.

Солнечная зарядка: P-MOSFET, управляемый через 2N2222 транзисторную схему драйвера, управляемой ШИМ от микроконтроллера. ШИМ зависит от напряжения внешней батареи. Ток от панели через P-MOSFET к свинцово-кислотной батарее. При полном заряде MOSFET отключается микроконтроллером. После отключения напряжение батареи плавно падает, при 13.6V зарядка возобновляется с низким скважностью для поддержки плавающего заряда.
12V DC нагрузка управляется N-MOSFET через Gate от микроконтроллера.

LED-лампа тоже через NMOS. Gate ШИМ-управляется для регулировки яркости.

Инвертер: 2 N-MOSFET, чередующиеся вкл/выкл для симуляции AC. С внешним трансформатором с центральным отводом получаем прямоугольный AC.

Принцип инвертора:

Пропуская ток через центральный отвод катушки повышающего трансформатора в противоположные стороны поочерёдно через MOSFET, создаём AC на вторичке. Когда верхний MOSFET открыт, нижний закрыт — ток вверх. Наоборот — ток вниз.

Оба MOSFET должны переключаться на удвоенной частоте AC. Для 50 Гц (20 мс период) — 10 мс на полуволну, значит 100 переключений в секунду Gate-сигналов MOSFET инвертора.



Внимание! Высокое напряжение AC смертельно опасно! Никогда не касайтесь высоковольтной стороны трансформатора голыми руками!
Если инвертер не используется, опция 2XDC Load позволяет использовать две дополнительные 12V DC нагрузки на клеммах инвертера.
Магия программного обеспечения
Два варианта кода: монолитный в одной вкладке Arduino и модульный по вкладкам для отдельных функций.
Задачи обобщены в функции:
Get_ADCVal()— измеряет напряжения панели, внутренней и внешней батарей, берёт 20 сэмплов, усредняет, обновляет переменные.Context_Control()— взаимодействие с пользователем, управление действиями, обновление инфо на дисплее.Charging_Control(),Discharging_Control(),Load_Control()— фоновые функции замкнутого цикла: мониторят напряжение батареи, авто-нагрузку, защиту от перезаряда/глубокого разряда.
Ввод пользователя через прерывания от кнопок. При нажатии выполняются ISR для INT0/INT1. Меняются volatile переменные dp и ds. Третья переменная level вместе с dp (указатель меню) и ds (селектор контента) позволяет навигировать по меню/подменю и выполнять действия.
16-битный Timer1 настроен на прерывание переполнения каждые 10 мс, переключая PIN_INVP и PIN_INVN при включённом инвертере.
Все функции Lcd_...() — работа с дисплеем.
Atmega8A vs Atmega328P (Uno)
Систему легко адаптировать под Atmega328P/Uno, заменив в коде:
TIMSK → TIMSK1
#define ADC_VREF 2.69 → #define ADC_VREF 1.11
#define ADCVDIV_R1 22 → #define ADCVDIV_R1 8.2
В железе: использовать резистор 8.2 кОм вместо 22 кОм для масштабирования напряжений панели/батареи в диапазон ADC.
Видео в действии
Обычно смотрю видео перед чтением. Если вы так же — наслаждайтесь роликами!
Перспективы улучшения
Место во Flash Atmega8A закончилось. Не удалось добавить критичные фичи:
- Программная защита от перегрузки/КЗ
- Графические иконки и логотип
- Измерение энергии и логирование
- Оценка и сигнал времени резерва
- Зарядка ноутбуков
- Поддержка 6V систем
- Bluetooth управление нагрузкой
- Таймерное управление нагрузкой
- RTC часы для новых функций
- IoT подключение
Если планируете похожее — добавьте эти фичи! Atmega328P (Uno) или Arduino Mega подойдут лучше для всего этого. Но текущая версия работает, я доволен.
Дополнительные ресурсы
- Скачать Arduino 1.0.6 с поддержкой платы Atmega8
- Открыть Arduino.exe, Tools → Board → Optiboot8
- Прошить загрузчик этим методом
- Компилировать и загрузить код
Код
Код во вкладкеArduino
#define PIN_SCE 12
#define PIN_RESET 13
#define PIN_DC 8
#define PIN_SDIN 7
#define PIN_SCLK 6
#define PIN_INVP 4
#define PIN_INVN 5
#define PIN_LOAD 9 // 12 v dc load
#define PIN_LAMP 10 // WHITE LED LIGHT
#define PIN_BATTPWM 11 // Drives PMOS for Ext BAtt Charging
#define PIN_BATTint_Sense A2
#define PIN_SOLAR_Sense A0
#define PIN_BATText_Sense A3
#define ADC_VREF 2.695 // internal ref voltage, around 1.11V for Arduino Uno aka Atmega328P, here my Atmega8A gives this !
#define ADCVDIV_R1 22 // 22 K voltage divider lower Resistor
#define ADCVDIV_R2 175 // 175 K voltage divider upper Resistor
//const uint8_t skulljoke[] PROGMEM ={2,} ; // PIN 2 & 3 USED AS INTERRUPT SWITCH
// PIN 0 & 1 AS PROGRAMMING
// PIN RESET AS
#define LCD_C LOW
#define LCD_D HIGH
#define LCD_X 84
#define LCD_Y 48
uint8_t x=0;
uint8_t level=0;
uint8_t blinker=0;
boolean Load_Auto_Enable=0;
float maxADC_Voltage=0.0;
float BattInt_Voltage=0.0;
float BattExt_Voltage=0.0;
float PV_Voltage=0.0;
volatile int y=0;
volatile uint8_t dp =0;
volatile uint8_t ds =0;
volatile boolean cycle=0;
volatile uint8_t cdc_level =0;
//int i;
void setup(void) {
LcdInitialise();
LcdClear();
LcdString("*ARDUBOY PV*");
LcdString(" CONTROLLER,");
LcdString("AC INVERTER,");
LcdString("POWER BANK, ");
LcdString("LAMP THING!!");
delay(3000);
analogReference(INTERNAL);
maxADC_Voltage=(ADC_VREF/ADCVDIV_R1)*(ADCVDIV_R1+ADCVDIV_R2);
pinMode(PIN_LOAD,OUTPUT);
digitalWrite(PIN_LOAD,LOW);
pinMode(2, INPUT_PULLUP);
attachInterrupt(0, SW1, FALLING);// Interrupt for Swithc 1
pinMode(3, INPUT_PULLUP);
attachInterrupt(1, SW2, FALLING);// Interrupt for Swithc 2
}
void loop(void) {
Get_ADCVal();
Context_Control();
Charging_Control();
Discharging_Control();
Load_Control();
LcdClear();;
}
///////////// ADC ///////////////
void Get_ADCVal(void) {
int I=0; int J=0; int K=0;
for(x=0;x<20;x++) {
I=I+analogRead(PIN_BATTint_Sense);
J=J+analogRead(PIN_BATText_Sense);
K=K+analogRead(PIN_SOLAR_Sense);
}
// average voltage
BattInt_Voltage=I/20.0;
BattExt_Voltage=J/20.0;
PV_Voltage=K/20.0;
BattInt_Voltage=maxADC_Voltage*BattInt_Voltage/1023.0;
BattExt_Voltage=maxADC_Voltage*BattExt_Voltage/1023.0;
PV_Voltage=maxADC_Voltage*PV_Voltage/1023.0;
}
//////////// Display and Control //////////////
void Context_Control(void) {
if(ds==0) {show_menu();}
if(ds==1 && dp == 0) {show_info(); delay(100);} //LcdClear();Get_ADCVal();}
///////////////////////Enters 1st Sub Menu////////////////////////////
if(ds==1 && dp == 1) {
level=1; dp=0;
while(level==1) {
int temp=ds;
LcdClear(); show_load_ctrl();delay(250);
if (dp==0){ if(ds!=temp){Load_Auto_Enable=0;digitalWrite(PIN_LOAD,LOW);}}
if (dp==1){if(ds!=temp){Load_Auto_Enable=0; digitalWrite(PIN_LOAD,HIGH);}}
if (dp==2){ if(ds!=temp){Load_Auto_Enable=1;}}
if (dp==3){show_load_ctrl();delay(250);level=0;dp=0;ds=0;}
}
}
//////////////////////Enters 2nd Sub Menu//////////////////////////////
if(ds==1 && dp == 2) {
level=2; dp=0;
while(level==2) {
int temp=ds;
show_inv_ctrl();delay(250);
LcdClear();
if (dp==0){ if(ds!=temp){Timer1_Init();}}
if (dp==1){ if(ds!=temp){Timer1_DeInit();}}
if (dp==2){ if(ds!=temp){Timer1_DeInit();digitalWrite(PIN_INVP,1);digitalWrite(PIN_INVN,1);}}
if (dp==3){show_inv_ctrl();delay(250);level=0;dp=0;ds=0;}
}
}
/////////////////////////////////////////////////////////
////////////////Enters 3rd Sub Menu/////////////////////////////
if(ds==1 && dp == 3) {
level=3; dp=0;
while(level==3) {
int temp=ds;
LcdClear(); show_led_ctrl();delay(250);
if (dp==0){blinker=0;if(ds!=temp) {if(y<=255){y=y+15;analogWrite(PIN_LAMP,y);}}}
if (dp==1){blinker=0;if(ds!=temp) {if(y>=0){y=y-15;analogWrite(PIN_LAMP,y);}}}
if (dp==2){if(ds!=temp) {blinker^=1;analogWrite(PIN_LAMP,127);delay(250);analogWrite(PIN_LAMP,0);}}
if (dp==3){show_led_ctrl();delay(250);level=0;dp=0;ds=0;}
}
}
/////////////////////////////////////////////////////////
// {show_inv_ctrl();} // {show_led_ctrl();} //}
if(blinker==1) {analogWrite(PIN_LAMP,0);}
delay(250);
if(blinker==1) {analogWrite(PIN_LAMP,127);}
}
/////////////////////////// Menu Text to be shown on Nokia 5110 display ///////////////////
void show_menu(void) {
LcdXY(0,dp); LcdString("->");
LcdXY(15,0); LcdString("Sys Info");
LcdXY(15,1); LcdString("DC Load+");
LcdXY(15,2); LcdString("AC Load~");
LcdXY(15,3); LcdString("LED Lamp");
}
void show_info(void) {
LcdXY(0,0); LcdString("Bat_I="); LcdNumtoString(BattInt_Voltage); LcdString("v");
LcdXY(0,1); LcdString("Bat_E="); LcdNumtoString(BattExt_Voltage); LcdString("v");
LcdXY(0,2); LcdString("Sol_P="); LcdNumtoString(PV_Voltage); LcdString("v");
LcdXY(0,3); if(BattExt_Voltage >8.0) {LcdString("Batt Conn OK");} else {LcdString("Connect Batt");}
if (PV_Voltage > 10.5 && cdc_level!=3 && cdc_level!=0) {LcdString("Charging:ON ");} else {LcdString("Charging:OFF");}
if (TCNT1 >=45535 ) {LcdString("Inverter:ON");} else {LcdString("Inverter:OFF");}
}
void show_load_ctrl(void) {
LcdXY(0,dp); LcdString("->");
LcdXY(15,0); LcdString("Load Off");
LcdXY(15,1); LcdString("Load On");
LcdXY(15,2); LcdString("Load Auto");
LcdXY(15,3); LcdString("Return");
LcdXY(0,4); LcdString("Must Connect");
LcdString("12V DC Load");
}
void show_inv_ctrl(void) {
LcdXY(0,dp); LcdString("->");
LcdXY(15,0); LcdString("AC Inv On");
LcdXY(15,1); LcdString("AC Inv Off");
LcdXY(15,2); LcdString("2XDC Load");
LcdXY(15,3); LcdString("Return");
LcdXY(0,4); LcdString("2XDC Load,NO");
LcdXY(0,5); LcdString("TRANSFORMER!");
}
void show_led_ctrl(void) {
LcdXY(0,dp); LcdString("->");
LcdXY(15,0); LcdString("LED ++");
LcdXY(15,1); LcdString("LED --");
LcdXY(15,2); LcdString("LED Blk");
LcdXY(15,3); LcdString("Return");
LcdXY(0,4); LcdString("LED DISABLEs");
LcdXY(0,5); LcdString("When INVR On");
}
////////////// Interrupt ISRs //////////////
void SW1() {
dp++; if(dp>3){dp=0;}
}
void SW2() {
ds^=1;
}
ISR(TIMER1_OVF_vect) {
noInterrupts();
TCNT1 = 45535; // TCNT1 = 25535;
cycle^=1;
if(cycle==0); {
digitalWrite(PIN_INVP,HIGH);
delayMicroseconds(1); // dead band
digitalWrite(PIN_INVN,LOW);
delayMicroseconds(1);
}
if(cycle==1) {
digitalWrite(PIN_INVP,LOW);
delayMicroseconds(1); // dead band
digitalWrite(PIN_INVN,HIGH);
delayMicroseconds(1);
}
interrupts();
}
/////////////Nokia 5110 Functions ////////////
static const byte ASCII[][5] = {
{0x00, 0x00, 0x00, 0x00, 0x00} // 20
,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 !
,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 "
,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 #
,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $
,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 %
,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 &
,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 '
,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 (
,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 )
,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a *
,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b +
,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c ,
,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d -
,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e .
,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f /
,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0
,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1
,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2
,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3
,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4
,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5
,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6
,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7
,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8
,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9
,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a :
,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ;
,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c <
,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d =
,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e >
,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ?
,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @
,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A
,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B
,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C
,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D
,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E
,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F
,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G
,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H
,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I
,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J
,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K
,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L
,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M
,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N
,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O
,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P
,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q
,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R
,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S
,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T
,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U
,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V
,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W
,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X
,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y
,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z
,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [
,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c \
,{0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ]
,{0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^
,{0x40, 0x40, 0x40, 0x40, 0x40} // 5f _
,{0x00, 0x01, 0x02, 0x04, 0x00} // 60 `
,{0x20, 0x54, 0x54, 0x54, 0x78} // 61 a
,{0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b
,{0x38, 0x44, 0x44, 0x44, 0x20} // 63 c
,{0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d
,{0x38, 0x54, 0x54, 0x54, 0x18} // 65 e
,{0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f
,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g
,{0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h
,{0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i
,{0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j
,{0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k
,{0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l
,{0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m
,{0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n
,{0x38, 0x44, 0x44, 0x44, 0x38} // 6f o
,{0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p
,{0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q
,{0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r
,{0x48, 0x54, 0x54, 0x54, 0x20} // 73 s
,{0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t
,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u
,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v
,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w
,{0x44, 0x28, 0x10, 0x28, 0x44} // 78 x
,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y
,{0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z
,{0x00, 0x08, 0x36, 0x41, 0x00} // 7b {
,{0x00, 0x00, 0x7f, 0x00, 0x00} // 7c |
,{0x00, 0x41, 0x36, 0x08, 0x00} // 7d }
,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ~
,{0x78, 0x46, 0x41, 0x46, 0x78} // 7f
};
void LcdCharacter(char character) {
LcdWrite(LCD_D, 0x00);
for (int index = 0; index < 5; index++) {
LcdWrite(LCD_D, ASCII[character - 0x20][index]);
}
LcdWrite(LCD_D, 0x00);
}
void LcdClear(void) {
for (int index = 0; index < LCD_X * LCD_Y / 8; index++) {
LcdWrite(LCD_D, 0x00);
}
}
void LcdInitialise(void) {
pinMode(PIN_SCE, OUTPUT);
pinMode(PIN_RESET, OUTPUT);
pinMode(PIN_DC, OUTPUT);
pinMode(PIN_SDIN, OUTPUT);
pinMode(PIN_SCLK, OUTPUT);
digitalWrite(PIN_RESET, LOW);
digitalWrite(PIN_RESET, HIGH);
LcdWrite(LCD_C, 0x21 ); // LCD Extended Commands.
LcdWrite(LCD_C, 0xA0 ); // Set LCD Vop (Contrast). //0xB1
LcdWrite(LCD_C, 0x04 ); // Set Temp coefficent. //0x04
LcdWrite(LCD_C, 0x14 ); // LCD bias mode 1:48. //0x13
LcdWrite(LCD_C, 0x20 ); // LCD Basic Commands
LcdWrite(LCD_C, 0x0C ); // LCD in normal mode.
}
void LcdString(char *characters) {
while (*characters) {
LcdCharacter(*characters++);
}
}
void LcdWrite(byte dc, byte data) {
// analogWrite(PIN_BL, 127);
digitalWrite(PIN_DC, dc);
digitalWrite(PIN_SCE, LOW);
shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data);
digitalWrite(PIN_SCE, HIGH);
}
void LcdXY(int x,int y) {
LcdWrite(LCD_C,0x80|x);
LcdWrite(LCD_C,0x40|y);
}
void LcdNumtoString(float number) {
char string[8];
LcdString(dtostrf(number,5,2, string));
}
///////////////// Background Loop Control Functions ////////////
// This codelet will automatically enable external load when Sun is Down using PV as Light Sensor
void Load_Control(void) {
if (Load_Auto_Enable == 1 && PV_Voltage < 4.5 && cdc_level > 0) {digitalWrite(PIN_LOAD,HIGH);}
if (Load_Auto_Enable == 1 && PV_Voltage >10.5|| cdc_level==0) {digitalWrite(PIN_LOAD,LOW);}
}
void Charging_Control (void) {
// BattExt_Voltage Level 14.3 Charging off, 13.5 or below Charging on, 10.8 Load off, 12.5 Load on
// this next condition prevenst the attemps of charging during night % night PV is 0 volt !
if (PV_Voltage > 10.5 && BattExt_Voltage >8.0) {
if (BattExt_Voltage <= 12.5) { analogWrite(PIN_BATTPWM,255); cdc_level = 0; }
if (BattExt_Voltage > 12.5 && BattExt_Voltage <=12.9) { analogWrite(PIN_BATTPWM,200); cdc_level = 1; }
if (BattExt_Voltage > 12.9 && BattExt_Voltage <=13.3) { analogWrite(PIN_BATTPWM,160); cdc_level = 1; }
if (BattExt_Voltage > 13.3 && BattExt_Voltage <=13.6) { analogWrite(PIN_BATTPWM,120); cdc_level = 2; }
if (BattExt_Voltage > 13.6 && BattExt_Voltage <=13.9 && cdc_level == 2) { analogWrite(PIN_BATTPWM,80); cdc_level = 2; }
if (BattExt_Voltage > 13.9 && BattExt_Voltage <=14.3 && cdc_level == 2) { analogWrite(PIN_BATTPWM,40); cdc_level = 2; }
// Over Voltage Lockout, while cdc_level is 3, NO Charging ! Charging Resumes when cdc is 2 which is below 13.5 v
if (BattExt_Voltage > 14.3 ) { analogWrite(PIN_BATTPWM,0); cdc_level = 3; }
} else {analogWrite(PIN_BATTPWM,0);cdc_level = 3;}
}
/// Under voltage Lockout
void Discharging_Control (void) {
if (BattExt_Voltage <= 10.8) {
cdc_level = 0;
{digitalWrite(PIN_LOAD,LOW);}
Timer1_DeInit();
}
}
/////////////// Timer 2 Functions //////////////
// This portion of the code is written in AVR style
void Timer1_Init(void) {
noInterrupts(); // disable all interrupts
pinMode(PIN_INVP,OUTPUT);
pinMode(PIN_INVN,OUTPUT);
TCCR1A = 0;
TCCR1B = 0;
/* ========================================
50 Hz AC means 20 ms Wave which is formed by 2, 10 ms Pulses from PIN_INVP and PIN_INVN pins
so both this pin should toggle at 100 Hz !
Now 100 Hz = .01 sec
Arduino System Clock 16 MHz = 16000000 cycle in 0,01 sec we have 160000 cycles
using prescaler of 8 (CS11) makes timer count 160000/8 = 20000 timer ticks
Since the timer 2 is 16 bit Up Counter and it Overflows at 65635 value
we need to start counting from 65535-20000 = from 45535 value upto 65535
thus TCNT1 starts at 45535 ..then tick tick tick ... 46000 .... 50000 .............. 65536 !!!
Boom Timer Over Flow Interrupt and toggle the Inverter driving pins in ISR
and start counting from 45535 Again !!! (This happens in the Background)
======================================== */
TCNT1 = 45535; //TCNT1 = 25535;
TCCR1B |= (1 << CS11); // 8 prescaler
TIMSK |= (1 << TOIE1); // enable timer compare interrupt
interrupts();
}
void Timer1_DeInit(void) {
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
// TIMSK &= (0 << TOIE1); // disable timer compare interrupt
digitalWrite(PIN_INVP,LOW);
digitalWrite(PIN_INVN,LOW);
interrupts();
}
Полный кодC/C++
Код организован и читаем
Нет предпросмотра (только скачивание).
ЗагрузчикиC/C++
Нет предпросмотра (только скачивание).
Схемы

Производственный процесс
- Что такое солнечный инвертор? Как он преобразует солнечную энергию в пригодную электричество
- Галогенные лампы: технологии, история и будущее — экспертный обзор
- Лавовая лампа: история, дизайн и производственные секреты
- Солнечные отопительные системы: от пассивных к активным решениям для устойчивого отопления домов
- Интегральные схемы (ИС): компактные мощные чипы, питающие современную электронику
- Солнечные ячейки: от ранних экспериментов к современной фотовольтаике — технологии, производство и перспекти…
- Умный светофор: адаптивное уличное освещение на базе IoT
- DIY USB-геймпад на Arduino: собственный высокопроизводительный контроллер
- Солнечный трекер v2.0 — продвинутая DIY система слежения за солнцем
- Солнечная энергия: полное руководство по чистой и надежной энергии