const char* info0 = "F/W: PWMGen3_v0.20  ";  // 20 chr
const char* info1 = "  Release 03/04/2018";
const char* info2 = "PCB Assy#  CES171107";                                                                
const char* info3 = "  update as built   ";

/* 
 *  PWMGen3_v0.2
 *    Add Alternet display of Freq/Duty and On-Time/Off-Time
 *  Mod Base on PWMGen3 v0.17 2/18/18 
 *  Initial 1st Built EDMP3 Lawrence Chu v0.17 2/18/18
 *    Remove Watt Display on EDP
 *  Initial 1st Built EDMP3 Lawrence Chu 2/16/18
 *    Add Min/Max/Avg AnalogRead()
 *    Reduce Prescaller display time to half
 *    PS+ dispVAW1() ps1Vmax, ps1Aavg
 *    EDP dispVAW2() ps2Vmin, ps1Amax
 *  Initial 1st Built EDMP3 Lawrence CHu 2/13/18
 *    Limit dutycycle to 90% MAX <dutycycleMAX>
 *  Initial 1st Built EDMP3 Lawrence CHu 2/4/18
 *    Change Info Display timing to 2s (same as ZDRV3)
 *    T1 = HS, T2 = IN
 *  Initial Release v0.10 Lawrence Chu 11/27/17
 * --------------------------------------------------------
 * Pro Mini 16MHz/5V
 *    Serial Monitor = None
 * --------------------------------------------------------
 * LCD 2004 I2C (5V)
 *    A5 = SCL
 *    A4 = SDA
 * --------------------------------------------------------
 * PWM Generator (I/O & POT Controls)
 *    A3 = Adj Prescale
 *    A7 = Adj Freq
 *    A6 = Adj dutycycle
 *    D11 = Output Freq/2 50% dutycycle
 *    D3 = Output PWM 
 *    D4 = Mode SW GND=F/2 50% dutycycle
 *    D5 = DRV Output OFF(-) GND = OFF
 *    D6 = PWM Output OFF(-) GND = OFF
 *    D7 = F/2 Output OFF(-) GND = OFF
 *    D12 *Reserved 
 * --------------------------------------------------------
 * DS18B20 Temperature Sensor (2x)
 *    D2 = Data Port
 *    Auto Detect
 * --------------------------------------------------------
 * ZSense Monitor
 *    A0 = DMV Power Supply (200V)
 *    A1 = DMV Probe (200V)
 *    A2 = DMA Current (50A)
 * 
 */

// LCD 2004 with I2C
  #include <FastIO.h>
  #include <I2CIO.h>
  #include <LiquidCrystal_I2C.h>
  // the pins on the I2C chip used for LCD connections:
  //                     addr, en,rw,rs,d4,d5,d6,d7,bl,blpol
  LiquidCrystal_I2C lcd2(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // TOP LCD TESTER
  //LiquidCrystal_I2C lcd2(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // TOP LCD EDMP3
  
// DS18B20 Temperature Sensor(s)
  #include <OneWire.h>
  #include <DallasTemperature.h>
  #include <Wire.h>
  #define Tport 2  // Port
  #define Tdata 10  // Resolution
  // Setup a oneWire instance to communicate with any OneWire devices
  OneWire oneWire(Tport);
  // Pass our oneWire reference to Dallas Temperature. 
  DallasTemperature sensors(&oneWire);
  // arrays to hold device addresses
  DeviceAddress Tsensor1, Tsensor2; // for Auto Config
  //DeviceAddress Tsensor1 = { 0x28, 0xFF, 0x77, 0x95, 0x94, 0x16, 0x04, 0x85 };  // RR= Brown
  //DeviceAddress Tsensor2 = { 0x28, 0xFF, 0xE8, 0x8E, 0x86, 0x16, 0x04, 0xF4 };  // LT= Blue
  float TempC;

// DMM Variables
  #define ps1V A0  // Supply Voltage PS+ Port 
  #define ps2V A1  // Probe Voltage Port 
  #define ps1A A2  // Current CS+ Port  
  // for testing only
  //#define ps1V A6  // Supply Voltage PS+ Port 
  //#define ps2V A7  // Probe Voltage Port 
  //#define ps1A A3  // Current CS+ Port  

  float dmmV;
  float dmmA;
  float dmmW;
  float dmmVdutycycle;
  float dmmAdutycycle;
  double K1V = 0.19550;  // 200.0 FSC pre-scale 200/1023 
  double K2V = 0.19550;  // 200.0 FSC pre-scale 200/1023 
  double K1A = 0.04887;  //  50.0 FSC pre-scale 50/1023 

// AnalogREAD Array
  const int AAcount = 10;  // sample size
  unsigned int ps1VA[AAcount]; // array
  unsigned int ps2VA[AAcount]; // array
  unsigned int ps1AA[AAcount]; // array
  unsigned int Aindex = 0;  // index
  unsigned int ps1Vmin;
  unsigned int ps1Vavg;
  unsigned int ps1Vmax;
  unsigned int ps2Vmin;
  unsigned int ps2Vavg;
  unsigned int ps2Vmax;
  unsigned int ps1Amin;
  unsigned int ps1Aavg;
  unsigned int ps1Amax;
  
// PulseGen I/O
  #define sFreq A7  //POT Freq Adj
  #define sdutycycle A6  //POT dutycycle Adj
  #define sPrescaler A3  //POT dutycycle Adj
  #define FREQOFF 7  //Output F/2 OFF(-)
  #define PWMOFF 6  //Output PWM OFF(-)
  #define DRVOFF 5  //Output F/2 OFF(-)
  
  #define freqout 11  //F/2 50% dutycycle output
  #define pwmout 3  //PWM output
  #define modesw 4  //Mode Switch input LOW=F/2

// PulseGen Variables
  double sysclk = 16000000; //system clock 16MHz default
  byte potfreq; //Freq Read ADC Value
  byte potdutycycle; //dutycycle Read ADC Value
  byte potprescale; //Prescale Read ADC Value
  float frequency; // for storing frequency
  float dutycycle;  //dutycycle 
  float Htime; // integer for storing high time
  float Ltime; // integer for storing low time
  float Ttime; // integer for storing total time of a cycle
byte ocra = 126; // set to 1KHz
  byte ocrb = 63;  // OCRA/2 50% dutycycle Phase Corrected
  int divider2[8] ={0,1,8,32,64,128,256,1024};
  byte prescaler2=4; //defualt Divider=64
  

// Prescaler MSG 2004 20-chr
  char* scalerPWM[]={
    "0     INOP Not Used ",
    "/1    F=  32K~380KHz",
    "/8    F=  3.9K~47KHz",
    "/32   F= 977~11.9KHz",
    "/64   F= 488~5.95KHz",
    "/128  F= 244~2.97KHz",
    "/256  F= 122~1.48KHz",
    "/1024 F=   69 ~845Hz"};
  char* scalerFreq[]={
    "0     INOP Not Used ",
    "/1    F=  16K~190KHz",
    "/8    F=  1.9K~23KHz",
    "/32   F= 488~5.95KHz",
    "/64   F= 244~2.97KHz",
    "/128  F= 122~1.48KHz",
    "/256  F= 122~1.48KHz",
    "/1024 F=  34.5~422Hz"};

// System Variable
  byte loopcount = 0;  //Init timeout
  byte loopmax = 25; // maximum loop count
  #define dispmax 100  // loop count for display change
  byte dispcount = 0; // Freq display change counts
  String mfg; // Long MSG
  
/* ------------------------------------------------------------------------*/
/// Setup Start Here
void setup() {
  //Output Controls (ALL OFF)
  pinMode(FREQOFF, OUTPUT); digitalWrite(FREQOFF, LOW);
  pinMode(PWMOFF, OUTPUT); digitalWrite(PWMOFF, LOW);
  pinMode(DRVOFF, OUTPUT); digitalWrite(DRVOFF, LOW);

  //LCD setup
  lcd2.begin(20, 4);

  //PWM setup
  pinMode(modesw, INPUT_PULLUP); // PWM=HI F/2=LOW
  pinMode(pwmout, OUTPUT); // Variable dutycycle port
  pinMode(freqout, OUTPUT); // 2/F 50% dutycycle port
  // PWM Mode (Fast or Phase-Correct)
  //TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); //Fast PWM
  TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM20); // Phase Correct PWM
  TCCR2B = _BV(WGM22) | _BV(CS22);

  // Display Info
  showinfo(); lcd2.clear();
  
}
/// Setup End Here
/* ------------------------------------------------------------------------*/
/// Program Start Here
void loop() {
  
  // Check Prescaler Freq dutycycle
  prescalerpot(); // Read Prescaler POT Setting
  if (prescaler2 == potprescale) {
    if (loopcount < loopmax) loopcount++; } 
  else { 
    prescaler2 = potprescale;
    loopcount=0; }
  pscaler();  // to ensured register setting
  freqpot(); // Read Freq/dutycycle POT Setting
  // Set PulseGen Output
  OCR2A = ocra;
  OCR2B = ocrb;

  // ADC Read DMM Section
  AAread(); 
  AAproc();

  // Alternate Display counter
  if (dispcount > dispmax) dispcount = 0; else dispcount ++;
  
  // Display Freq & Scaller Row#1-2
  if (digitalRead(modesw)==LOW) {
    lcd2.setCursor(0,0); 
    if (dispcount < dispmax/2) freqDF(); else hltimeD();
    lcd2.setCursor(0,1); 
    //if (loopcount < loopmax) lcd2.print(scalerFreq[prescaler2]); else showVAW1(); } // realtime
    if (loopcount < loopmax) lcd2.print(scalerFreq[prescaler2]); else dispVAW1(); }
  else {
    lcd2.setCursor(0,0);
    if (dispcount < dispmax/2) freqD(); else hltimeD();
    lcd2.setCursor(0,1); 
    //if (loopcount < loopmax) lcd2.print(scalerPWM[prescaler2]); else showVAW1(); }  // realtime
    if (loopcount < loopmax) lcd2.print(scalerPWM[prescaler2]); else dispVAW1(); }
  // Row #3 RAW VAW or CRxA/B
  lcd2.setCursor(0,2);  
  //if (loopcount < loopmax) ocrab(); else showVAW2();  // realtime
  if (loopcount < loopmax) ocrab(); else dispVAW2();
  
  // Row #4 Dual Temp
  lcd2.setCursor(0,3);  
  showT12();
  //ocrab();

  //Output Controls (ALL ON)
  digitalWrite(FREQOFF, HIGH);
  digitalWrite(PWMOFF, HIGH);
  digitalWrite(DRVOFF, HIGH);
  
}
/// Program End Here
/* ------------------------------------------------------------------------*/
/// SYS Function to display info
void showinfo() {
  lcd2.setCursor(0,0); lcd2.print(info0);
  lcd2.setCursor(0,1); lcd2.print(info1);
  lcd2.setCursor(0,2); lcd2.print(info2);
  lcd2.setCursor(0,3); lcd2.print(info3);
  delay(2000);
}

/// PulseGen Prescaller Setting
void pscaler()  {
  switch(prescaler2) { // Phase Corrected
    case 1: TCCR2B = TCCR2B & B11111000 | B00000001; break; // T2 divisor = 1   INOP
    case 2: TCCR2B = TCCR2B & B11111000 | B00000010; break; // T2 divisor = 8   3.9KHz ~ 50KHz (H)
    case 3: TCCR2B = TCCR2B & B11111000 | B00000011; break; // T2 divisor = 32  980Hz ~ 12.2KHz
    case 4: TCCR2B = TCCR2B & B11111000 | B00000100; break; // T2 divisor = 64  247Hz ~ 6.2KHz (M)
    case 5: TCCR2B = TCCR2B & B11111000 | B00000101; break; // T2 divisor = 128 491Hz ~ 3.15KHz
    case 6: TCCR2B = TCCR2B & B11111000 | B00000110; break; // T2 divisor = 256 121Hz ~ 1.56KHz (L)
    case 7: TCCR2B = TCCR2B & B11111000 | B00000111; break; // T2 divisor = 1024 31Hz ~ 390Hz
  }
}

/// Get Prescaler from POT
void prescalerpot() {
  int j;
  j = 3; if (j != 0) { potprescale = analogRead(sPrescaler)/147; j--; }
  potprescale++; 
}

/// Get Frequency/dutycycle from POT
void freqpot() {
  int j;
  j = 3; if (j != 0) { potfreq = analogRead(sFreq)/4; j--; }
  j = 3; if (j != 0) { potdutycycle = analogRead(sdutycycle)/4; j--; }
  if (potfreq < 6) { potfreq = 6; }
  if (potdutycycle > 240) { potdutycycle = 240; } //Limit MAX dutycycle to ~90%
  ocra = potfreq;
  ocrb = potdutycycle * ocra / 255;
  //ocrb = potdutycycle;
  frequency = sysclk / divider2[prescaler2] / (ocra+1) / 2;
  Ttime = (float)1000000/frequency; // in uS
  dutycycle = (float)ocrb / (float)ocra; 
  Htime = (float)dutycycle * Ttime;
  Ltime = (float)Ttime - Htime;  
}

/// PulseGen Display "FREQ xxx.xxxKHz D%xx" 20-chr
/// PulseGen Display "Fxxx.xxxKHz D%xx" 16-chr
void freqD() {
    mfg = "FREQ ";  //20-chr
    //mfg = "F";  //16-chr
    if ((frequency/1000)>400) { mfg += "! Over Range ! "; } 
    else {
      if ((frequency/1000)<10) { mfg += "  "; } 
      else if ((frequency/1000)<100) { mfg += " ";  }
      mfg += String(frequency/1000,3) + "KHz ";
      //dutycycle = (float)ocrb / (float)ocra;
      if ((dutycycle<0.99) && (dutycycle>0.001)) { mfg += "D%"; mfg += String(dutycycle*100 ,0); } 
      else 
        if (dutycycle>0.99) { mfg += "D%99"; }
        if (dutycycle<0.001) { mfg += "=OFF"; }
        lcd2.print(mfg);
    }
}

/// PWMout Display H/L Time "12345678901234567890"
///                         "ON= xxxuS OFF= xxxuS"
void hltimeD() {
    mfg = "ON:";
    if ((Htime)>999) {
      if ((Htime/1000)<10) { mfg=mfg+" "; } 
      mfg=mfg + String(Htime/1000,1) + "mS";
    } else {
      mfg += " ";
      if (Htime<10) { mfg=mfg+" "; } else if (Htime<100) { mfg=mfg+" "; }
      mfg = mfg + String(Htime,0) + "uS";
    }
    mfg += " OFF:";
    if ((Ltime)>1000) {     
      if ((Ltime/1000)<10) { mfg=mfg+" "; } 
      mfg=mfg + String(Ltime/1000,1) + "mS";
    }
    else {
      mfg += " ";
      if (Ltime<10) { mfg=mfg+" "; } else if (Ltime<100) { mfg=mfg+" "; }
    mfg=mfg + String(Ltime,0) + "uS";
    }
    lcd2.print(mfg);
}

/// PulseGen Display In F/2 Mode
void freqDF() {
    mfg = "FREQ ";  //20-chr
    //mfg = "F";  //16-chr
    frequency=frequency/2;
    if ((frequency/1000)>200) { mfg += "! Over Range ! "; } 
    else {
      if ((frequency/1000)<10) { mfg += "  "; } 
      else if ((frequency/1000)<100) { mfg += " ";  }
      mfg += String(frequency/1000,3) + "KHz D%50";
      lcd2.print(mfg);
    }
}

/// PulseGen Display "CRxA=xxx  CRxB=xxx" //20-chr
/// PulseGen Display "CxA=xxx  CxB=xxx" //16-chr
void ocrab() {
    mfg = "CRxA:";  //20-chr
    //mfg = "CRxA:";  //16-chrt
    if (ocra<10) { mfg += "  "; } else if (ocra<100) { mfg += " "; }
    mfg += String(ocra) + "   CRxB:"; //20-chr
    //mfg += String(ocra) + "   CxB:"; //16-chr
    if (ocrb<10) { mfg += "  "; } else if (ocrb<100) { mfg += " "; }
    mfg += String(ocrb); mfg += (" ");
    lcd2.print(mfg);
}

/// Array READ
void AAread() {
  Aindex++; if (Aindex == AAcount) Aindex = 0;
  int j;
    j = 3; if (j != 0) { ps1VA[Aindex] = analogRead(ps1V); j--; }
    j = 3; if (j != 0) { ps2VA[Aindex] = analogRead(ps2V); j--; }
    j = 3; if (j != 0) { ps1AA[Aindex] = analogRead(ps1A); j--; }
}

/// Array data Processing
void AAproc() {
  ps1Vmin = ps1VA[Aindex]; ps1Vavg = ps1VA[Aindex]; ps1Vmax = ps1VA[Aindex]; 
  ps2Vmin = ps2VA[Aindex]; ps2Vavg = ps2VA[Aindex]; ps2Vmax = ps2VA[Aindex]; 
  ps1Amin = ps1AA[Aindex]; ps1Aavg = ps1AA[Aindex]; ps1Amax = ps1AA[Aindex]; 
  int i=0;
  for (i=0; i<AAcount; i++) {
    if (ps1Vmin > ps1VA[i]) ps1Vmin = ps1VA[i];
    if (ps2Vmin > ps2VA[i]) ps2Vmin = ps2VA[i];
    if (ps1Amin > ps1AA[i]) ps1Amin = ps1AA[i];
    if (ps1Vmax < ps1VA[i]) ps1Vmax = ps1VA[i];
    if (ps2Vmax < ps2VA[i]) ps2Vmax = ps2VA[i];
    if (ps1Amax < ps1AA[i]) ps1Amax = ps1AA[i];
    ps1Vavg = ( ps1Vavg + ps1VA[i] ) /2;
    ps2Vavg = ( ps2Vavg + ps2VA[i] ) /2;
    ps1Aavg = ( ps1Aavg + ps1AA[i] ) /2;
  }
}

/// DMM Display "xxxV xx.xA xxxxW" 16-chr
void dispVAW1() { 
  mfg = "PS+ ";
  dmmV = ps1Vmax * K1V;
  dmmA = ps1Aavg * K1A;
  dmmW = dmmV * dmmA;
  if (dmmV<100) { mfg += " ";  }
  mfg += String(dmmV,0) + "V ";
  if (dmmA<10) { mfg += " ";  }
  mfg += String(dmmA,1) + "A ";
  /// Display "xxxxW" 5-Chr
  if (dmmW>9999) { mfg += " --- "; }
  else {
    if (dmmW<100) { mfg += "  ";  }  else if (dmmW<1000) { mfg += " ";  }
    mfg += String(dmmW,0) + "W"; 
  }
  lcd2.print(mfg);
}

/// DMM Display "xxxV xx.xA xxxxW" 16-chr
void dispVAW2() { 
  mfg = "EDP ";
  dmmV = ps2Vmin * K2V;
  dmmA = ps1Amax * K1A;
  //dmmW = dmmV * dmmA;
  if (dmmV<100) { mfg += " ";  }
  mfg += String(dmmV,0) + "V ";
  if (dmmA<10) { mfg += " ";  }
  mfg += String(dmmA,1) + "A ";
  /// Display Controld Status
  mfg += " ";
  if (digitalRead(FREQOFF) == HIGH) { mfg += "+"; } else { mfg += "-"; }
  if (digitalRead(PWMOFF) == HIGH) { mfg += "+"; } else { mfg += "-"; }
  if (digitalRead(DRVOFF) == HIGH) { mfg += "+"; } else { mfg += "-"; }
  mfg += " ";  
  /// Display "xxxxW" 5-Chr
  //if (dmmW>9999) { mfg += " --- "; }
  //else {
    //if (dmmW<100) { mfg += "  ";  }  else if (dmmW<1000) { mfg += " ";  }
    //mfg += String(dmmW,0) + "W"; 
  //}
  lcd2.print(mfg);
}

/// DS18B20 Dual Temp Display  "T1-xxx.xC  T2-xxx.xC"
void dualTemp(char* pname1, DeviceAddress paddress1, byte pindex1, char* pname2, DeviceAddress paddress2, byte pindex2) {
    mfg = (pname1);
    if (!sensors.getAddress(paddress1,pindex1)) { mfg += "  --- ";  } 
    else {  dispT(paddress1); }
    mfg += "  ";  mfg += (pname2);
    if (!sensors.getAddress(paddress2,pindex2)) { mfg += "  --- ";  } 
    else { dispT(paddress2); }
    lcd2.print (mfg);
}

/// Show Temperatures Sensors
void showT12() { dualTemp("HS=", Tsensor1, 0, "IN=", Tsensor2, 1); }

/// DS18B20 Display Temperature in "-xxx.xC" 8chr
void dispT(DeviceAddress paddress) {
    sensors.requestTemperaturesByAddress(paddress);
    TempC = sensors.getTempC(paddress);
    //mfg=""; 
    if (TempC<0) {
      if (TempC>-10) { mfg += " "; } else if (TempC>-100) { mfg += "";  }
      mfg += String(TempC,1);    
    }
    else {
      mfg += " ";
      if (TempC<10) { mfg += " "; } else if (TempC<100) { mfg += "";  }
      mfg += String(TempC,1);      
    }
    mfg += ("C");
}

/// Software Reset
void resetBoot() {
  asm volatile ("  jmp 0");
}

/// F/W End Here
/* ------------------------------------------------------------------------*/