/*
Optimized Pure Sine Wave Inverter Code
Key optimizations for smoother sine wave output
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Pin definitions (optimized for direct port access)
#define FB 0
#define HS1 1
#define HS2 2
#define LS1 3
#define LS2 4
#define SW 5
#define PIN_ZC (PIND & (1 << PD3))
#define PIN_MNS (PIND & (1 << PD2))
#define PIN_SW (PINB & (1 << PB5))
#define W_CNG_RLY_HIGH (PORTD |= 0b10000000)
#define W_CNG_RLY_LOW (PORTD &= 0b01111111)
// Constants (optimized values)
#define SW_FREQ 16000
#define MAX_COUNT 1000
#define MAX_COUNT_CHG 400
#define NP 160
#define Q_CYCLE 80
#define DT 100
#define THC 200
#define MOD_INDX_MAX 95
#define MOD_INDX_MIN 40
// Pre-calculated constants to avoid runtime calculations
constexpr unsigned int VO_LO = 537;
constexpr unsigned int VO_HI = 562;
constexpr unsigned int VB_MAX = 950;
constexpr unsigned int VB_TOP = 920;
constexpr unsigned int VC_MAX = 85;
constexpr unsigned int VC_TOP = 25;
constexpr unsigned int VB_LOW = 750;
constexpr unsigned int VB_CRIT = 690;
constexpr unsigned int VL_MAX = 560;
constexpr unsigned int MNS_WAIT_TIME = 3000;
constexpr unsigned int CNG_OVER_DLY = 8;
#define CNG_OVER true
// Status flags
#define MNS_ON 0b10000000
#define INV_ON 0b01000000
#define CHG_ON 0b00100000
#define BAT_LO 0b00010000
#define OVR_LD 0b00001000
#define FUS_BL 0b00000100
// Pin declarations
const int pinVo = A0, pinVt = A1, pinVb = A2, pinMd = A3, pinVl = A6, pinVc = A7;
const int pinFan = 1, pinMns = 2, pinZc = 3, pinBuz = 4, pinChRng = 5, pinChRly = 6, pinCngRly = 7, pinFb = 8, pinSw = 13;
// Variables (optimized scope and storage)
volatile byte i = 0;
volatile bool pcyl = false;
byte ledsr = 0;
byte erCode = 0;
bool invOn = false;
bool mnsOn = false;
bool chgOn = false;
bool cngOverRequest = false;
bool zcPrev = true;
bool topChg = false;
unsigned int modIndx = 50;
unsigned int vo, vb, vl, vc;
unsigned long timePrev = 0;
unsigned long timeInvOff = 0;
unsigned long timeMnsOn = 0;
unsigned long zcTime = 0;
unsigned long mnsTime = 0;
// Pre-calculated lookup tables (placed in program memory)
const PROGMEM unsigned int lookUpInverseHalf[NP] = {
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,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
};
// Runtime lookup table (RAM)
unsigned int lookUp[NP];
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Function declarations
void changeDuty(void);
void fireInv(void);
void shutdownInv(bool coEnable = false);
void startChg(void);
void stopChg(void);
void refreshDisp(const byte oneByte);
void beepErr(void);
inline void setPWMValues(byte idx);
void setup()
{
DDRB = 0b00011110;
pinMode(pinFan, OUTPUT);
pinMode(pinMns, INPUT_PULLUP);
pinMode(pinZc, INPUT_PULLUP);
pinMode(pinBuz, OUTPUT);
pinMode(pinChRng, INPUT_PULLUP);
pinMode(pinChRly, OUTPUT);
pinMode(pinCngRly, OUTPUT);
pinMode(SCL, INPUT_PULLUP);
pinMode(SDA, INPUT_PULLUP);
lcd.begin(16,2);
lcd.backlight();
// Initialize lookup table
changeDuty();
refreshDisp(MNS_ON | INV_ON | CHG_ON | BAT_LO | OVR_LD | FUS_BL);
digitalWrite(pinBuz, HIGH);
delay(500);
digitalWrite(pinBuz, LOW);
delay(2000);
refreshDisp(0);
mnsOn = !PIN_MNS;
if (PIN_SW && !mnsOn) {
fireInv();
}
}
void loop()
{
if (invOn) {
// Inverter Mode - optimized condition checking
if (i == 1) {
if (cngOverRequest) {
shutdownInv();
} else {
// Read sensors only at zero-crossing points
vo = analogRead(pinVo);
vb = analogRead(pinVb);
vl = analogRead(pinVl);
// Optimized voltage regulation
if (vo < VO_LO && modIndx < MOD_INDX_MAX) {
modIndx++;
changeDuty();
} else if (vo > VO_HI && modIndx > MOD_INDX_MIN) {
modIndx--;
changeDuty();
}
// Error checking
if (vb < VB_CRIT) erCode = BAT_LO;
else if (vl > VL_MAX) erCode = OVR_LD;
if (erCode) {
shutdownInv(CNG_OVER);
beepErr();
}
if (!PIN_SW) {
shutdownInv(CNG_OVER);
}
}
}
// Mains detection logic
if (mnsOn) {
if (cngOverRequest) {
if (millis() - zcTime > CNG_OVER_DLY) {
shutdownInv(CNG_OVER);
}
} else if (PIN_ZC && !zcPrev) {
cngOverRequest = pcyl;
zcTime = millis();
}
zcPrev = PIN_ZC;
mnsOn = !PIN_MNS;
} else {
if (!PIN_MNS) {
if (!mnsTime) {
mnsTime = millis();
} else if (millis() - mnsTime > MNS_WAIT_TIME) {
mnsOn = true;
mnsTime = 0;
zcPrev = true;
cngOverRequest = false;
}
} else {
mnsTime = 0;
}
}
} else {
// Mains Mode - optimized charging logic
if (mnsOn) {
if (!chgOn) {
if (millis() - timeMnsOn > 2500) {
startChg();
}
} else {
if (millis() - timePrev > 9) {
const unsigned int vbatMax = topChg ? VB_TOP : VB_MAX;
vb = analogRead(pinVb);
vc = analogRead(pinVc);
if (vc < VC_MAX && vb < vbatMax && OCR1B < MAX_COUNT_CHG) {
OCR1B++;
} else if ((vb > vbatMax || vc > VC_MAX) && OCR1B > 0) {
OCR1B--;
}
topChg = topChg ? vc <= VC_TOP : (vb >= VB_MAX && vc < VC_TOP);
if ((ledsr & CHG_ON) && topChg) {
refreshDisp(ledsr & ~CHG_ON);
} else if (!(ledsr & CHG_ON) && !topChg) {
refreshDisp(ledsr | CHG_ON);
}
timePrev = millis();
}
}
if (!(ledsr & MNS_ON)) {
refreshDisp(ledsr | MNS_ON);
}
pcyl = !PIN_ZC;
} else {
if (PIN_SW) {
fireInv();
}
if (!invOn) {
timeMnsOn = millis();
if (chgOn) {
stopChg();
}
if (ledsr & MNS_ON) {
refreshDisp(ledsr & ~MNS_ON);
}
}
}
mnsOn = !PIN_MNS;
}
}
// Optimized ISR with minimal operations
ISR(TIMER1_OVF_vect)
{
if (i >= NP) {
i = 0;
pcyl = !pcyl;
}
if (i == 1) {
if (pcyl) {
TCCR1A = 0b11000010;
TIMSK1 = 0b00000101;
} else {
TCCR1A = 0b00110010;
TIMSK1 = 0b00000011;
}
TIFR1 |= 0b00000110;
}
setPWMValues(i);
if (lookUp[i] > THC) {
PORTB |= 0b00011000;
} else if (pcyl) {
PORTB &= 0b11110111;
} else {
PORTB &= 0b11101111;
}
i++;
}
ISR(TIMER1_COMPA_vect)
{
if (invOn) {
PORTB &= 0b11101111;
} else if (OCR1B > 0) {
PORTB |= 0b00011000;
}
}
ISR(TIMER1_COMPB_vect)
{
if (invOn) {
PORTB &= 0b11110111;
} else {
PORTB &= 0b11100111;
}
}
// Inline function for faster PWM value setting
inline void setPWMValues(byte idx)
{
if (pcyl) {
OCR1A = lookUp[idx];
OCR1B = lookUp[idx] - DT;
} else {
OCR1B = lookUp[idx];
OCR1A = lookUp[idx] - DT;
}
}
void changeDuty(void)
{
// Boundary check
if (modIndx > MOD_INDX_MAX) modIndx = MOD_INDX_MAX;
if (modIndx < MOD_INDX_MIN) modIndx = MOD_INDX_MIN;
lookUp[0] = MAX_COUNT;
// Calculate quarter cycle - optimized loop
for (int i = 1; i <= Q_CYCLE; i++) {
lookUp[i] = MAX_COUNT - ((pgm_read_word(&lookUpInverseHalf[i]) * modIndx) / 50);
}
// Mirror quarter cycle - optimized copy
for (int i = Q_CYCLE + 1, j = Q_CYCLE - 1; i < NP; i++, j--) {
lookUp[i] = lookUp[j];
}
}
void fireInv(void)
{
TIMSK1 = 0;
PORTB &= 0b11100001;
vb = analogRead(pinVb);
if (vb > VB_LOW) {
W_CNG_RLY_HIGH;
refreshDisp(INV_ON);
TCCR1A = pcyl ? 0b11000010 : 0b00110010;
TCCR1B = 0b00011001;
ICR1 = MAX_COUNT;
i = 0;
setPWMValues(i);
PORTB &= 0b11100001;
TIFR1 |= 0b00000111;
TIMSK1 = pcyl ? 0b00000101 : 0b00000011;
sei();
invOn = true;
mnsOn = false;
chgOn = false;
erCode = 0;
} else {
if (!(ledsr & BAT_LO)) {
refreshDisp(BAT_LO);
digitalWrite(pinBuz, HIGH);
}
delay(800);
digitalWrite(pinBuz, LOW);
delay(200);
}
}
void shutdownInv(bool coEnable = false)
{
TIMSK1 = 0;
TCCR1A = 0b00000010;
PORTB &= 0b11100001;
timeInvOff = millis();
if (coEnable) {
delay(1);
W_CNG_RLY_LOW;
invOn = false;
refreshDisp(!erCode ? MNS_ON : erCode);
mnsOn = !PIN_MNS;
timeMnsOn = millis();
cngOverRequest = false;
modIndx = 70;
changeDuty();
}
chgOn = false;
}
void startChg(void)
{
refreshDisp(ledsr | CHG_ON);
TCCR1A = 0b00000010;
TCCR1B = 0b00011001;
ICR1 = MAX_COUNT;
OCR1A = 0;
OCR1B = 0;
PORTB &= 0b11100001;
TIFR1 |= 0b00000111;
TIMSK1 = 0b00000110;
chgOn = true;
topChg = false;
}
void stopChg(void)
{
OCR1A = 0;
OCR1B = 0;
TIMSK1 = 0;
PORTB &= 0b11100001;
chgOn = false;
refreshDisp(ledsr & ~CHG_ON);
}
void refreshDisp(const byte oneByte)
{
ledsr = oneByte;
lcd.clear();
if (oneByte & INV_ON)
lcd.print("INVERTER ON");
else if (oneByte & MNS_ON)
lcd.print("MAINS ON");
if (oneByte & 0x3F) {
lcd.setCursor(0, 1);
if (oneByte & CHG_ON)
lcd.print("CHARGING...");
else if (oneByte & BAT_LO)
lcd.print("BATTERY LOW!");
else if (oneByte & OVR_LD)
lcd.print("OVER LOAD!");
else if (oneByte & FUS_BL)
lcd.print("FUSE BLOWN!");
}
}
void beepErr(void)
{
digitalWrite(pinBuz, HIGH);
delay(800);
digitalWrite(pinBuz, LOW);
delay(3000);
}