/*
****[Untested Version. Please Use with Caution! Preferably connect a load (e.g. 200 W incandacent bulb) in series with the mains supply.]****
Pure_Sine_Wave_Inverter_V.2.1 [NOT compatible with version 1.0 boards]
Change log: 125 V Taping has been implemented.
Sayantan Sinha: 23/09/2020
sPWM on the atMega328P for the arduino NANO. H-bridge output with deadtime.
Half-bridge #1: Operating freq 16 kHz, High Side driver = pin D9 (OC1A), Low Side driver = pin D10 (OC1B)
Half-bridge #2: Operating freq 50 Hz, High Side driver = pin D11, Low Side driver = pin D12.
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
#define SD0 0 // Shut down MOSFET driver ICs (D8 i.e. bit-0 in PORTB) (HIGH: shutdown, LOW: run)
#define HS1 1 // High side drive pin of Half Bridge #1 (D9 i.e. bit-1 in PORTB)
#define LS1 2 // Low side drive pin of Half Bridge #1 (D10 i.e. bit-2 in PORTB)
#define HS2 3 // High side drive pin of Half Bridge #2 (D11 i.e. bit-3 in PORTB)
#define LS2 4 // Low side drive pin of Half Bridge #2 (D12 i.e. bit-4 in PORTB)
#define LED 5 // LED (D13 i.e. bit-5 in PORTB)
#define FAN_PIN 1 // Cooling fan
#define VI_SENS_PIN 2 // Output of the optocoupler
#define PH_SENS_PIN 3 // To sense +ve or -Ve half
#define BUZZ_PIN 4
#define TAPPING_RLY_PIN 5
#define CHG_RLY_PIN 6
#define CHNG_OVR_RLY_PIN 7 // Change over relay
#define LED_PIN 13
#define VO_SENS_PIN A0
#define T_SENS_PIN A1 // Temperature sensing pin (output from LM35)
#define LED_GRID A2
//#define LED_CHG A2
#define LED_SD A3
// A4 & A5 are used in I2C communication
#define VB_SENS_PIN A6 // A6 in Arduino Nano
#define I_SENS_PIN A7 // A7 in Arduino Nano
#define SW_FREQ 16000
#define MAX_COUNT 500 // (16 MHz / SW_FREQ) / 2
#define NO_OF_PULSES 160 // 16 kHz / 50 Hz = 320; 320 / 2 = 160 divisions in one half-cycle
#define Q_CYCLE 80
#define K_P 0.01
#define VO_FB_FACTOR 100.0 // Connect 220 V to 6 V step-down transformer in feedback
#define VB_SENS_FACTOR 4.047 // Battery sensor factor
#define I_SENS_FACTOR 4.74 // Current sensor factor
#define ADC_RES 0.004888 // Resolution of the ADC = 4.888 mV
#define VO_DESIRED 220.0 // Set AC voltage at Output
//#define VI_CUTOFF 40.0 // 40 A cutoff current
#define VB_CUTOFF 10.80 // Corelate with battery data sheet
#define VB_LOW 11.00 // Battery warning starts beeping here
#define VB_CHG_START 13.8 // Battery will start charging below this voltage
#define SC_VOLTAGE 20.0 // Output voltage at short circuit
#define VB_SET 14.0 // Battery voltage during charging
#define VB_FLOAT 3.9 // Battery voltage where floating charge starts
#define I_SET 5.0 // Maximum charging current
#define I_FLOAT 0.01 // Current goes below this value indicates floating charge
#define I_FAST 0.3 // Current beyond this value indicates fast charge
#define T_HIGH 42.0 // Above this temperature the fan will turn on
#define T_C 60.0 // Above this temperature the unit will shut down
#define T_LOW 35.0 // Below this temperature the fan will turn off
#define GRID_MODE 0
#define INV_MODE 1
//Look up tables with 160 entries each, normalised to have max value of 500 which is the period of the PWM loaded into register ICR1.(D:\Simulations\MPLAB PROJECTS\SPWM_PhaseFreqCorrect_LookUp_Table_Gen.m)
unsigned int lookUp_100[NO_OF_PULSES] = {0, 10, 20, 29, 39, 49, 59, 69, 78, 88, 98, 107, 117, 126, 136, 145, 155, 164, 173, 182, 191, 200, 209, 218, 227, 236, 244, 253, 261, 270, 278, 286, 294, 302, 310, 317, 325, 332, 339, 347, 354, 360, 367, 374, 380, 387, 393, 399, 405, 410, 416, 421, 426, 431, 436, 441, 446, 450, 454, 458, 462, 466, 469, 472, 476, 478, 481, 484, 486, 488, 490, 492, 494, 495, 497, 498, 498, 499, 500, 500, 500, 500, 500, 499, 498, 498, 497, 495, 494, 492, 490, 488, 486, 484, 481, 478, 476, 472, 469, 466, 462, 458, 454, 450, 446, 441, 436, 431, 426, 421, 416, 410, 405, 399, 393, 387, 380, 374, 367, 360, 354, 347, 339, 332, 325, 317, 310, 302, 294, 286, 278, 270, 261, 253, 244, 236, 227, 218, 209, 200, 191, 182, 173, 164, 155, 145, 136, 126, 117, 107, 98, 88, 78, 69, 59, 49, 39, 29, 20, 10};
unsigned int lookUp[NO_OF_PULSES] = {500, 490, 480, 471, 461, 451, 441, 431, 422, 412, 402, 393, 383, 374, 364, 355, 345, 336, 327, 318, 309, 300, 291, 282, 273, 264, 256, 247, 239, 230, 222, 214, 206, 198, 190, 183, 175, 168, 161, 153, 146, 140, 133, 126, 120, 113, 107, 101, 95, 90, 84, 79, 74, 69, 64, 59, 54, 50, 46, 42, 38, 34, 31, 28, 24, 22, 19, 16, 14, 12, 10, 8, 6, 5, 3, 2, 2, 1, 0, 0, 0, 0, 0, 1, 2, 2, 3, 5, 6, 8, 10, 12, 14, 16, 19, 22, 24, 28, 31, 34, 38, 42, 46, 50, 54, 59, 64, 69, 74, 79, 84, 90, 95, 101, 107, 113, 120, 126, 133, 140, 146, 153, 161, 168, 175, 183, 190, 198, 206, 214, 222, 230, 239, 247, 256, 264, 273, 282, 291, 300, 309, 318, 327, 336, 345, 355, 364, 374, 383, 393, 402, 412, 422, 431, 441, 451, 461, 471, 480, 490};
volatile bool toggleHalfBridge2 = false;
volatile bool positiveHalf = true;
volatile bool zeroCrossing = false;
volatile bool sensVoltage = false;
volatile bool knockKnock = false;
volatile bool rampUp = true;
volatile int tick2msCounter = 0;
bool flagChangeDuty = false, lowBat = false, criticalBat = false, overCurrent = false, sc = false, criticalTem = false, flagBeep = false, flagSwitchToGrid = false;
bool flagWait = false, flag2000ms = false, flagCharging = false, flag125vTapping = false, flagChgSw = false, flagOverVoltage = false, flagShutDown = false, flagFan = false;
bool flagFloatChg = false;
byte mode = GRID_MODE;
float vOut, vBat, current, tem; // Output voltage from feedback transformer, battery voltage, pd across current sense resistor
float iSens; // Input current from sens resistance
unsigned int modIndx = 40; // Start the inverter with this modulation index
enum beepType {shortBeep = 100, longBeep = 300};
unsigned long eventTime = 0;
unsigned long dispTime = 0;
unsigned int cc = 300; // Counter to keep delay of 3 s between lcd updates during charging
void changeDuty(void);
void correctVo(void);
void error(void);
void tick2ms(void);
void tick2000ms(void);
void dispRefresh(void);
void atZero (void);
void atPositive (void);
void switchToInv(void);
void switchToGrid(void);
void isGridOn(void);
void shutDown(void);
void startChg(void);
void stopChg(void);
void actuateCurrent(void);
void beep(byte t = shortBeep, byte n = 1, unsigned int p = 500);
void setup()
{
lcd.init(); // Initialize the LCD
lcd.backlight(); // Turn on the backlight
lcd.begin(16, 2);
DDRB = DDRB | 0b00111111; // Set digital pin13 (PB5) to digital pin 8 (PB0) as output. D13 = PB5 (PB7 & PB6 are unusable). PORTB = [XTAL2 XTAL1 D13 D12 D11 D10 D9 D8]
PORTB = _BV(SD0); // Shut down the gate driver ICs
pinMode (CHNG_OVR_RLY_PIN, OUTPUT);
pinMode (VI_SENS_PIN, INPUT_PULLUP);
pinMode (PH_SENS_PIN, INPUT_PULLUP);
pinMode (CHG_RLY_PIN, OUTPUT);
pinMode (TAPPING_RLY_PIN, OUTPUT);
pinMode (BUZZ_PIN, OUTPUT);
pinMode (LED_GRID, OUTPUT);
// pinMode (LED_CHG, OUTPUT);
pinMode (LED_SD, OUTPUT);
pinMode (FAN_PIN, OUTPUT);
digitalWrite(CHNG_OVR_RLY_PIN, LOW);
digitalWrite(CHG_RLY_PIN, LOW);
digitalWrite(TAPPING_RLY_PIN, LOW);
digitalWrite(LED_GRID, LOW);
digitalWrite(LED_SD, LOW);
// digitalWrite(LED_CHG, LOW);
digitalWrite(FAN_PIN, LOW);
digitalWrite(BUZZ_PIN, HIGH);
delay(100);
digitalWrite(BUZZ_PIN, LOW);
if (digitalRead (VI_SENS_PIN) == LOW)
mode = GRID_MODE;
else
mode = INV_MODE;
changeDuty();
// Timer2 Initialization for interruputing every 2 ms
TCCR2A = 0b00000010; // CTC Mode
TCCR2B = 0b00000001; // No prescaling
OCR2A = 32; // For interrrupt after 2 ms
if (mode == INV_MODE) {
switchToInv();
}
else {
switchToGrid();
}
sei(); // Enable global interrupts.
}
void loop()
{
/*#################################################################################################################*/
/* ~~~~~~~~INVERTER MODE~~~~~~~~ */
/*#################################################################################################################*/
if (mode == INV_MODE) {
if (zeroCrossing) {
zeroCrossing = false;
PORTB = PORTB & ~(_BV(HS2) | _BV(LS2)); // Reset HS2 & LS2
if (flagSwitchToGrid) {
PORTB = _BV(SD0);
switchToGrid();
}
}
if (toggleHalfBridge2) {
toggleHalfBridge2 = false;
if (positiveHalf)
PORTB = PORTB | _BV(LS2); // HS2 = LOW, LS2 = HIGH. (Change the polarity)
else
PORTB = PORTB | _BV(HS2); // HS2 = HIGH, LS2 = LOW. (Change the polarity)
vBat = (float)analogRead(VB_SENS_PIN) * ADC_RES * VB_SENS_FACTOR;
}
if (knockKnock) {
knockKnock = false;
isGridOn();
}
if (sensVoltage) {
sensVoltage = false;
vOut = (float)analogRead(VO_SENS_PIN) * ADC_RES * VO_FB_FACTOR;
tem = (float)analogRead(T_SENS_PIN) * 0.4888; // LM35: V_out = temperature (°C) * 10 mV
if (abs(VO_DESIRED - vOut) >= 15)
correctVo();
else
rampUp = false;
if (vBat < VB_CUTOFF)
if (!rampUp)
criticalBat = true;
if (vBat < VB_LOW)
lowBat = true;
if (vOut < SC_VOLTAGE && !rampUp)
sc = true;
if (tem > T_HIGH) {
if (tem > T_C)
criticalTem = true;
else
criticalTem = false;
if (!flagFan) {
digitalWrite(FAN_PIN, HIGH);
flagFan = true;
}
}
else if (tem < T_LOW && flagFan) {
digitalWrite(FAN_PIN, LOW);
flagFan = false;
}
if (criticalBat || sc || criticalTem) { // Low battery or short circuit occured or critically high temperature
shutDown();
}
if (lowBat && !flagBeep) {
lowBat = false;
beep(longBeep, 1, 2000);
}
}
if (flagChangeDuty)
changeDuty();
if (flagShutDown) {
if (knockKnock) {
knockKnock = false;
isGridOn();
}
if (flagSwitchToGrid)
switchToGrid();
unsigned long timeNow = millis();
unsigned long timeElapsed = eventTime > timeNow ? (4294967295 - eventTime) + timeNow : timeNow - eventTime;
if (timeElapsed > 5000)
switchToInv(); // Reattempt to switch to inverter 5 s after shutdown
}
if (digitalRead(VI_SENS_PIN) == LOW && !flagWait) {
tick2000ms(); // If Grid voltage is sensed, wait 2 s before switching to the Grid Mode
flagWait = true;
}
}
/*#################################################################################################################*/
/* ~~~~~~~~GRID MODE~~~~~~~~ */
/*#################################################################################################################*/
else if (mode == GRID_MODE) {
if (digitalRead(VI_SENS_PIN) == HIGH && !flagWait) {
tick2ms(); // If Grid voltage down is sensed, wait 2 ms before switching to the Inverter Mode
flagWait = true;
}
if (digitalRead(PH_SENS_PIN) == LOW) { // Mains is at positive half.
positiveHalf = false; // If grid shuts down, inverter will start negative half
}
else {
positiveHalf = true;
}
if (knockKnock) {
knockKnock = false;
if (digitalRead(VI_SENS_PIN) == HIGH) {
TIMSK1 = 0b00000000; // TOIE1 = 0: All Timer1 Interrupts Disabled // Shutdown charging first
TCCR1A = 0b00000000; // OC1A (pin D9) Disconnected; OC1B (pin D10) Disconnected;
PORTB = _BV(SD0); // Shut down gate drivers
switchToInv();
}
else {
flagWait = false;
}
}
else { // Charging Control...
unsigned long timeNow = millis();
unsigned long timeElapsed = eventTime > timeNow ? (4294967295 - eventTime) + timeNow : timeNow - eventTime;
if (!flagCharging) {
if (timeElapsed > 3000) { // Start charging after 3 s
startChg();
}
}
else {
if (timeElapsed > 10) { // Actuate current at every 10 ms interval
vBat = (float)analogRead(VB_SENS_PIN) * ADC_RES * VB_SENS_FACTOR;
current = (float)analogRead(I_SENS_PIN) * ADC_RES * I_SENS_FACTOR;
tem = (float)analogRead(T_SENS_PIN) * 0.4888; // LM35: V_out = temperature (°C) * 10 mV
if (!flagFloatChg && !flagFan) {
digitalWrite(FAN_PIN, HIGH);
flagFan = true;
}
if (flagFloatChg && flagFan) {
digitalWrite(FAN_PIN, LOW);
flagFan = false;
}
actuateCurrent();
eventTime = millis();
}
}
}
}
if (flagBeep)
beep();
}
ISR(TIMER1_OVF_vect)
{
static byte i = 1; // Static means it will NOT be reinitialized
if (i >= NO_OF_PULSES) {
i = 0;
}
OCR1A = lookUp[i];
OCR1B = lookUp[i];
if (i == 1) {
positiveHalf = !positiveHalf;
zeroCrossing = true;
TCCR1A = positiveHalf ? 0b11000000 : 0b00110000; // +Ve half: Set OC1A (pin D9) on Compare Match; Disconnect OC1B (pin D10); -Ve half: Opposite
}
else if (i == 2) {
toggleHalfBridge2 = true;
}
else if (i == Q_CYCLE) {
sensVoltage = true;
}
i++;
}
ISR(TIMER1_COMPA_vect)
{
PORTB = 0b00010100;
}
ISR(TIMER1_COMPB_vect)
{
PORTB = 0;
}
ISR(TIMER2_COMPA_vect)
{
if (flag2000ms) {
if (++tick2msCounter >= 125) { // 125 * 16 ms = 2 s
knockKnock = true;
TIMSK2 = 0b00000000; // No more interrupt
}
}
else {
knockKnock = true;
TIMSK2 = 0b00000000; // No more interrupt
}
}
void tick2ms(void) // knockKnock after 2 ms
{
if (flag2000ms) { // Timer2 Initialization for interruputing after 2 ms
TCCR2A = 0b00000010; // CTC Mode
TCCR2B = 0b00000110; // f_cpu / 256
OCR2A = 125; // For interrrupt after 2 ms (2 / (128 * 0.0000625))
flag2000ms = false;
}
knockKnock = false;
TCNT2 = 0; // Reset counter
TIMSK2 = 0b00000010; // Interrupt on compare match A
}
void tick2000ms(void) // Timer2 Initialization for interruputing after 2000 ms
{
TCCR2A = 0b00000010; // CTC Mode
TCCR2B = 0b00000111; // f_cpu / 1024
OCR2A = 250; // For interrrupt after 16 ms (16 / (1024 * 0.0000625))
tick2msCounter = 0;
flag2000ms = true;
knockKnock = false;
TCNT2 = 0; // Reset counter
TIMSK2 = 0b00000010; // Interrupt on compare match A
}
void atZero(void)
{
zeroCrossing = true;
tick2ms(); // Knock after 2 ms
}
void atPositive (void)
{
if (mode == GRID_MODE)
positiveHalf = true;
}
void changeDuty(void)
{
// PORTB |= _BV(LED);
modIndx = modIndx > 100 ? 100 : modIndx < 20 ? 20 : modIndx;
lookUp[0] = MAX_COUNT;
for (int i = 1; i <= Q_CYCLE; i++) {
lookUp[i] = MAX_COUNT - ((lookUp_100[i] * modIndx) / 100); // Calculate new duty cycle for quarter cycle
}
int j = Q_CYCLE - 1;
for (int i = Q_CYCLE + 1; i < NO_OF_PULSES; i++) { // Copy the duty cycle for next quarter cycle
lookUp[i] = lookUp[j];
j--;
}
flagChangeDuty = false;
// PORTB &= ~_BV(LED);
}
void correctVo(void)
{
flagChangeDuty = true;
int e = VO_DESIRED - vOut;
if ((e > 0 && modIndx >= 100) || (e < 0 && modIndx <= 20))
flagChangeDuty = false;
if (flagChangeDuty) {
if (e > 0) {
if (rampUp)
modIndx = modIndx + 5; // Step-up the voltage linerarly
else
modIndx = modIndx + 1; // Step-up the voltage linerarly
}
else if (e < 0) {
if (rampUp)
modIndx = modIndx - 5; // Step-up the voltage linerarly
else
modIndx = modIndx - 1; // Step-down the voltage linerarly
}
else
flagChangeDuty = false;
}
else
rampUp = false; // Ramping up at start up is finished
}
void isGridOn(void)
{
if (digitalRead(VI_SENS_PIN) == HIGH) { // Grid off
if (mode == GRID_MODE) {
zeroCrossing = false;
digitalWrite(SD0, LOW);
switchToInv();
}
else {
flagSwitchToGrid = false;
flagWait = false;
}
}
else {
if (mode == INV_MODE) {
flagSwitchToGrid = true;
}
else {
zeroCrossing = false;
}
}
}
void switchToGrid(void)
{
TIMSK1 = 0b00000000; // TOIE1 = 0: Overflow Interrupt Disabled
TCCR1A = 0b00000000; // OC1A (pin D9) Disconnected; OC1B (pin D10) Disconnected, Phase and Frequency Correct (Mode 8);
PORTB = _BV(SD0); // Shut down
digitalWrite(CHNG_OVR_RLY_PIN, LOW); // Switch power output to grid
digitalWrite(CHG_RLY_PIN, LOW); // Charging relay off
mode = GRID_MODE;
modIndx = 40;
changeDuty();
flagSwitchToGrid = false;
flagCharging = false;
flagWait = false;
criticalTem = false;
eventTime = millis();
lcd.clear();
lcd.print("Grid On V");
lcd.setCursor(9, 0);
lcd.print(vBat);
digitalWrite(LED_GRID, HIGH);
digitalWrite(LED_SD, LOW);
// digitalWrite(LED_CHG, LOW);
}
void switchToInv(void)
{
TIMSK1 = 0b00000000; // TOIE1 = 0: Overflow Interrupt Disabled
TCCR1A = 0b00000000; // OC1A (pin D9) Disconnected; OC1B (pin D10) Disconnected, Phase and Frequency Correct (Mode 8);
PORTB = _BV(SD0); // Shut down gate drivers
digitalWrite(CHNG_OVR_RLY_PIN, HIGH); // Switch power output to inverter
delay(3);
digitalWrite(CHG_RLY_PIN, LOW); // Charging relay off
digitalWrite(LED_GRID, LOW);
digitalWrite(LED_SD, LOW);
// digitalWrite(LED_CHG, LOW);
lcd.clear();
lcd.print("Inverter On");
criticalBat = false;
criticalTem = false;
vBat = (float)analogRead(VB_SENS_PIN) * ADC_RES * VB_SENS_FACTOR;
tem = (float)analogRead(T_SENS_PIN) * 0.4888; // LM35: V_out = temperature (°C) * 10 mV
if (vBat > VB_LOW && tem < T_C) {
PORTB = 0; // Enable gate drivers
PORTB |= positiveHalf ? _BV(LS1) : _BV(LS2); // To charge the bootstrap capacitors
delay(10);
//PORTB = _BV(POW_RLY); // Switch power output to inverter
PORTB = _BV(SD0); // Shut down gate drivers
flagWait = false;
lowBat = false; // Reset all the protection flags
overCurrent = false;
flagShutDown = false;
zeroCrossing = false;
sc = false;
rampUp = true;
beep(shortBeep, 3);
TCCR1A = positiveHalf ? 0b11000000 : 0b00110000; // To start with: +Ve half-> Set OC1A (pin D9) on Compare Match; Disconnect OC1B (pin D10); -Ve half-> Opposite
TCCR1B = 0b00010001; // PWM, Phase and Frequency Correct (Mode 8); Clock Select = System clock (No Prescaling) [Ref. Data Sheet, p. 132]
ICR1 = MAX_COUNT; // Switching frequency = 16 kHz (1 / (MAX_COUNT * 62.5e-9))
OCR1A = lookUp[0];
OCR1B = lookUp[0];
TIMSK1 = 0b00000001; // TOIE1 = 1: Overflow Interrupt Enable
PORTB &= ~_BV(SD0); // Pull shutdown low (Enable gate drivers)
PORTB |= positiveHalf ? _BV(LS2) : _BV(HS2); // If the last grid cycle was +Ve half then start with -Ve half and vice versa
}
else {
if (vBat < VB_LOW)
criticalBat = true;
else if (tem >= T_C)
criticalTem = true;
shutDown();
}
mode = INV_MODE;
}
void shutDown(void)
{
PORTB |= _BV(SD0); // shutdown the gate driver ICs
TIMSK1 = 0b00000000; // TOIE1 = 0: Overflow Interrupt Disabled
TCCR1A = 0b00000000; // OC1A (pin D9) Disconnected; OC1B (pin D10) Disconnected, Phase and Frequency Correct (Mode 8);
PORTB = _BV(SD0);
digitalWrite(CHNG_OVR_RLY_PIN, HIGH); // Switch power output to inverter
delay(3);
digitalWrite(CHG_RLY_PIN, LOW); // Charging relay off
zeroCrossing = false;
toggleHalfBridge2 = false;
flagShutDown = true;
flagWait = false;
modIndx = 40;
changeDuty();
lcd.setCursor(0, 1);
lcd.print(criticalBat ? "BAT LOW " : criticalTem ? "THERMAL SHUTDOWN" : overCurrent ? "OVER LOAD " : "OVER LOAD ");
if (criticalBat) {
lcd.print(vBat);
lcd.print("V");
}
if (tem > T_HIGH && !flagFan) {
digitalWrite(FAN_PIN, HIGH);
flagFan = true;
}
else if (tem < T_LOW && flagFan) {
digitalWrite(FAN_PIN, LOW);
flagFan = false;
}
digitalWrite(LED_SD, HIGH);
eventTime = millis();
}
void startChg(void)
{
vBat = (float)analogRead(VB_SENS_PIN) * ADC_RES * VB_SENS_FACTOR;
tem = (float)analogRead(T_SENS_PIN) * 0.4888; // LM35: V_out = temperature (°C) * 10 mV
if (vBat < VB_CHG_START && tem < T_C) {
digitalWrite(TAPPING_RLY_PIN, HIGH);
flag125vTapping = true;
delay(30);
digitalWrite(CHG_RLY_PIN, HIGH);
delay(3);
PORTB = _BV(SD0); // Shutdown the gate driver ICs.
TCCR1A = 0b00000010; // OC1A disconnected & OC1B Clear on comp match;
TCCR1B = 0b00011001; // Fast PWM Mode (Mode 14); Clock Select = System clock (No Prescaling) [Ref. Data Sheet, p. 132]
ICR1 = 3200; // switching frequency = 5 kHz.
OCR1A = 0; // pins LS1 & LS2 are set at interrupt on compare match A
OCR1B = 0; // pins LS1 & LS2 are reset at interrupt on compare match B. Min. duty cycle is 0%
TIMSK1 = 0; // No Interrupts
flagCharging = true;
flagOverVoltage = false;
flagFloatChg = false;
criticalTem = false;
// digitalWrite(LED_CHG, HIGH);
lcd.setCursor(0, 1);
lcd.print("Charging A");
}
else {
digitalWrite(CHG_RLY_PIN, LOW);
digitalWrite(TAPPING_RLY_PIN, LOW);
flag125vTapping = false;
PORTB = _BV(SD0); // Shutdown the gate driver ICs.
flagCharging = false;
lcd.setCursor(0, 1);
lcd.print(" ");
}
flagChgSw = false; // Switching has not been started
cc = 300;
if (tem > T_HIGH) {
digitalWrite(FAN_PIN, HIGH);
flagFan = true;
}
else if (tem < T_LOW) {
digitalWrite(FAN_PIN, LOW);
flagFan = false;
}
}
void stopChg(void)
{
TIMSK1 = 0b00000000; // TOIE1 = 0: All timer1 Interrupts Disabled
TCCR1A = 0b00000000; // OC1A (pin D9) Disconnected; OC1B (pin D10) Disconnected;
PORTB = _BV(SD0); // Shutdown switching
flagChgSw = false; // Status flag to indicate that the charging switching is shut down
digitalWrite(CHG_RLY_PIN, LOW); // Disengage the charging relay
digitalWrite(TAPPING_RLY_PIN, LOW);
flag125vTapping = false;
flagCharging = false;
// digitalWrite(LED_CHG, LOW);
lcd.setCursor(0, 1);
lcd.print(" ");
}
void actuateCurrent(void)
{
if (!flagChgSw) {
lcd.setCursor(9, 1); // Update current and voltage
lcd.print(current);
lcd.setCursor(9, 0);
lcd.print(vBat);
if (current < I_SET && vBat < VB_CHG_START) {
TCCR1A = 0b00100010; // OC1A disconnected & OC1B Clear on comp match;
OCR1B = 0; // Start with min duty cycle
PORTB &= ~_BV(SD0); // Enable gate drivers
TIMSK1 = 0b00000110; // Interrupts on compare match A & B
flagChgSw = true;
flagFloatChg = false;
}
}
if (current < I_SET && vBat < VB_SET) {
if (OCR1B < 1280) { // Maximum duty cycle 40% (3200 * 40%);
OCR1B = OCR1B + 1; // Increase current slowly
}
else if (!flag125vTapping) {
OCR1B = 0;
digitalWrite(TAPPING_RLY_PIN, HIGH);
flag125vTapping = true;
}
}
else if (vBat > VB_SET || current > I_SET) {
if (OCR1B > 0) { // Minimum possible duty cycle 0%
OCR1B = OCR1B - 1; // Decrease current slowly
if (OCR1B < 32 && flag125vTapping) {
digitalWrite(TAPPING_RLY_PIN, LOW);
flag125vTapping = false;
}
}
else {
TIMSK1 = 0b00000000; // TOIE1 = 0: All timer1 Interrupts Disabled
TCCR1A = 0b00000000; // OC1A (pin D9) Disconnected; OC1B (pin D10) Disconnected;
PORTB = _BV(SD0); // Shutdown switching
flagChgSw = false; // Status flag to indicate that the charging switching is shut down
if (cc < 220) { // Wait 800 ms
digitalWrite(CHG_RLY_PIN, LOW); // Disengage the charging relay
flagOverVoltage = true; // Over voltage in grid input
flagCharging = false;
// digitalWrite(LED_CHG, LOW);
lcd.setCursor(0, 1);
lcd.print(" ");
}
}
}
cc--;
if (cc <= 0) {
cc = 300; // Update current in the lcd every 3 s
if (vBat > VB_FLOAT && current < I_FLOAT) {
if (OCR1B < 32) { // (If duty cycle is less than 2%) Fast charging ended, swithed to float charge
if (!flagFloatChg) {
// digitalWrite(LED_CHG, LOW);
flagFloatChg = true;
lcd.setCursor(0, 1);
lcd.print(" ");
}
}
}
else if (current > I_FAST) {
if (flagFloatChg) { // Not in float charge mode
// digitalWrite(LED_CHG, HIGH);
flagFloatChg = false;
lcd.setCursor(0, 1);
lcd.print("Charging A");
}
}
if (!flagFloatChg) {
lcd.setCursor(9, 1);
lcd.print(current);
}
}
if (cc == 150) {
lcd.setCursor(9, 0);
lcd.print(vBat);
}
}
void beep(byte t = shortBeep, byte n = 1, unsigned int p = 500) // beep(<type of beep>, <number of beeps>, <pause time in ms between beeps>)
{
static bool beepState = false;
static unsigned int beepTime;
static unsigned int beepPause;
static unsigned long beepToggled = 0;
static int noOfBeep = 0;
if (!flagBeep) {
beepTime = t;
beepPause = p;
noOfBeep = n;
digitalWrite(BUZZ_PIN, HIGH);
beepToggled = millis();
beepState = true;
flagBeep = true;
}
if (flagBeep) {
unsigned long timeNow = millis();
unsigned long beepRun = beepToggled > timeNow ? (4294967295 - beepToggled) + timeNow : timeNow - beepToggled;
if (beepState) {
if (beepRun > beepTime) {
digitalWrite(BUZZ_PIN, LOW);
beepToggled = millis();
beepState = false;
noOfBeep--;
}
}
else {
if (beepRun > beepPause) { // Blanking time 500 ms between two beeps
if (noOfBeep) {
digitalWrite(BUZZ_PIN, HIGH);
beepToggled = millis();
beepState = true;
}
else {
flagBeep = false;
}
}
}
}
}
void error(void)
{
while (1) {
digitalWrite(LED_PIN, HIGH);
delay(300);
digitalWrite(LED_PIN, LOW);
delay(500);
}
}