Messbox für Spannung und Strom

Guten Abend Leute,

ich habe vor kurzem durch die Uni angefangen, mit einem Arduino Nano zu arbeiten und habe leider noch nicht so die Erfahrungen was möglich ist und was nicht.
Ich möchte mit dem Arduino ein Messgerät betreiben, welches Strom und Spannung messen kann.
Vor mir hat bereits jemand an dieser Aufgabe gearbeitet und ich habe sein Sketch bekommen.
Soweit mir bekannt, wandelt der ADC mit der internen Vcc, mein Vorgänger hat diese für die Funktionen verwendet, um die Genauigkeit der Werte zu verbessern. Es gibt jedoch leider einige Teile, die sich mir nicht erschließen.

const int analogInPinU1 = A0; // Analog input pin
const int analogInPinU2 = A1; // Analog input pin
const int analogInPinU3 = A2; // Analog input pin
const int analogInPinU4 = A3; // Analog input pin
const int analogInPinI1 = A7; // Analog input pin
const int analogInPinI2 = A6; // Analog input pin
const int analogInPinI3 = A5; // Analog input pin
const int analogInPinI4 = A4; // Analog input pin



void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(9600); }


long readInternalVcc() {

  long result;
  ADMUX   = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); 
  delay(2);                                                    // Warte auf Vref 
  ADCSRA |= _BV(ADSC);                                         // Konvertiere
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
  result = 1126400L / result;                                  // AVcc in MV
  return result;
} 

 double current(int raw) {
  
 int  sensitivity = 40; 
 
 long   internalVCC     = readInternalVcc();  //Interne Spannung = ext. Spannung
 double ZeroCurrentVCC  = internalVCC *0.001/ 2; //Sensor 0 A definiert bei halber Versorgungsspannung
 double sensedVoltage   = (raw* internalVCC*0.001) / 1024; //Interne Spg. auf Volt umrechnen und Digitalwert mit dem Verhältnis multiplizieren
 double difference      = sensedVoltage - ZeroCurrentVCC; // Differenz zwischen Eingangswert und Nullpunkt
 double sensedCurrent   = difference * sensitivity; // Ausgabe der Eingangsspannung in A

 return sensedCurrent;
 }
 double voltage(int raw2) {
  
 int  sensitivity2 = 219.8039216; 
 
 long   internalVCC     = readInternalVcc();  //Interne Spannung = ext. Spannung
 double ZeroVoltageVCC  = internalVCC *0.001 / 2 ; //Sensor 0 V definiert bei halber Versorgungsspannung
 double sensedVoltage   = (raw2* internalVCC*0.001) / 1024; //Interne Spg. auf Volt umrechnen und Digitalwert mit dem Verhältnis multiplizieren
 double difference      = sensedVoltage - ZeroVoltageVCC; // Differenz zwischen Eingangswert und Nullpunkt
 double Voltage   = difference * sensitivity2; // Ausgabe der Eingangsspannung in A

 return Voltage;
 }


void loop() {


 // Gib den Wert mittels Serial Monitor aus
  
  
  
  
  Serial.print("InternalVcc=  ");Serial.print(readInternalVcc());Serial.println("mV");
  Serial.print("U1=  " );Serial.print(voltage(analogRead(analogInPinU1))); Serial.print("V");Serial.println(analogRead(analogInPinU1));// Ausgabe des Eingangswertes (durch einlesen des Wertes und Abarbeitung des Programms "voltage")
   Serial.print("U2=  " );Serial.print(voltage(analogRead(analogInPinU2))); Serial.print("V");Serial.println(analogRead(analogInPinU2));// Ausgabe des Eingangswertes (durch einlesen des Wertes und Abarbeitung des Programms "voltage")
    Serial.print("U3=  " );Serial.print(voltage(analogRead(analogInPinU3))); Serial.print("V");Serial.println(analogRead(analogInPinU3));// Ausgabe des Eingangswertes (durch einlesen des Wertes und Abarbeitung des Programms "voltage")
     Serial.print("U4=  " );Serial.print(voltage(analogRead(analogInPinU4))); Serial.print("V");Serial.println(analogRead(analogInPinU4));// Ausgabe des Eingangswertes (durch einlesen des Wertes und Abarbeitung des Programms "voltage")
      Serial.print("I1=  " );Serial.print(current(analogRead(analogInPinI1))); Serial.print("A");Serial.println(analogRead(analogInPinI1));// Ausgabe des Eingangswertes (durch einlesen des Wertes und Abarbeitung des Programms "current")
       Serial.print("I2=  " );Serial.print(current(analogRead(analogInPinI2))); Serial.print("A");Serial.println(analogRead(analogInPinI2));// Ausgabe des Eingangswertes (durch einlesen des Wertes und Abarbeitung des Programms "current")
        Serial.print("I3=  " );Serial.print(current(analogRead(analogInPinI3))); Serial.print("A");Serial.println(analogRead(analogInPinI3));// Ausgabe des Eingangswertes (durch einlesen des Wertes und Abarbeitung des Programms "current")
         Serial.print("I4=  " );Serial.print(current(analogRead(analogInPinI4))); Serial.print("A");Serial.println(analogRead(analogInPinI4));// Ausgabe des Eingangswertes (durch einlesen des Wertes und Abarbeitung des Programms "current")
         
 // 
  delay(50); }

Alle 8 analogen Eingänge werden für Messungen verwendet.
Ich habe dazu jetzt ein paar Fragen, da ich noch nicht relativ bewandert mit der Materie der A/D-Wandlung bin.
Der ADC meines µC bietet eine 10 Bit Auflösung. Ist es möglich für einzelne Eingänge zum Programmstart eine variable Abtastrate festzulegen? In der Praxis soll der Anwender der Messbox vorher eingeben können, welche Eingänge mit welcher Auflösung/Abtastrate gewandelt werden sollen. Ist so etwas vom Prinzip her Möglich?

Ich habe im Internet ein paar Beiträge über eine A/D-Wandlung gelesen, welche per Interrupt ausgeführt wird. Ist dies von Vorteil gegenüber einfachen Funktionen, wie im eingefügten Sketch, die aufgerufen werden?

Im Internet habe ich jedoch auch eine, meiner Meinung nach, sehr einfache Möglichkeit gefunden, eine AD-Wandlung durchzuführen. Stellt dies auch eine Möglichkeit dar, eine AD-Wandlung durchzuführen.

#include "pins_arduino.h"

const int mess1 = 0;
const int AREF = 20;


void setup() {               
  Serial.begin(9600);
}

void loop() {
  int mess = analogRead(mess1);
  float Spannung = map(mess, 0, 1023, 0 , 500)/100.00;
  Serial.println(analogRead(AREF));
  Serial.println(mess);
  Serial.print(Spannung);Serial.println(" V");
  //Serial.println(Prozent);
  Serial.println("----");
  delay(1000);
}

Ich hoffe ich wirke nicht ganz planlos.

Vielen Dank für eure Hilfe.

In der Praxis soll der Anwender der Messbox vorher eingeben können, welche Eingänge mit welcher Auflösung/Abtastrate gewandelt werden sollen. Ist so etwas vom Prinzip her Möglich?

Theoretisch ja. Denke aber daran dass du nur einen ADC hast und die Analog-Eingänge die Eingänge eines Multiplexer sind. Es geht also nicht beliebig schnell. Aber so schnell so hier auch gar nicht gemessen werden.

Deshalb ist es auch unsinnig das mit Interrupts zu machen. Man kann z.B. den ADC über einen Timer-Interrupt triggern wenn das Abstast-Interval ganz exakt eingehalten werden muss. Oder den ADC ständig messen lässen und einen Interrupt auslösen wenn eine Messung fertig ist. Das ist aber erst interessant wenn man im µs oder unteren ms Bereich arbeitet. Davon bist du weit, weit entfernt.

Auflösung ändern bedeutet hier auch nur die nieder-wertigeren Stellen zu verwerfen. Gemessen wird mit analogRead() immer 10 Bits.

Programm-technisch sind wie hier wieder wahrscheinlich beim Array aus struct :p Ein struct anlegen, das den Pin, das Intervall, die letzte millis() Zeit und die Auflösung enthält. Dann ständig darüber iterieren und eine Messung machen wenn die Zeit abgelaufen ist.

Was das Messen der Versorgungsspannung/Referenzspannung betrifft: Wenn deren Genauigkeit ein Problem ist sollte man sich vielleicht besser eine bessere externe Referenz zulegen und die an AREF anschließen.

Hey vielen Dank für deine Antwort hat mir auf jeden Fall weitergeholfen.

Ich bin im Internet mittlerweile auf die Funktion gestoßen per ISR eine A/D-Wandlung vorzunehmen. Zeitlich braucht eine Wandlung dafür nur noch 4us. Davor hatte ich den Prescale Wert verändert und landetet bei 20us.
Jetzt habe ich allerdings das Problem, ich möchte mehrere Eingänge nacheinander gewandelt bekommen. Jedoch bekomme ich, mit meinem Code, immer nur Eingang A1 ausgegeben für U1 und U2. Eigentlich müssten sich die Werte unterscheiden.

Später sollen die anderen Eingänge dazukommen und der Benutzer soll auswählen können welche verwendet werden. Deshalb habe ich in ISR die if-Abfrage eingebaut um am Ende dann unterscheiden zu können, welche Eingänge alles ausgewählt wurden.

Was muss ich ändern um in ISR(ADC_vect) mehrere Eingänge nacheinander wandeln zu lassen um anschließend eine Ausgabe zu bekommen?

//Volatile damit Variablen Compiler weiß, dass Inhalt sich auch außerhalb ändern kann. Wird gebraucht wegen Interrupt, der nicht zum normalen Programmfluß gehört.
volatile int analogVal;
volatile int analogVal1;
volatile int passed_time;
volatile int start_times;
volatile int stop_times;
volatile int passed_time1;
volatile int start_times1;
volatile int stop_times1;
volatile int statusU1;
volatile int statusU2;


void setup() {
  Serial.begin(9600);
  ADMUX = 0xC0; //Binär 1100 0000 Internal 1.1V Voltage Reference with external capacitor at AREF pin. AD-Pin 0 wird als Eingang gewählt. ADLAR = 0 ==> unteren 8 Bits werden in ADCL geschrieben die oberen 2 in ADCH

  ADCSRA |= 0x8F; //B 1000 1111

  sei(); //Aktiviert Interrupts

  statusU1 = 1;
  statusU2 = 1;

  ADCSRA |= 0x40;//B01000000; //ADSC muss gesetzt werden, um die Wandlung zu starten
}

void loop() {

}

void Ausgabe() {

  float Spannung = map(analogVal, 0, 1023, 0, 500) / 100.00;
  Serial.println("U1");
  Serial.print(passed_time);
  Serial.print(" us  ");
  Serial.print(Spannung);
  Serial.print(" V  ");
  Serial.println(analogVal);
  float Spannung1 = map(analogVal1, 0, 1023, 0, 500) / 100.00;
  Serial.println("U2");
  Serial.print(passed_time1);
  Serial.print(" us  ");
  Serial.print(Spannung1);
  Serial.print(" V  ");
  Serial.println(analogVal1);
  ADCSRA |= 0x40;
}


ISR(ADC_vect) { 
  if (statusU1 == 1) { //Hier soll A0 gewandelt und das Ergebnis in analogVal gespeichert werden
    Serial.println("1");
    ADMUX |= 0x00;
    start_times = micros();
    analogVal = ADCL | (ADCH << 8);
    stop_times = micros();
    passed_time = stop_times - start_times;
  }

  if (statusU2 == 1) { //Hier soll A1 gewandelt und das Ergebnis in analogVal1 gespeichert werden
    Serial.println("2");
    ADMUX |= 0x01;
    start_times1 = micros();
    analogVal1 = ADCL | (ADCH << 8);
    stop_times1 = micros();
    passed_time1 = stop_times1 - start_times1;
  }

  Ausgabe(); 

}

Serial hat in ISRs nichts verloren!!

Den Sinn der ADC ISR verstehst du wohl auch generell nicht so recht. Die wird erst ausgelöst wenn die Messung fertig ist. Man muss als erst im ADMUX Register den entsprechenden Eingang auswählen, dann die Messung starten und dann in der ISR nur das Ergebnis auslesen und eventuell an loop() melden dass man fertig ist. Oder man verwendet den Free-Running Mode in dem am Ende jeder Messung automatisch eine neue ausgelöst wird (dazu muss man das ADATE Bit aktivieren). Aber auch da ist man nur der in der ISR wenn eine Wandlung abgeschlossen wurde.

Wenn du in loop() sowieso nichts machst, kannst du auch genauso gut blockierend messen. Da du anscheinend nicht richtig mit ISRs umgehen kannst, solltest du das vielleicht erst mal sein lassen. Schau dir statt dessen mal das ADSC Bit im ADSCRA Register genauer an. Wenn du das setzt wird eine Messung gestartet. Dann ist es 1 solange die Messung aktiv ist und wird 0 wenn die Messung fertig ist. Du kannst also einfach das Bit abfragen um festzustellen wann du fertig bist.

Also: 1.) MUX Bits im ADMUX Register entsprechend setzen 2.) Messung starten 3.) Bit abfragen 4.) Ergebnis auslesen

Probier erst mal das. Und wenn es nur darum geht, dass du den ADC besser verstehst.

Nochwas: Bei deinen sehr langen Serial-Ausgaben musst du beachten dass bei 9600 Baud ein Zeichen 1ms dauert! Deine Serial Übertragung ist also sehr, sehr viel langsamer als deine Messung. Die Übertragung ist zwar erst mal nicht-blockierend, aber nur solange Platz im Ausgangspuffer ist.

Hey vielen Dank für deine schnelle Antwort.

die Serial Ausgaben habe ich erstmal nur integriert um zu schauen, wo mein Programm zur Zeit steckt. Habe leider noch nicht so das Verständnis dafür und so kann ich es mir leichter veranschaulichen.

Ich glaube zu verstehen was du in deinem 1. Abschnitt meinst und werde es morgen Abend versuchen umzusetzen.
Ich stelle es mir jetzt mal so vor: Ich wähle zu Beginn des Programms aus welche Eingänge ausgelesen werden sollen. Dies geschiet über Serial.read und dadurch werden dann die entsprechenden MUX Bits im ADMUX Register gesetzt oder eben nicht. Anschließend starten die Wandlungen. Nach jeder Wandlung wird der ISR ausgelöst und übergibt das Ergebnis an eine entsprechende Variable. Dies geschieht in einer Art Schleife so das alle Eingänge nacheinander gewandelt werden.
Ist das vom Prinzip her richtig?

Pausf: die Serial Ausgaben habe ich erstmal nur integriert um zu schauen, wo mein Programm zur Zeit steckt.

Was du da willst ist mir schon klar. Es geht aber einfach nicht richtig und stört außerdem den Programm Ablauf. Wenn überhaupt dann darfst du in der ISR lediglich ein Flag setzen auf das du in loop() abfragst. Und dann die Serial Ausgabe in loop() machen. Aber auch da hast du das Problem dass schon mehrere Messungen fertig sind bevor der erste Text überhaupt zu Ende gesendet wurde. Also die Baudrate auf Maximum setzen und die Serial-Ausgaben minimal halten. Und nicht in der ISR machen!

Aber wieso versteigerst du dich so auf die ISR? Das geht auch ohne Interrupts! Du machst doch in loop() sowieso nichts. Wieso also nicht einfach auf das Ende der Messung warten? Das ist wesentlich einfacher, vor allem da dir etwas der Überblick fehlt wie die verschiedenen ADC Modi funktionieren.

Die ISR ist dafür da, dass die Wandlung den Prozessor nicht blockiert und man während der Wandlung andere Dinge tun kann. Eine Wandlung wird dann über eine der Trigger-Quellen im ADCSRB Register gestartet. Also z.B. Timer, externe Interrupts oder auch der Free-Running Modus. Aber auch nur wenn Auto-Trigger aktiviert ist! Das brauchst du aber nicht!! Oder zumindest noch nicht.

Wenn du das mal eine einfacher Version kapiert hast kann man es immer noch anders machen. Aber du fängst da mit der kompliziertesten Variante an und verstehst nicht was du machen musst. Dir ist weder die übergreifende Logik, noch die Implementierung im Detail klar. Da solltest du wirklich ein, zwei Stufen tiefer ansetzen. Also jede Wandlung per Hand anfordern und auf das Ende der Wandlung warten. Das kann man dann schön der Reihe nach abarbeiten ohne dass man sich mit Interrupts herumärgern muss.

In deinem aller-ersten Sketch hattest du das schon mal:

while (bit_is_set(ADCSRA,ADSC));

Das wartet auf das Ende der Konvertierung

Hier ist auch noch was das falsch ist:

ADMUX |= 0x01;

Du kannst nicht einfach ODER mit den bestehenden Bits machen. Du musst erst mal untersten 4 Bits löschen und dann auf den neuen Wert setzen:

ADMUX = ADMUX & 0xF0 | muxbits;

Durch deine Hilfe bin ich ein gutes Stück weiter gekommen, DANKE.
Ich kann jetzt die verschiedenen Eingänge auswählen und wandeln lassen.

Zum Thema wieso ISR:
Nachdem ich mit einem meiner ersten Skatches die Möglichkeit hatte den Prescale Wert zu verändern und die Eingänge nacheinander zu wandeln (analogRead), kam mein Professor auf die Idee es nochmal mit ISR umzusetzen.

Als nächsten Schritt soll ich die gewandelten Werte in ein Array speichern und addieren. Durch die Summe soll dann eine “gemittelte” Spannungsangabe entstehen.
Im Angehängten Skatch habe ich mir erstmal 2 Arrays erstellt, für U1 und U2. Wenn ich nur eine Spannung auswähle funktioniert alles super, wenn ich jedoch 2 Spannungen immer im wechsel gewandelt haben möchte, passen die Ergebnisse leider nicht mehr.

Würdest du die Umrechnung mit dem map-Befehl machen oder gibt es da noch andere eventuell genauere Möglichkeiten?

//Volatile damit Variablen Compiler weiß, dass Inhalt sich auch außerhalb ändern kann. Wird gebraucht wegen Interrupt, der nicht zum normalen Programmfluß gehört.
volatile int analogVal;
volatile int statusU1;
volatile int statusU2;
volatile int statusU3;
volatile int statusU4;
char serialEing;
unsigned long U1[64];
unsigned long U2[64];



void setup() {
  Serial.begin(9600);
  pinMode(1, INPUT); // Ist das wichtig? Pins haben auch so funtkioniert, hatte ich aber in einen meiner ersten Sketchs drinne. 
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  
  ADMUX = 0xC0; //Binär 1100 0000 Internal 1.1V Voltage Reference with external capacitor at AREF pin. AD-Pin 0 wird als Eingang gewählt. ADLAR = 0 ==> unteren 8 Bits werden in ADCL geschrieben die oberen 2 in ADCH

  ADCSRA |= 0x8F; //B 1000 1111

  sei(); //Aktiviert Interrupts

  statusU1 = 0;
  statusU2 = 0;
  statusU3 = 0;
  statusU4 = 0;
}

void loop() {
  Serial.println("Welche Eingaenge werden verwendet?");
  Serial.println("Eingaenge 1-4");
  Serial.println("Mit 9 bestaetigen und Wandlung beginnen");
  lab:
  serialEing = Serial.read();
  if( serialEing == '1' ) {
    statusU1 = 1;
    Serial.println("U1 ausgewaehlt");
  }
  if( serialEing == '2' ) {
    statusU2 = 1;
    Serial.println("U2 ausgewaehlt");
  }
  if( serialEing == '3' ) {
    statusU3 = 1; 
    Serial.println("U3 ausgewaehlt");
  }
  if( serialEing == '4' ) {
    statusU4 = 1; 
    Serial.println("U4 ausgewaehlt");
  }
  if( serialEing == '9' ) {
    Serial.println("Eingabe abgeschlossen");
    Ausgabe();
  }
  else goto lab; 
  
}

void Ausgabe(){

  serialEing = Serial.read();
  if( serialEing == '0'){
    statusU1 = 0; 
    statusU2 = 0;
    statusU3 = 0;
    statusU4 = 0;
    loop();
  }
  if (statusU1 == 1) {
      unsigned int i;
  long summe = 0;
    ADMUX  = ADMUX & 0xF0; 
    ADMUX |= 0x00;
    ADCSRA |= 0x40;
    for(i=0;i<64;i++) {
      U1[i]=analogVal;
      summe+=U1[i];
      if (i == 63) {
        float SpgU1 = map(summe, 0, 65472, 0 , 500)/100.00;
        Serial.print(SpgU1);
        Serial.println(" V U1");
      }
    }
    
  }
  if (statusU2 == 1) {
    unsigned int j;
    long summe1 = 0;
    ADMUX  = ADMUX & 0xF0;
    ADMUX |= 0x01; 
    ADCSRA |= 0x40; 
    for(j=0;j<64;j++) {
      U2[j]=analogVal;
      summe1+=U2[j];
      if (j == 63) {
        float SpgU2 = map(summe1, 0, 65472, 0 , 500)/100.00;
        Serial.print(SpgU2);
        Serial.println(" V U2 ");
      }
    }
  }
  if (statusU3 == 1) {
    ADMUX  = ADMUX & 0xF0;
    ADMUX |= 0x02; 
    ADCSRA |= 0x40; 
    Serial.print("U3 ");
    Serial.println(analogVal);
  }
  if (statusU4 == 1) {
    ADMUX  = ADMUX & 0xF0; 
    ADMUX |= 0x03;
    ADCSRA |= 0x40;  
    Serial.print("U4 ");
    Serial.println(analogVal);
  }
  Ausgabe();
}

ISR(ADC_vect) {
  analogVal = ADCL | (ADCH << 8);
}

Nach einer Kanalumschaltung braucht die Elektronik etwas Zeit, bis die Spannung sauber am ADC ankommt. Das ist zwar schon in Hardware realisiert, die Zeit reicht aber meistens nicht aus (hängt vom Innenwiderstand der Signalquelle ab).

Ohne Interrupts mißt man deshalb meist einen Kanal mehrmals hintereinander, und wirft die erste Messung weg. Mit Interrupts wird das ziemlich kompliziert, Du brauchst dann noch einen Timer-Interrupt für die Wartezeit nach der Kanalumschaltung, der dann erst die Wandlung auslöst.

Wieso rufst du loop() per Hand auf? Lass das mal ganz schnell sein! Du müllst dir da nur den Stack zu, weil die Funktionen nicht richtig beendet werden. Wenn du wieder nach loop() zurück willst, kannst du return machen

Das goto ist überflüssig. Deine einmaligen Serial Ausgaben kommen nach setup()

Das mit den Interrupts hast du immer noch nicht richtig verstanden. Du willst du die Messung per Hand starten und das Ergebnis per Interrupt auswerten. Dann kannst du auch nicht gleich das Ergebnis abfragen. Die Wandlung braucht Zeit! Aber der Interrupt ist schlicht überflüssig hier. Egal was dein Professor sagt. Du machst während der Wandlungszeit doch sowieso nichts.

Wenn dann müsstest du ihn der ISR ein Flag setzen dass angibt dass eine Messung fertig ist. Und dann erst das Ergebnis auslesen wenn das Flag gesetzt wurde.

Du solltest dir auch auch mal angewöhnen die Bit-Bezeichnungen zu verwenden. Statt dem:

ADCSRA |= 0x8F;

So:

ADCSRA = _BV(ADEN) | _BV(ADIE) |_BV(ADPS2) |_BV(ADPS1) |_BV(ADPS0);

Oder statt:

ADCSRA |= 0x40;

Das:

ADCSRA |= _BV(ADSC);

Da sieht man sofort welche Bits gesetzt sind. Wesentlich übersichtlicher

Beachte auch dass ich bei der Initialisierung von ADCSRA nicht das Compound Oder verwendet habe. Die Register sind oft schon durch die Arduino IDE beschrieben. Man sollte sie daher am Anfang sicherheitshalber völlig neu schreiben. Hier ging es wohl, aber mit Timern z.B. macht man da schnell Fehler.

Ohne Interrupts mißt man deshalb meist einen Kanal mehrmals hintereinander, und wirft die erste Messung weg.

Man kann auch nach der Kanal-Umschaltung etwas warten.

Aber hier wird das Problem sein, dass der gar nicht mal auf das Ende der Messung wartet. Sondern sofort das Ergebnis ausliest.

Serenifly: Du solltest dir auch auch mal angewöhnen die Bit-Bezeichnungen zu verwenden. Statt dem:

ADCSRA |= 0x8F;

So:

ADCSRA = _BV(ADEN) | _BV(ADIE) |_BV(ADPS2) |_BV(ADPS1) |_BV(ADPS0);

Jezt nur noch = durch |= ersetzen, oder dazuschreiben daß die anderen Bits tatsächlich auf 0 gesetzt werden sollen. Bitfummeleien sehen immer irgendwie unhandlich aus, egal wie man's macht.

Das _BV erschließt sich mir nicht ganz - was macht das?

Das _BV erschließt sich mir nicht ganz - was macht das?

nicht viel :wink:

#define _BV(bit) (1 << (bit))

Spart 0 bis 2 Zeichen zu tippen, und macht das Ganze leserlicher, wenn man’s gewohnt ist.
Und hilft, den beliebten Fehler (0 << bit) zu vermeiden :wink:

Macht aus einer konstanten Bitnummer eine konstante Maske, zur Kompilierzeit.
Kann aber auch mit Variablen verwendet werden.

Ich habe jetzt ein wenig rumprobiert und gemerkt, wenn ich in die for-Schleife delay(1) reingeschreibe, dass meine Werte stimmen (Multichannel ausgewählt).
Das Problem mit der 1. Messung habe ich gelöst, indem ich den 1. Array Eintrag von der Summe abgezogen habe und beim mappen den Maximalwert ebenfalls angepasst habe.

Serenifly wie würdest du denn vorgehen? Mit analogRead? oder gibt es noch eine andere Möglichkeit?

if (statusU1 == 1) {
      unsigned int i;
      long summe = 0;
      ADMUX  = ADMUX & 0xF0;
      ADMUX |= 0x00;
      ADCSRA |= 0x40;           
      for (i = 0; i < 64; i++) {
        U1[i] = analogVal;
        delay(1); 
        summe += U1[i];
        if (i == 63) {
          summe = summe - U1[0];
          float SpgU1 = map(summe, 0, 64449, 0 , 500) / 100.00; 
          Serial.print(SpgU1);
          Serial.println(" V U1");
        }
      }
    }

DrDiettrich: Jezt nur noch = durch |= ersetzen

Eben nicht an dieser Stelle! Das = bewirkt dass die Default Einstellungen der Arduino IDE überschrieben werden. Das ist die Initialisierung des Registers am Anfang.

Hier vielleicht nicht unbedingt nötig, aber ganz, ganz wichtig wenn man was mit Timern macht, da die durch die PWM Geschichte vorkonfiguriert sind.

Bei den anderem Sachen danach natürlich |= da man nach der Initialisierung die anderen Bits beibehalten will.

Serenifly wie würdest du denn vorgehen? Mit analogRead? oder gibt es noch eine andere Möglichkeit?

Entweder analogRead() und nur den Prescaler anpassen wenn du unbedingt die Geschwindigkeit brauchst. Das wird für dich erst mal das beste sein, da du zwar die Register kennst, dir aber anscheinend der Überblick fehlt was der ADC genau macht. Siehe hier: http://www.microsmart.co.za/technical/2014/03/01/advanced-arduino-adc/

Oder wie ich schon in paar mal gesagt habe, die Messung starten und über das ADSC Bit abfragen wann du fertig bist (d.h in einer while-Schleife auf das Ende der Messung warten!). Wenn du Register direkt programmieren willst, schreib dir halt man eine Funktion der du die Mux Bits übergibst, den Kanal blockierend ausliest und den Analog-Wert zurückgibt.

Das steht doch so klar im Datenblatt:

ADSC will read as one as long as a conversion is in progress. When the conversion is complete, it returns to zero. Writing zero to this bit has no effect.

Entweder analogRead() und nur den Prescaler anpassen wenn du unbedingt die Geschwindigkeit brauchst. Das wird für dich erst mal das beste sein, da du zwar die Register kennst, dir aber anscheinend der Überblick fehlt was der ADC genau macht. Siehe hier:
http://www.microsmart.co.za/technical/2014/03/01/advanced-arduino-adc/

Ein Skatch anhand dieser Vorlage hatte ich bereits erstellt gehabt und hat auch wunderbar funktioniert.

Oder wie ich schon in paar mal gesagt habe, die Messung starten und über das ADSC Bit abfragen wann du fertig bist (d.h in einer while-Schleife auf das Ende der Messung warten!). Wenn du Register direkt programmieren willst, schreib dir halt man eine Funktion der du die Mux Bits übergibst, den Kanal blockierend ausliest und den Analog-Wert zurückgibt.

Habe eine while-Schleife wie du gesagt hast eingefügt, die das Bit abfragt.

    if (statusU1 == 1) {
    unsigned int i;
    summeU1 = 0;
    ADMUX  = ADMUX & 0xF0;
    ADMUX |= 0x00;
    ADCSRA |= 0x40;
    while (ADCSRA & (1 >> ADSC)) {
    }
    delay(1);
    for (i = 0; i < 32; i++) {
      U1[i] = analogVal;
      summeU1 += U1[i];
      if (i == 31) {
        summeU1 = summeU1 - U1[0];
        float SpgU1 = map(summeU1, 0, 31713, 0 , 500) / 100.00; 
        Serial.print(SpgU1);
        Serial.println(" V U1");
      }
    }
  }

Wenn ich das delay(1) weglasse, vertauschen sich die Ergebnisse komischerweise. Dann bekomme ich als U2 das Ergebnis von U1 ausgegeben und umgekehrt. Woran kann das liegen?

Hier nochmal der komplette Skatch

//Volatile damit Variablen Compiler weiß, dass Inhalt sich auch außerhalb ändern kann. Wird gebraucht wegen Interrupt, der nicht zum normalen Programmfluß gehört.
volatile int analogVal;
volatile int statusU1;
volatile int statusU2;
volatile int statusU3;
volatile int statusU4;
char serialEing;
unsigned long U1[32];
unsigned long U2[32];
long summeU1;
long summeU2;
long summeU3;
long summeU4;




void setup() {
  Serial.begin(9600);
  pinMode(1, INPUT); // Ist das wichtig? Pins haben auch so funtkioniert, hatte ich aber in einen meiner ersten Sketchs drinne.
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);

  ADMUX = 0xC0; //Binär 1100 0000 Internal 1.1V Voltage Reference with external capacitor at AREF pin. AD-Pin 0 wird als Eingang gewählt. ADLAR = 0 ==> unteren 8 Bits werden in ADCL geschrieben die oberen 2 in ADCH

  ADCSRA |= 0x8F; //B 1000 1111

  sei(); //Aktiviert Interrupts
  statusU1 = 0;
  statusU2 = 0;
  statusU3 = 0;
  statusU4 = 0;
  Serial.println("Welche Eingaenge werden verwendet?");
  Serial.println("Eingaenge 1-4");
  Serial.println("Mit 9 bestaetigen und Wandlung beginnen");
}

void loop() {
  serialEing = Serial.read();
  if ( serialEing == '1' ) {
    statusU1 = 1;
    Serial.println("U1 ausgewaehlt");
  }
  if ( serialEing == '2' ) {
    statusU2 = 1;
    Serial.println("U2 ausgewaehlt");
  }
  if ( serialEing == '3' ) {
    statusU3 = 1;
    Serial.println("U3 ausgewaehlt");
  }
  if ( serialEing == '4' ) {
    statusU4 = 1;
    Serial.println("U4 ausgewaehlt");
  }
  if ( serialEing == '9' ) {
    Serial.println("Eingabe abgeschlossen");
    Ausgabe();
  }
}

void Ausgabe() {

  serialEing = Serial.read();
  if ( serialEing == '0') {
    statusU1 = 0;
    statusU2 = 0;
    statusU3 = 0;
    statusU4 = 0;
    return;
  }

  if (statusU1 == 1) {
    unsigned int i;
    summeU1 = 0;
    ADMUX  = ADMUX & 0xF0;
    ADMUX |= 0x00;
    ADCSRA |= 0x40;
    while (ADCSRA & (1 >> ADSC)) {
    }
    delay(1);
    for (i = 0; i < 32; i++) {
      U1[i] = analogVal;
      summeU1 += U1[i];
      if (i == 31) {
        summeU1 = summeU1 - U1[0];
        float SpgU1 = map(summeU1, 0, 31713, 0 , 500) / 100.00; //64449 da 1. Wert abgezogen wird 31713
        Serial.print(SpgU1);
        Serial.println(" U1")
      }
    }
  }

  if (statusU2 == 1) {
    unsigned int i;
    summeU2 = 0;
    ADMUX  = ADMUX & 0xF0;
    ADMUX |= 0x01;
    ADCSRA |= 0x40;
    while (ADCSRA & (1 >> ADSC)) {//Dadurch wird gewartet bis das Bit wieder 0 ist und somit die Wandlung abgeschlossen ist. Der Wandler ist danach wieder bereit für eine neue Wandlung
    }
    delay(1);
    for (i = 0; i < 32; i++) {
      U2[i] = analogVal;
      summeU2 += U2[i];
      if (i == 31) {
        summeU2 = summeU2 - U2[0];
        float SpgU2 = map(summeU2, 0, 31713, 0 , 500) / 100.00; //65472
        Serial.println(SpgU2);
        Serial.println(" U2")
      }
    }
  }

  if (statusU3 == 1) {
    ADMUX  = ADMUX & 0xF0;
    ADMUX |= 0x02;
    ADCSRA |= 0x40;
    while (ADCSRA & (1 >> ADSC)) {
    }
    Serial.println(analogVal);
  }
  if (statusU4 == 1) {
    ADMUX  = ADMUX & 0xF0;
    ADMUX |= 0x03;
    ADCSRA |= 0x40;
    while (ADCSRA & (1 >> ADSC)) {
    }
    Serial.println(analogVal);
  }
  Ausgabe();
}

ISR(ADC_vect) {
  analogVal = ADCL | (ADCH << 8);
}

Ein Skatch anhand dieser Vorlage hatte ich bereits erstellt gehabt und hat auch wunderbar funktioniert.

Dann bleib dabei wenn es funktioniert :slight_smile: Vor allem die Messung mit Interrupts übersteigt erstens deine Kenntnisse und zweitens ist es bei dieser Anwendung schlicht unnötig.

Das ist falsch:

while (ADCSRA & (1 >> ADSC))

Korrekt:

while (ADCSRA & (1 << ADSC))

Und wieso ist da immer noch die ISR drin?!!?! Mach die mal raus und setze nicht das ADC Interrupt Enable Bit. Das Auslesen der Ergebnis Register kannst du doch direkt machen nach dem die Wandlung fertig ist.

Das mit der Mittelung passt auch nicht. Wenn du x Messungen machst, musst du auch x Messungen starten. Du kannst so nicht einmal eine Messung starten und dann x mal das Ergebnis-Register auslesen. Das ginge nur im Auto-Trigger Modus und dann bräuchtest mehr Logik um den Ablauf zu steuern.

Denke doch mal über den Ablauf nach. Du meinst du startest die Messung und dann wandelt der ADC immer. Selbst wenn er das tun würde (was bei dir nicht der Fall ist), fängst du sofort nach dem Start an 32 Werte auszulesen und zu Mitteln. Fällt dir da was auf? Du berücksichtigst nicht das jede einzelne Messung eine bestimmte Anzahl an µs braucht! Du kannst erst den nächsten Wert addieren wenn die Messung fertig ist.

Deshalb so:

for (i = 0; i < 32; i++)
{
    //Messung starten
    //auf Ende warten
    //Ergebnis auslesen und addieren
}

Und besser macht man das auch so dass man Start/Warten/Auslesen in eine eigene Funktion schreibt. Und da du das mit dem Mitteln auch mindestens zwei mal machst würde das auch in eine Funktion wandern.

Damit mit das mit den Interrupts gehen würde, müsstest du den ADC in den Auto-Trigger Modus versetzen. Dann brauchst du noch ein Flag dass das Ende der Wandlung an loop() meldet. Und dann nur addieren wenn eine Wandlung fertig ist. Dann kann es auch sein, dass wenn du den Kanal während der Messung umschaltest, dass erste Ergebnis noch vom vorherigen Kanal ist. Das ist auch wieder so ein Fall wo man die erste Messung verwerfen sollte.

Aber wie gesagt, wozu willst du das mit Interrupts machen? Interrupts braucht man beim ADC eigentlich nur wenn man während der Messung noch andere Dinge tun will. Und es ist insgesamt nicht einfach. Da du schon Probleme hast, dass ohne Interrupts zu machen kann man nur davon abraten.

Durch das Ändern von while (ADCSRA & (1 >> ADSC)) auf while (ADCSRA & (1 << ADSC)) konnte ich delay(1) weglassen.

Habe den ISR jetzt rausgenommen und das wandeln und mitteln in eine Funktion ausgelagert.

Das mit der Mittelung passt auch nicht. Wenn du x Messungen machst, musst du auch x Messungen starten. Du kannst so nicht einmal eine Messung starten und dann x mal das Ergebnis-Register auslesen. Das ginge nur im Auto-Trigger Modus und dann bräuchtest mehr Logik um den Ablauf zu steuern.

Verstehe ich nicht so ganz. Es wird doch bei jedem Durchlauf der For-Schleife eine neue Messung gestartet. Nachdem eine Messung fertig ist, wird das Ergebnis ausgelsen. Danach startet eine neue Messung. Sobald 64 Messungen abgeschlossen sind, wird das Ergebnis gemittelt und von vorne begonnen, entweder erneut bei dem Eingang oder es wird zu einem anderen ausgewählten Eingang gesprungen.

Damit mit das mit den Interrupts gehen würde, müsstest du den ADC in den Auto-Trigger Modus versetzen. Dann brauchst du noch ein Flag dass das Ende der Wandlung an loop() meldet. Und dann nur addieren wenn eine Wandlung fertig ist. Dann kann es auch sein, dass wenn du den Kanal während der Messung umschaltest, dass erste Ergebnis noch vom vorherigen Kanal ist. Das ist auch wieder so ein Fall wo man die erste Messung verwerfen sollte.

Also meiner Meinung nach hat es ja mit dem ISR funktioniert auch ohne Auto-Trigger Modus. Das 1. Messergebnis habe ich abziehen lassen. Jetzt, da ich den ISR rausgenommen habe, ist die 1. Messung bereits nicht mehr falsch (weil noch der Wert vom vorigen Kanal drin ist) und ich konnte das wieder rausnehmen.

//Volatile damit Variablen Compiler weiß, dass Inhalt sich auch außerhalb ändern kann. Wird gebraucht wegen Interrupt, der nicht zum normalen Programmfluß gehört.
volatile int analogVal;
volatile int statusU1;
volatile int statusU2;
volatile int statusU3;
volatile int statusU4;
char serialEing;
unsigned long UVal[64];
long summe;
int x;




void setup() {
  Serial.begin(9600);
  pinMode(1, INPUT); // Ist das wichtig? Pins haben auch so funtkioniert, hatte ich aber in einen meiner ersten Sketchs drinne.
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);

  ADMUX = 0xC0; //Binär 1100 0000 Internal 1.1V Voltage Reference with external capacitor at AREF pin. AD-Pin 0 wird als Eingang gewählt. ADLAR = 0 ==> unteren 8 Bits werden in ADCL geschrieben die oberen 2 in ADCH

  ADCSRA |= 0x87; //B 1000 0111

  statusU1 = 0;
  statusU2 = 0;
  statusU3 = 0;
  statusU4 = 0;
  Serial.println("Welche Eingaenge werden verwendet?");
  Serial.println("Eingaenge 1-4");
  Serial.println("Mit 9 bestaetigen und Wandlung beginnen");
}

void loop() {
  serialEing = Serial.read();
  if ( serialEing == '1' ) {
    statusU1 = 1;
    Serial.println("U1 ausgewaehlt");
  }
  if ( serialEing == '2' ) {
    statusU2 = 1;
    Serial.println("U2 ausgewaehlt");
  }
  if ( serialEing == '3' ) {
    statusU3 = 1;
    Serial.println("U3 ausgewaehlt");
  }
  if ( serialEing == '4' ) {
    statusU4 = 1;
    Serial.println("U4 ausgewaehlt");
  }
  if ( serialEing == '9' ) {
    Serial.println("Eingabe abgeschlossen");
    Ausgabe();
  }
}

void Ausgabe() {

  serialEing = Serial.read();
  if ( serialEing == '0') {
    statusU1 = 0;
    statusU2 = 0;
    statusU3 = 0;
    statusU4 = 0;
    return;
  }

  if (statusU1 == 1) {
    ADMUX  = ADMUX & 0xF0;
    ADMUX |= 0x00;
    x = 1;
    messung();
  }

  if (statusU2 == 1) {
    ADMUX  = ADMUX & 0xF0;
    ADMUX |= 0x01;
    x = 2;
    messung();
  }

  if (statusU3 == 1) {
    ADMUX  = ADMUX & 0xF0;
    ADMUX |= 0x02;
    x = 3;
    messung();
  }

  if (statusU4 == 1) {
    ADMUX  = ADMUX & 0xF0;
    ADMUX |= 0x03;
    x = 4;
    messung();

  }

  Ausgabe();
}

void messung() {
  unsigned int i;
  summe = 0;
  for (i = 0; i < 64; i++) {
    ADCSRA |= 0x40;
    while (ADCSRA & (1 << ADSC)) {
    }
    analogVal = ADCL | (ADCH << 8);
    UVal[i] = analogVal;
    summe += UVal[i];
    if (i == 63) {
      Serial.print("U"); Serial.print(x); Serial.print(": ");
      float SpgVal = map(summe, 0, 65472, 0 , 500) / 100.00; 
      Serial.println(SpgVal);
    }
  }
}

Pausf: Es wird doch bei jedem Durchlauf der For-Schleife eine neue Messung gestartet

In deinem jetzigen Code, ja. In dem Code aus #13 nicht

Also meiner Meinung nach hat es ja mit dem ISR funktioniert auch ohne Auto-Trigger Modus.

Mit delay(1) vielleicht, weil selbst standardmäßig eine Messung "nur" 150µs dauert und du dann 1ms gewartet hast. Da stellt sich dann wieder die Frage welchen Sinn der Interrupt hat.

So sieht jetzt auf den ersten Blick vom Prinzip her mal passend aus :)

Danke für eure Hilfe.

Ich habe jetzt jedoch nochmal eine Frage. Ich würde gerne die 4 Kanäle nacheinandere immer nur 1 mal wandeln und dann zum nächsten Kanal springen. Der jeweils gewandelte Wert soll in einem Array oder einer Variablen gespeichert werden. Sobald jeder Kanal 64 mal gewandelt wurde soll der addierte Wert gemappt werden. Mit einer For-Schleife werde ich nicht arbeiten können oder?
Muss ich für die Umsetzung nun mit Interrupts arbeiten?

Habe jetzt meinen Code erstmal so geändert, dass U1 und U2 gewandelt werden und in summe/summe1 aufaddiert werden. Jetzt bräuchte ich noch einen Zähler oder eine Schleife die zählt wie oft schon addiert wurde und bei 64 den Wert dann mappt und ausgibt.

//Volatile damit Variablen Compiler weiß, dass Inhalt sich auch außerhalb ändern kann. Wird gebraucht wegen Interrupt, der nicht zum normalen Programmfluß gehört.
volatile int analogVal;
volatile int statusU1;
volatile int statusU2;
volatile int statusU3;
volatile int statusU4;
char serialEing;
unsigned long UVal[64];
long summe;
long summe1;
int x;




void setup() {
  Serial.begin(9600);
  pinMode(1, INPUT); // Ist das wichtig? Pins haben auch so funtkioniert, hatte ich aber in einen meiner ersten Sketchs drinne.
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);

  ADMUX = 0xC0; //Binär 1100 0000 Internal 1.1V Voltage Reference with external capacitor at AREF pin. AD-Pin 0 wird als Eingang gewählt. ADLAR = 0 ==> unteren 8 Bits werden in ADCL geschrieben die oberen 2 in ADCH

  ADCSRA |= 0x87; //B 1000 0111

  statusU1 = 0;
  statusU2 = 0;
  statusU3 = 0;
  statusU4 = 0;
  Serial.println("Welche Eingaenge werden verwendet?");
  Serial.println("Eingaenge 1-4");
  Serial.println("Mit 9 bestaetigen und Wandlung beginnen");
}

void loop() {
  serialEing = Serial.read();
  if ( serialEing == '1' ) {
    statusU1 = 1;
    Serial.println("U1 ausgewaehlt");
  }
  if ( serialEing == '2' ) {
    statusU2 = 1;
    Serial.println("U2 ausgewaehlt");
  }
  if ( serialEing == '3' ) {
    statusU3 = 1;
    Serial.println("U3 ausgewaehlt");
  }
  if ( serialEing == '4' ) {
    statusU4 = 1;
    Serial.println("U4 ausgewaehlt");
  }
  if ( serialEing == '9' ) {
    Serial.println("Eingabe abgeschlossen");
    Ausgabe();
  }
}

void Ausgabe() {
  serialEing = Serial.read();
  if ( serialEing == '0') {
    statusU1 = 0;
    statusU2 = 0;
    statusU3 = 0;
    statusU4 = 0;
    summe = 0;
    summe1 = 0;
    return;
  }

  if (statusU1 == 1) {
    ADMUX  = ADMUX & 0xF0;
    ADMUX |= 0x00;
    x = 1;
    messung();
    summe += analogVal;
    Serial.println(summe); 
  }

  if (statusU2 == 1) {
    ADMUX  = ADMUX & 0xF0;
    ADMUX |= 0x01;
    x = 2;
    messung();
    summe1 += analogVal;
    Serial.println(summe1); 
  }

  if (statusU3 == 1) {
    ADMUX  = ADMUX & 0xF0;
    ADMUX |= 0x02;
    x = 3;
    messung();
  }

  if (statusU4 == 1) {
    ADMUX  = ADMUX & 0xF0;
    ADMUX |= 0x03;
    x = 4;
    messung();
  }

  Ausgabe();
}

void messung() {
    ADCSRA |= 0x40; 
    while (ADCSRA & (1 << ADSC)) { 
    }
    analogVal = ADCL | (ADCH << 8); 
}

Habe es geschafft, habe eine For-Schleife eingebaut und nun funktioniert es so wie ich es möchte.
Vielen Dank euch nochmal für eure Hilfe und die mitgebrachte Geduld.

//Volatile damit Variablen Compiler weiß, dass Inhalt sich auch außerhalb ändern kann. Wird gebraucht wegen Interrupt, der nicht zum normalen Programmfluß gehört.
volatile int analogVal;
volatile int statusU1;
volatile int statusU2;
volatile int statusU3;
volatile int statusU4;
char serialEing;
unsigned long UVal1[64];
unsigned long UVal2[64];
unsigned long UVal3[64];
unsigned long UVal4[64];
long summe1;
long summe2;
long summe3;
long summe4;





void setup() {
  Serial.begin(9600);
  pinMode(1, INPUT); // Ist das wichtig? Pins haben auch so funtkioniert, hatte ich aber in einen meiner ersten Sketchs drinne.
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);

  ADMUX = 0xC0; //Binär 1100 0000 Internal 1.1V Voltage Reference with external capacitor at AREF pin. AD-Pin 0 wird als Eingang gewählt. ADLAR = 0 ==> unteren 8 Bits werden in ADCL geschrieben die oberen 2 in ADCH

  ADCSRA |= 0x87; //B 1000 0111

  statusU1 = 0;
  statusU2 = 0;
  statusU3 = 0;
  statusU4 = 0;
  Serial.println("Welche Eingaenge werden verwendet?");
  Serial.println("Eingaenge 1-4");
  Serial.println("Mit 9 bestaetigen und Wandlung beginnen");
}

void loop() {
  serialEing = Serial.read();
  if ( serialEing == '1' ) {
    statusU1 = 1;
    Serial.println("U1 ausgewaehlt");
  }
  if ( serialEing == '2' ) {
    statusU2 = 1;
    Serial.println("U2 ausgewaehlt");
  }
  if ( serialEing == '3' ) {
    statusU3 = 1;
    Serial.println("U3 ausgewaehlt");
  }
  if ( serialEing == '4' ) {
    statusU4 = 1;
    Serial.println("U4 ausgewaehlt");
  }
  if ( serialEing == '9' ) {
    Serial.println("Eingabe abgeschlossen");
    Ausgabe();
  }
}

void Ausgabe() {
  serialEing = Serial.read();
  if ( serialEing == '0') {
    statusU1 = 0;
    statusU2 = 0;
    statusU3 = 0;
    statusU4 = 0;
    summe1 = 0;
    summe2 = 0;
    summe3 = 0;
    summe4 = 0;
    return;
  }
  unsigned int i;
  summe1 = 0;
  summe2 = 0;
  summe3 = 0;
  summe4 = 0;
  for (i = 0; i < 64; i++) {  
    if (statusU1 == 1) {
      ADMUX  = ADMUX & 0xF0;
      ADMUX |= 0x00;
      messung();
      UVal1[i] = analogVal;
      summe1 += UVal1[i];
      if (i == 63) {
        Serial.print("U1: ");
        float SpgVal1 = map(summe1, 0, 65472, 0 , 500) / 100.00;
        Serial.println(SpgVal1);
      }
    }

    if (statusU2 == 1) {
      ADMUX  = ADMUX & 0xF0;
      ADMUX |= 0x01;
      messung();
      UVal2[i] = analogVal;
      summe2 += UVal2[i];
      if (i == 63) {
        Serial.print("U2: ");
        float SpgVal2 = map(summe2, 0, 65472, 0 , 500) / 100.00;
        Serial.println(SpgVal2);
      }
    }

    if (statusU3 == 1) {
      ADMUX  = ADMUX & 0xF0;
      ADMUX |= 0x02;
      messung();
      UVal3[i] = analogVal;
      summe3 += UVal3[i];
      if (i == 63) {
        Serial.print("U3: ");
        float SpgVal3 = map(summe3, 0, 65472, 0 , 500) / 100.00;
        Serial.println(SpgVal3);
      }
    }

    if (statusU4 == 1) {
      ADMUX  = ADMUX & 0xF0;
      ADMUX |= 0x03;
      messung();
      UVal4[i] = analogVal;
      summe4 += UVal4[i];
      if (i == 63) {
        Serial.print("U4: ");
        float SpgVal4 = map(summe4, 0, 65472, 0 , 500) / 100.00;
        Serial.println(SpgVal4);
      }
    }
  }
  Ausgabe();
}

void messung() {
  ADCSRA |= 0x40;
  while (ADCSRA & (1 << ADSC)) {
  }
  analogVal = ADCL | (ADCH << 8);
}

Du rufst in der Funktion Ausgabe() die Funktion Ausgabe() auf? Keine gute Idee

Funktionen können übrigens auch einfach Variablen zurückgeben:

unsigned int messung() 
{
  ADCSRA |= 0x40;
  while (ADCSRA & (1 << ADSC)) {
  }

  return ADCL | (ADCH << 8);
}
UVal2[i] = messung();

volatile brauchst du hier übrigens nicht mehr. Das nimmt man i.d.R. nur für Variablen die innerhalb und außerhalb von Interrupt Routinen verwendet werden.