#define info0  "EMC4 Build-01 PWMGen"
#define info1  "F/W P0325-C01_v0.13 " 
#define info2  "Assy: CES180325-3   "
#define info3  "Date: 10/12/2018    "

/* 
 * P0325-C01_v0.13 10/12/18 -------------------------------- 
 *    Check PWM Prescaller, update calculation OK 
 *    Program 47%, Memory 49%
 *  Ready for Acceptance Test
 *    Pending LCD Display I2C Address updater
 *    Pending CS Current Calibration
 * P0325-C01_v0.12 9/19/18 -------------------------------- 
 *  Program 49%, Memory 49%
 * --------------------------------------------------------
 * Pro Mini 16MHz/5V
 *    1x5 Extend Header
 * --------------------------------------------------------
 *    Pro MiniPORT Assignments
 *      A7  'sFreq' POT Frequency Adj
 *      A6  'sDutycycle' POT Duty Adj
 *      A5/D19  I2C-SCL
 *      A4/D18  I2C-SDA
 * -----------------------     
 *      A3/D17  'sPrescaler' POT Prescaler Adj
 *      A2/D16  'ps1A' DMA-CS (Ics Load Current 100A)
 *      A1/D15  'ps2V' DMV-PB (PROBE 200V)
 *      A0/D14  'ps1V' DMV-PS (PS+ 200V)
 *      D13 loopbeat (IDE Reserved Output Only)
 *      D12 Output ZDRV (connect to ZDRV.D17)
 *      D11 Output 'freqout' PWM-OUT 
 *      D10 Input EDM-ON sameas ZDRV.D10 (HI=EDM-On)
 *      D9  Input DCOMM nRXD Rx NodeReady (LO=NotReady)
 *      D8  Output DCOMM nTXD Tx RunMode (LO=EDM-OFF)
 *      D7  Output 'FREQOFF' F/2 OFF(-) GND = OFF
 *      D6  Output 'PWMOUT' PWM OFF(-) GND = OFF
 *      D5  Output 'DRVOFF' OFF(-) GND = OFF
 *      D4  Input 'modesw' Mode SW GND=F/2 50% dutycycle
 *      D3  Output 'pwmout' PWM output
 *      D2  Data 'Tport' DS18B20 Temp Sensor
 * -----------------------     
 *      D1  RXD reserved
 *      D0  TXD reserved
 * === Pro Mini w/ 5x1 Extended Header =========================
 *  (D9)(D8)(D7)(D6)(D5)(D4)(D3)(D2)(GND)(RES)(RXD)(TXD) < DTR >
 *      (GND) *                                          < RX >
 *         A4 *                                          < TX >
 *         A5 *                                          < VCC >
 *         A6 *                                          < CTS >
 *         A7 *                                          < GND >
 *  (D10)(D11)(D12)(D13)(A0)(A1)(A2)(A3)(VCC)(RES)(GND)(RAW) 
 * =============================================================
 */

// 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>
  #define Tport 2  // Port
  #define Tdata 9  // Resolution
  //#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;

// ZDRV Interface
  #define zDRV 12 //ZDRV Interface Input Port
  #define Ready HIGH
  #define NotReady LOW
  String NRmsg = " ! Node NOT Ready ! ";
  #define zEDM  10  //Input connect to ZDRV EDM Control Port D10 (HI=EDM-ON)
  #define zEDMon HIGH
  #define zEDMoff LOW
  
// Node Interface
  #define nRXD 9  //Input from all Nodes (LO=NotReady)
  #define nTXD 8  //Output Broadcast EDM Control On/Off Status to all Nodes
  #define EDMon LOW
  #define EDMoff HIGH  
  
// 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.196899874;  // 200.0 FSC pre-scale 200/1023 
  double K2V = 0.196899874;  // 200.0 FSC pre-scale 200/1023 
  //double K1A = 0.106251859;  // 100.0 FSC pre-scale 100/1023 
  //double K1V = 0.196899874;  // 200.0 FSC pre-scale 200/1023 Calibrated
  //double K2V = 0.196899874;  // 200.0 FSC pre-scale 200/1023 Calibrated
  double K1A = 0.115545796;  // 100.0 FSC pre-scale 100/1023 Calibrated

// AnalogREAD Array
  const byte AAcount = 10;  // sample size
  byte ps1VA[AAcount]; // array
  byte ps2VA[AAcount]; // array
  byte ps1AA[AAcount]; // array
  byte Aindex = 0;  // index
  byte ps1Vmin;
  byte ps1Vavg;
  byte ps1Vmax;
  byte ps2Vmin;
  byte ps2Vavg;
  byte ps2Vmax;
  byte ps1Amin;
  byte ps1Aavg;
  byte ps1Amax;
  
// PWM I/O
  #define sFreq A7  //POT Freq Adj
  #define sDutycycle A6  //POT Dutycycle Adj
  #define sPrescaler A3  //POT Prescaler 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

// PWM POT
  byte potfreq; //Freq ADC Value
  byte lastpotfreq; //save value
  byte potduty; //dutycycle ADC Value
  byte lastpotduty; //save value
  byte potprescale; //Prescaler ADC Value
  byte lastpotprescale;  //save value

// PWM Output
  byte pwmfreq; //Freq ADC Value
  byte pwmduty; //dutycycle ADC Value
  byte pwmprescale; //Prescaler ADC Value
  float pwmHt; // for storing high time
  float pwmfrequency; // for storing frequency
  float F2frequency;
  float F2Ht;

// PWM Controls
  int divider2[8] = { 0,1,8,32,64,128,256,1024 };
  //float freqmax[8] = { 0, 1333333, 166667, 41667, 20833, 10417, 5208, 1302 };
  //float freqmin[8] = { 0, 31746, 3968, 996, 496, 248, 124, 31 };
  byte ocra = 126; // set to 1KHz
  byte ocrb = 63;  // OCRA/2 50% dutycycle Phase Corrected
  double sysclk = 16000000; //system clock 16MHz default

// PWM Prescaler MSG 2004 20-chr
  char* scalerPWM[]={
    "0     INOP Not Used ",
    "/1    F=  32K~1.3MHz",
    "/8    F= 3.9K~166KHz",
    "/32   F= 977~41.6KHz",
    "/64   F= 496~20.8KHz",
    "/128  F= 248~10.4KHz",
    "/256  F=  124~5.2KHz",
    "/1024 F=   31~1.3KHz"};
  char* scalerF2[]={
    "0     INOP Not Used ",
    "/1    F=  16K~650KHz",
    "/8    F=  1.9K~83KHz",
    "/32   F= 498~20.8KHz",
    "/64   F= 248~10.4KHz",
    "/128  F=  124~5.2KHz",
    "/256  F=   62~2.6KHz",
    "/1024 F=    16~651Hz"};

// System Variable
  #define loopbeat 13  // toggle onboard LED per Loop
  String mfg; // Long MSG
  int rdisplock = 0;  //>0 lock rdisplay
  byte rline; //rdisp line#
  String rmfg; //rdisp message
  String rtemp; //temperary msg storage
  unsigned long msNow; //Current Wait Timer
  unsigned long msLast; //Last Wait Timer
  unsigned long msWait = 1000; //timer default at 1-second Wait  
  byte waitprescaler = 0; // Prescaler display timer
  byte waitfreq = 0; // Freq display timer
  byte waitduty = 0;  // Duty display timer
  byte waitFT = 3;  // Freq/Time display timer
  #define waitFTset 3 // Freq/Time display change period default
  byte showF = 1; // Freq/Time display setting
  byte screenscan = 1;
  byte screen = 0;  // screen page index
  #define freqpage 1 // screen page for freq setup
  #define bypass 2  // screen page for Node Info
  
/* ------------------------------------------------------------------------*/
/// Setup Start Here
void setup() {
  
  // Loop Beat
  pinMode(loopbeat, OUTPUT); 
  
  //Output Controls (ALL OFF)
  pinMode(FREQOFF, OUTPUT); digitalWrite(FREQOFF, LOW);
  pinMode(PWMOFF, OUTPUT); digitalWrite(PWMOFF, LOW);
  pinMode(DRVOFF, OUTPUT); digitalWrite(DRVOFF, LOW);

  //ZDRV Interface
  pinMode(zDRV, OUTPUT); digitalWrite(zDRV, Ready);
  pinMode(zEDM, INPUT_PULLUP);

  //Node Interface
  pinMode(nRXD, INPUT_PULLUP);
  pinMode(nTXD, OUTPUT);
  
  //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
  mfg = (info0); printmfg(0,0);
  mfg = (info1); printmfg(0,1);
  mfg = (info2); printmfg(0,2);
  mfg = (info3); printmfg(0,3);
  delay(4000);
  
}
/// Setup End Here
/* ------------------------------------------------------------------------*/
/*  P r o g r a m   S t a r t   H e r e
/* ------------------------------------------------------------------------*/
void loop() {  
  
  // Loop Beat on D13 LED
  if (digitalRead(loopbeat)==LOW) digitalWrite(loopbeat,HIGH); else digitalWrite(loopbeat,LOW);
  
  // PWM read POT for change
  readPWMpot();
  if ((potfreq>lastpotfreq+1) || (potfreq<lastpotfreq-1) || (potduty>lastpotduty+1) || (potduty<lastpotduty-1) || (potprescale!=lastpotprescale)) {
    setPWM();
    msLast = msNow; waitfreq=3;
  }

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

  // Screen Page Display
  if (waitfreq>0) {
    screen = freqpage; 
    rdisplock = 0; 
    rline=0;
  } else {
    if (rdisplock>0) { screen = bypass; } else { screen=0; }
  }
  
  switch(screen) {
    case 0: // default screen
      lcd2.setCursor(0,0);
      if (digitalRead(modesw)==LOW) {
        if (showF>0) dispFD(F2frequency, F2Ht); else dispHL(F2frequency, F2Ht);
      } else {
        if (showF>0) dispFD(pwmfrequency, pwmHt); else dispHL(pwmfrequency, pwmHt);
      }
      lcd2.setCursor(0,1); dispVAW1(); 
      lcd2.setCursor(0,2); dispVAW2();
      lcd2.setCursor(0,3); 
      if (digitalRead(nRXD)==NotReady) lcd2.print (NRmsg); else showT12();
      break;

    case freqpage: // freq/Duty/Prescaler change screen
      lcd2.setCursor(0,0); 
        if (digitalRead(modesw)==LOW) dispFD(F2frequency, F2Ht); else dispFD(pwmfrequency, pwmHt);
      lcd2.setCursor(0,1); 
         if (digitalRead(modesw)==LOW) dispHL(F2frequency, F2Ht); else dispHL(pwmfrequency, pwmHt);
      lcd2.setCursor(0,2); 
        if (digitalRead(modesw)==LOW) lcd2.print(scalerF2[pwmprescale]); else lcd2.print(scalerPWM[pwmprescale]);
      lcd2.setCursor(0,3);
        if (digitalRead(nRXD)==NotReady) lcd2.print (NRmsg); else ocrab();      
      break;

    case bypass:
      break;
  }

  //Output Controls (ALL ON)
  digitalWrite(FREQOFF, HIGH);
  digitalWrite(PWMOFF, HIGH);
  digitalWrite(DRVOFF, HIGH);

  //Interprete Node and PWM OFF status
  if ((digitalRead(nRXD)==NotReady) || (ocrb==0)) digitalWrite(zDRV,NotReady); else digitalWrite(zDRV,Ready);
    
  //Broadcast Console EDM Status 
  if (digitalRead(zEDM)==zEDMoff) digitalWrite(nTXD,EDMoff); else digitalWrite(nTXD,EDMon);

  // Auto Timer Update
  msNow = millis();
  if (msNow-msLast > msWait) {
    msLast = msNow;
    if (waitprescaler>0) waitprescaler--;
    if (waitfreq>0) waitfreq--;
    if (waitduty>0) waitduty--;
    if (waitFT>0) waitFT--; else {
      waitFT=waitFTset;
      if (showF==0) showF=1; else showF=0; }
    if (rdisplock>0) rdisplock--; 
  }

}
/// Program End Here
/* ------------------------------------------------------------------------*/

/// Print mfg on Cursor i, Line j
void printmfg(int i, int j) {
  lcd2.setCursor(i,j); lcd2.print(mfg);
}

/// Clear line J
void dispclear(int j) {
  lcd2.setCursor(0,j); lcd2.print ("                "); 
}

/// << PWM Section >> -------------------------------------------------

/// Read Freq/Duty/Prescale POTs 
void readPWMpot() {
  int j;
  j = 2; if (j != 0) { potfreq = analogRead(sFreq)/4; j--; }
  j = 2; if (j != 0) { potduty = analogRead(sDutycycle)/4; j--; }
  j = 2; if (j != 0) { potprescale = analogRead(sPrescaler)/147; j--; }
  //potduty = 255 - potduty;  //swap H/L
  if (potfreq < 6) { potfreq = 6; }
  if (potduty > 240) { potduty = 240; } //Limit MAX dutycycle to ~90%
}

/// Set PWM Output
void setPWM()  {
  lastpotfreq = potfreq;
  lastpotduty = potduty;
  lastpotprescale = potprescale;
  ocra = potfreq;
  ocrb = potduty * ocra / 255;
  pwmprescale = potprescale +1; //skip 0
  pwmfrequency = sysclk / divider2[pwmprescale] / (ocra) / 2;  //Phase Corrected
  F2frequency = pwmfrequency / 2;
  F2Ht = (1000000 / F2frequency / 2);
  pwmHt = (float)ocrb / (float)ocra * (1000000/pwmfrequency); // in uS
  switch(pwmprescale) { 
    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
  }
  OCR2A = ocra;  OCR2B = ocrb;
}

/// PWM Display Freq/Duty "FREQ xxx.xxxKHz D%xx" 20-chr
void dispFD(float freq, float Ht) {
    mfg = "FREQ ";
    if (ocrb==0) { mfg=mfg+" -- PWM OFF -- "; } else {
      float Tt = 1000000 / freq; // in uS
      if ((freq/1000)<10) { mfg=mfg+"  "; } else if ((freq/1000)<100) { mfg=mfg+" ";  }
      if (freq<1000000) { mfg=mfg + String(freq/1000,3); } else { mfg=mfg + String(freq/1000,2); }
      mfg += "KHz D"+ String((Ht/Tt)*100,0) + "%";
    }
    lcd2.print(mfg);
}

/// PWM Display H/L Time "ON= xxxuS OFF= xxxuS"

/// H/L Time Display // 16 chr
void dispHL(float freq, float Ht) {
    float Lt = 1000000 / freq - Ht; // in uS
    mfg = "ON "; tDisp(Ht);
    mfg += " OFF "; tDisp (Lt);
    lcd2.print(mfg);
}

/// H/L Data output
/// "x.xxuS" 
/// "xx.xuS" 
/// " xxxuS" 
/// "x.xxmS" 
/// "xx.xmS"
/// " xxxmS"
void tDisp (float T) {
    if ((T)>1000) {  //mS Range
      if ((T/1000)<10) { 
        mfg += String(T/1000,2); 
      } else if ((T/1000)<100) {
        mfg += String(T/1000,1);
      } else { mfg += "="; mfg += String(T/1000,0); }
      mfg += "mS";
    } else {
      if ((T)<10) { 
        mfg += String(T,2); 
      } else if ((T)<100) { 
        mfg += String(T,1); 
      } else { mfg += "="; mfg += String(T,0); }
      mfg += "uS";
    }
}

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

/// << DMM Section >> ///////////////////////////////////
/// Array READ
void AAread() {
  Aindex++; if (Aindex == AAcount) Aindex = 0;
  int j;
    j = 2; if (j != 0) { ps1VA[Aindex] = analogRead(ps1V); j--; }
    j = 2; if (j != 0) { ps2VA[Aindex] = analogRead(ps2V); j--; }
    j = 2; 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);
    rmfg = String(TempC,1);
    //mfg=""; 
    for (int j=5; j>rmfg.length(); j--) mfg += " ";
    mfg += (rmfg); mfg += "C";
}

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

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

