Forum: Mikrocontroller und Digitale Elektronik Zwei ADC-Werte in der main verfügbar machen


von D. M. (Gast)


Lesenswert?

Hallo zusammen,

ich habe eine Frage zum ADC meines Atmega32 und hoffe, dass ihr mir 
helfen könnt.
Ich habe folgende ISR außerhalb meiner main, mit der ich zwei ADCs 
einlese:
1
ISR(ADC_vect)
2
{  
3
  uint8_t adc0_wert;
4
  uint8_t adc0_wert;
5
  char adc0[4];
6
  char adc1[4];
7
8
  switch (ADMUX)
9
  {
10
    case 0x60: //ADLAR and REFS0 = 1 and ADC0 selected
11
      adc0_wert = ADCH;
12
      itoa(ADCH, adc0, 10);
13
      lcd_gotoxy(5,0);
14
      lcd_puts(adc0);
15
      lcd_puts(" ");
16
      ADMUX = 0x61;
17
      break;
18
    case 0x61: //ADLAR and REFS0 = 1 and ADC1 selected
19
      adc1_wert = ADCH;
20
      itoa(ADCH, adc1, 10);
21
      lcd_gotoxy(5,1);
22
      lcd_puts(adc1);
23
      lcd_puts(" ");
24
      ADMUX = 0x60;
25
      break;
26
    default:
27
      //Default code
28
      break;
29
  }
30
  //Start the next conversion
31
  ADCSRA |= 1 << ADSC;
32
}

Ich würde gerne innerhalb meiner main die beiden ADC-Werte verwenden.
Die Zeile "adc0_wert = ADCH;" bringt leider nicht das gewünschte 
Ergebnis, wahrscheinlich weil ich auf diese Weise den ADCH-Wert nicht 
global in adc0_wert speichern kann.
Bei einem ADC-Kanal kann man ja immer ADCH auslesen.
Kennt jemand eine Möglichkeit den aktuellen ADC-Wert der beiden Kanäle 
immer in der main verfügbar zu machen?

Vielen Dank im Voraus!

Gruß DM

von Max H. (hartl192)


Lesenswert?

Du musst adc0_wert und adc1_wert als globale Variablen deklarieren.

BTW: Eigentlich sollte man keine zeitaufwändigen Sachen, wie z.B. Text 
auf einem LCD ausgeben in der ISR machen.

: Bearbeitet durch User
von Conny G. (conny_g)


Lesenswert?

Max H. schrieb:
> Du musst adc0_wert und adc1_wert als globale Variablen deklarieren.

Und als Volatile deklarieren.

> BTW: Eigentlich sollte man keine zeitaufwändigen Sachen, wie z.B. Text
> auf einem LCD ausgeben in der ISR machen.

von San L. (zwillingsfreunde)


Lesenswert?

Max H. schrieb:
> Du musst adc0_wert und adc1_wert als globale Variablen deklarieren.

Oder die "korrekte" oder eher saubere Variante:
Die beiden Werte als Rückgabewert der Funktion an Main liefern. So 
kannst du zwei Variablen Lokal in deiner Mainfunktion definiren und die 
Werte einfach aus der Funktion zurückgeben lassen.

Max H. schrieb:
> BTW: Eigentlich sollte man keine zeitaufwändigen Sachen, wie z.B. Text
> auf einem LCD ausgeben in der ISR machen.

Gute Bemerkung. Diesen Teil solltest du dringend aus der ISR streichen. 
ISR sollte so kurz wie möglich gehalten werden!

Gruss

von Schimanski (Gast)


Lesenswert?

Fanden Sie diese Antwort hilfreich?

[ ] Ja
[ ] Nein
[ ] kein Plan, was gemeint ist

von Falk B. (falk)


Lesenswert?

Siehe Interrupt

von Peter II (Gast)


Lesenswert?

San Lue schrieb:
> Die beiden Werte als Rückgabewert der Funktion an Main liefern.

rückgabewerte bei einer ISR wird aber nicht so einfach.

von Karl H. (kbuchegg)


Lesenswert?

Aber eigentlich ist das Bearbeiten von 2 ADC-Kanälen mittels ISR keine 
so gute Idee. Denn da gibt es noch eine klitzekleine Nebenbedingung, die 
dir das Leben schwer macht.

An deiner Stelle würde ich den ADC nicht über Interrupts laufen lassen, 
sondern ganz klassisch in der Hauptschleife abfragen. Und dazu benutze 
ich die ADC-Routinen aus dem AVR-GCC-Tutorial (dort nach ADC suchen, da 
wird weiterverlinkt). Für die meisten Fälle sind die völlig ausreichend.
Bis auf zwei Ausnahmen (sampeln eines Video-Signals bzw. ähnlich 
gelagert der Bau eines 'Oszilloskops') hab ich noch nie ein Beispiel 
erlebt, bei dem eine ADC Behandlung über Interrupt einfacher gewesen 
wäre oder aufgrund der Samplerate notwendig gewesen wäre. Mittels 
Polling erreicht man immer noch genügend hohe Abtastraten und der 
Löwenanteil geht dann sowieso in der Aufbereitung bzw. Anzeige der Werte 
drauf.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

San Lue schrieb:

> Gute Bemerkung. Diesen Teil solltest du dringend aus der ISR streichen.
> ISR sollte so kurz wie möglich gehalten werden!

Nicht nur das.
Man denke nur an den Fall, dass der ADC Interrupt genau zu einem 
Zeitpunkt kommt, in dem gerade eine Instanz der Funktionen lcd_gotoxy 
oder lcd_puts in der Hauptschleife am laufen sind. Das Chaos an der 
Anzeige ist damit vorprogrammiert.
Bei derartigen klassischen 'Shared Resources' muss man sich entscheiden
* entweder alle Zugriffe darauf nur in den ISR, solange die sich nicht 
gegenseitig unterbrechen können
* oder alle Zugriffe nur in der Hauptschleife
* oder komplizierte Synchronisierungen zwischen ISR und Hauptschleife
Aber einfach drauflos zugreifen, geht mit Sicherheit irgendwann in die 
Hose. Klassischerweise geht es genau dann in die Hose, wenn man es am 
wenigsten gebrauchen kann.

: Bearbeitet durch User
von D. M. (Gast)


Lesenswert?

Vielen Dank euch allen, hat mir wirklich sehr geholfen!
Ich mache es jetzt so:
1
uint8_t ADC_Read(uint8_t channel)
2
{
3
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
4
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
5
  while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung warten
6
  }
7
  return ADCH;  
8
}

Funktioniert prima so! Danke!

Gruß DM

von Dennis K. (scarfaceno1)


Lesenswert?

Ganz oben im Quelltext wurde 2 mal die Variable adc0_wert deklariert...

adc1_wert fehlt...

die while Schleife in der du auf die Fertigstellung der Conversion 
wartest, steht die auch im Interrupt?

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.