/*
Titel     : MCU-Butler Control
--------------------------------------------------------------------------------------
Funktion  : Einschalt- und Anzeigesteuerung für MCU-Butler V.2x
Schaltung : D:\_Elektronik\Eigenprojekte\_DEVBOARDs\MCU-Butler\V2.0
--------------------------------------------------------------------------------------
Prozessor : ATmega328P 
Datum     : 05.06.2024
Version   : 1.0
Autor     : c 2024 by Peter Lampe

HOW TO Einbinden in Visual Studio Code mit PlatformIO:
1. VScode starten
2. PIO-Home öffnen
3. QUICK ACCESS-->OPEN PROJEKT betätigen und Verzeichnis "MCU-Butler_2.x" öffnen

als Upload-Tool ist USBasp in platform.ini eingebunden
falls serielle Kommunikation benötigt wird, den COM-Port ebenfalls in 
platform.ini anpassen 
*/
//--------------------------- fundamentale Systemeinstellungen------------------------

#include <Arduino.h>
#include <U8g2lib.h>            //liegt in: .pio\libdeps\nanoatmega328\U8g2
                                //Hex-File liegt unter .pio\build\nanoatmega328\src

//--------------------------- Deklaratinen / Definitionen ----------------------------
#define Off 0                   //Statussignal
#define On 1                    //Statussignal
#define ONswitch 6              //Input/Pullup: roter Taster EIN
#define OFFswitch 7             //Input/Pullup: blauer Taster AUS
#define Power 3                 //PW-Schaltausgang H-aktiv:
#define Uin 0                   //Analogmesseingang Eingangsspannung Netzteil
#define U3P3 1                  //Analogmesseingang 3P3
#define U5P 2                   //Analogmesseingang 5P
#define UPW 3                   //Analogmesseingang Sytemspannung

uint8_t Status = Off;           //Einschaltstatus
float Voltage[4];               //Spannung an allen 4 Messpunkten
uint16_t Dividor[4];            //Wertanpassung

//---------------------------------- Prototypes ---------------------------------------
void getOFFscreen();
void getONscreen();
uint16_t ADC_read (uint8_t channel, uint8_t samples);
void ADC_init (uint8_t Reference, uint8_t format, uint8_t prescale);

//Einstellung Grafik library für SSD1306 128x64
U8G2_SSD1306_128X64_NONAME_1_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);

//------------------------------- Initialisierung -------------------------------------
void setup(void) 
{
  pinMode(Power, OUTPUT);                       //Ausgang H-aktiv: Einschalten PW
  pinMode(ONswitch, INPUT_PULLUP);              //Input/Pullup: roter Taster EIN
  pinMode(OFFswitch, INPUT_PULLUP);             //Input/Pullup: blauer Taster AUS
                                                //bei jedem Aufbau neu anpassen!
  Dividor[0]=118;                               //Zuordnung der Korrekturteiler zu den Spannungswerten
  Dividor[1]=507;                               //muss experimentell, messtechnisch ermittelt werden
  Dividor[2]=282;                               //Dividor wurde mit 2 Multipliziert!
  Dividor[3]=282;                               //siehe dazu Berechnung von Voltage8[x]
   
  u8g2.begin();                                 //Initialisierung Grafik library
  ADC_init (2, 0, 6);									          //Referenz int. 1,1V; Ergebnis rechtsbündig; Vorteiler 64
  getOFFscreen();                               //OFF-Bildschirm als Startbildschirm anzeigen
  
  //Serial.begin(115200);                       //Initialisierung UART
  /* Debug-Meldungen                           
  Serial.println("***********************");    //serielle Startmeldung
  Serial.println("* System Start Up OK! *");    //serielle Startmeldung
  Serial.println("***********************\n");  //serielle Startmeldung

  getOFFscreen();                               //Startbildschirm im OFF-Zustand anzeigen
  Serial.println("Startbildschirm (OFF) System inaktiv");
*/
  
}

//----------------------------------- Main --------------------------------------------
void loop(void) 
{
 uint8_t i;                                     //allgemeine Laufvariable

 if (!(digitalRead(ONswitch)))                  //ON-Taster gedrückt
  {
    digitalWrite(Power, On);                    //ja, dann Board einschalten
    Status=On;                                  //Systemstatus auf "EIN" setzen
    //Serial.println("Betriebsbildschirm (ON) System aktiv");
  }
 
 if (!(digitalRead(OFFswitch)))                 //OFF-Taster gedrückt?
  {
    digitalWrite(Power, Off);                   //ja, dann Boardspannungen ausschalten
    Status=Off;                                 //Systemstatus auf "AUS" setzen
    getOFFscreen();                             //und OFF-Bildschirm anzeigen
    //Serial.println("Startbildschirm (OFF) aktiv");
  }
                                                //Abfrage des Sysemstatus
  if (Status==On)                               //wenn eingeschaltet, 
    {                                           
    for ( i = 0; i < 4; i++)                    //dann Messungen an allen 4 Messtellen 
      {
        Voltage[i]=(float(ADC_read (i,8))/Dividor[i]*2);  //mit 8-fach Oversampling durchführen
                                                          //umrechnen und in Array speichern
                                                          //Achtung, um die Anzeigegenauigkeit zu verdoppeln,
                                                          //ohne mit Kommazahlen rechnen zu müssen, 
                                                          //wurde der Dividor mit 2 multipliziert. 
                                                          //Bei der Ermittlung des Spannungswertes hier,
                                                          //wird das wieder ausgeglichen. (Multiplikation mi 2)!
        /*
        Serial.print("U");
        Serial.print(i);
        Serial.print(": ");
        Serial.print(Voltage[i]);
        Serial.println(" V");     */
      }
      //Serial.println(" ");
       
   getONscreen();                               //ON-Bildschirm mit Datenausgabe aufrufen
  }  
} 

 //--------------------------------- Functions -----------------------------------------
void getOFFscreen()                             //OFF-Bildschirmaufbauen
{
  u8g2.clearDisplay();                          //Dislay von vorangegangener Anzeige säubern
  u8g2.firstPage();                             //Seite fr Aufnahme der Bildelemente vorbereiten
  do 
  {
    u8g2.drawFrame(0,0,127,63);
    u8g2.drawHLine(0,31,127);
    u8g2.setFont(u8g2_font_luRS14_tr);          //Definition großer Font (siehe u8g2.h)
    u8g2.drawStr(10,23,"MCU-Butler");           //für Headerausgabe
    u8g2.setFont(u8g2_font_luRS10_te);          //definition kleiner Fonts (siehe u8g2.h)
   // u8g2.setCursor(5, 45);                    //(nur) Text-Curser neu positionieren (Zahlen)
    u8g2.drawStr(30,45,"c 2024 by");            //Coppyright ausgeben
    u8g2.drawStr(22,58,"Peter Lampe");
  } while ( u8g2.nextPage() );                  //warten bis alles im Display ist
  return; 
}


void getONscreen()                              //ON-Bildschirm mit Messwertausgabe
{
  u8g2.firstPage();                             //Seite für Aufnahme der Bildelemente vorbereiten
  do 
  {
    u8g2.drawFrame(8,0,117,25);                 //Rahmen für PW-Anzeige
    u8g2.setFont(u8g2_font_luRS14_tr);          //Definition großer Font (siehe u8g2.h)
    u8g2.drawStr(12,20,"PW:          V");       //Rahmeninformationen für PW setzen
    u8g2.setCursor(57, 20);                     //Curser für Wert setzen
    u8g2.print(Voltage[3]);                     //Wert von PW ausgeben
  
    u8g2.setFont(u8g2_font_luRS10_te);          //Definition kleiner Font (siehe u8g2.h)
    u8g2.drawStr(25,38,"Uin :           V");    //Rahmeninformationen für Uin setzen
    u8g2.setCursor(59, 38);                     //Curser für Wert setzen
    u8g2.print(Voltage[0]);                     //Wert von Uin ausgeben

    u8g2.drawStr(22,51,"3P3 :           V");    //Rahmeninformationen für 3P3 setzen
    u8g2.setCursor(59, 51);                     //Curser für Wert setzen
    u8g2.print(Voltage[1]);                     //Wert von 3P3 ausgeben

    u8g2.drawStr(27,64," 5P :           V");    //Rahmeninformationen für 5P setzen
    u8g2.setCursor(59, 64);                     //Curser für Wert setzen
    u8g2.print(Voltage[2]);                     //Wert von 5P ausgeben
  } while ( u8g2.nextPage() );                  //Display aktualisieren
  return; 
}

uint16_t ADC_read (uint8_t channel, uint8_t samples)  //ermittelt Durchschnittsmesswert an Kanal
{
  uint8_t i;                                          //Laufvariable
  uint32_t Messwert=0;                                //Messwert

  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);       //unabhängige Muxer-Kanaleinstellung!						                  
  
  for (i = 0; i < samples; i++)
  {
    ADCSRA|=(1<<ADSC);							                  //Umsetzung starten
    while(ADCSRA & (1<<ADSC));					              //warten bis Messung fertig ist
    Messwert+=ADCW;								                    //und Messwert aufaddieren
  }
  return Messwert/samples;                            //Wert zurückgeben
}

void ADC_init (uint8_t Reference, uint8_t format, uint8_t prescale) //Initialisierung ADC
{
  if (prescale>7)										//Wertebegrenzung
	  prescale=0;										  //Standardwert setzen, wenn Wertebereich überschritten
	
  ADCSRA=prescale;									//Vorteiler setzen
  ADMUX=0;											    //Register in Ursprungszustand versetzen

  if (format)											  //Formatangabe >0
	  ADMUX|=(1<<ADLAR);							//Ergebnis linksverschoben formatieren
  else
	  ADMUX&= ~(1<<ADLAR);
	
  if (Reference>3)									//Wertebegrenzung
	  Reference=3;									  //Standardwert setzen wenn Wertebereich überschritten
  ADMUX|=(Reference<<6);						//und platzieren

  ADCSRA|=(1<<ADEN);								//ADC anschalten
  ADCSRA|=(1<<ADSC);								//und ersten Warmlaufen starten
  while(ADCSRA & (1<<ADSC));				//Umsetzung abwarten
}