Forum: Mikrocontroller und Digitale Elektronik Mega32 AD Wandlung Aufruf in der Main oder in ISR?


von Matthias M. (Gast)


Lesenswert?

Hallo liebe Forumsgemeinde,

bei mir im Programm läuft erfolgreich eine ADC Wandlung nach folgendem 
Beispiel:
1
// Diese Beispiel zeigt die Anwendung des ADC eines ATmega169
2
// unter Verwendung der internen Referenzspannung von nominell 1,1V.
3
// Zur Anpassung an andere AVR und/oder andere Referenzspannungen
4
// siehe Erläuterungen in diesem Tutorial und im Datenblatt
5
 
6
/* ADC initialisieren */
7
void ADC_Init(void) {
8
 
9
  uint16_t result;
10
 
11
  // die Versorgungsspannung AVcc als Refernz wählen:
12
  ADMUX = (1<<REFS0);    
13
  // oder interne Referenzspannung als Referenz für den ADC wählen:
14
  // ADMUX = (1<<REFS1) | (1<<REFS0);
15
 
16
  // Bit ADFR ("free running") in ADCSRA steht beim Einschalten
17
  // schon auf 0, also single conversion
18
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
19
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
20
 
21
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
22
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
23
 
24
  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung 
25
  while (ADCSRA & (1<<ADSC) ) {         // auf Abschluss der Konvertierung warten
26
  }
27
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
28
     Wandlung nicht übernommen. */
29
  result = ADCW;
30
}
31
 
32
/* ADC Einzelmessung */
33
uint16_t ADC_Read( uint8_t channel )
34
{
35
  // Kanal waehlen, ohne andere Bits zu beeinflußen
36
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
37
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
38
  while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung warten
39
  }
40
  return ADCW;                    // ADC auslesen und zurückgeben
41
}
42
 
43
/* ADC Mehrfachmessung mit Mittelwertbbildung */
44
/* beachte: Wertebereich der Summenvariablen */
45
uint16_t ADC_Read_Avg( uint8_t channel, uint8_t nsamples )
46
{
47
  uint32_t sum = 0;
48
 
49
  for (uint8_t i = 0; i < nsamples; ++i ) {
50
    sum += ADC_Read( channel );
51
  }
52
 
53
  return (uint16_t)( sum / nsamples );
54
}
55
 
56
...
57
 
58
/* Beispielaufrufe: */
59
 
60
int main()
61
{
62
  uint16_t adcval;
63
  ADC_Init();
64
 
65
  while( 1 ) {
66
    adcval = ADC_Read(0);  // Kanal 0
67
    // mach was mit adcval
68
69
  }
70
}

Mein CPU läuft mit 8 MHz, sprich in der Main wird ADC_Read mit CPU Takt 
aufgerufen was viel zu viel ist. Nun bin ich bei meinen Recherchen auf 
die ISR(ADC_vect) gestoßen.

Kann ich statt in der Main auch einfach folgendes schreiben?
1
ISR(ADV_vect) {
2
   adcval = ADC_Read(0);  // Kanal 0
3
}

Und mir den Wert von adcval in der Main z.b. auf ein LCD ausgeben 
lassen?

Soweit ich das verstanden habe wird die ISR immer dann aufgerufen wenn 
ein neuer AD Wert vorliegt, stimmt das?

Ich hab übrigens noch einen weiteren Timer (ISR( TIMER0_OVF_vect ) alle 
10ms am laufen, stören die beiden sich?

Gruß

von Karl H. (kbuchegg)


Lesenswert?

Matthias M. schrieb:

> Mein CPU läuft mit 8 MHz, sprich in der Main wird ADC_Read mit CPU Takt
> aufgerufen was viel zu viel ist.

Was heisst das: 'viel zu viel'


> Nun bin ich bei meinen Recherchen auf
> die ISR(ADC_vect) gestoßen.
>
> Kann ich statt in der Main auch einfach folgendes schreiben?

Können tust du schon. Sinnlos ist es auch.

>
>
1
> ISR(ADV_vect) {
2
>    adcval = ADC_Read(0);  // Kanal 0
3
> }
4
>

Nö.
Wenn die ISR aufgerufen wird, dann liegt ein ADC Ergebnis vor. Das 
WISSEN wir, sonst wäre die ISR ja nicht aufgerufen worden.

Ergo
1
ISR(ADV_vect) {
2
  adcval = ADC;
3
}

fertig.
Jetzt musst du nur noch an strategisch günstigen Positionen den 
ADC-Wandler anstossen, eine Wandlung vorzunehmen. Ist er damit fertig, 
dann wird die ISR aufgerufen.

Aber im Ernst: Ist das wirlich ein großes Problem, dass die 
Hauptschleife ein paar Takte verbrutzelt, weil sie aktiv auf den ADC 
wartet? Immerhin hat das den Charme, dass du von jedem Ergebnis in der 
Hauptschleife auch den Zeitpunkt weißt, wann es während eines Durchgangs 
durch die Hauptschleife entstanden ist. Und wenn einem die Wartezeit 
dann wirklich stört, dann kann man das ganze auch so umformen, dass die 
Wandlung läuft, während der µC in der Hauptschleife gerade was anderes 
macht. Nach dem Muster
1
  ...
2
3
  while( 1 )
4
  {
5
     ADC Ergebnis holen
6
     nächste Wandlung starten
7
8
     Ergebnis bearbeiten
9
  }

Die Wandlungt läuft, während das letzte Eregbnis bearbeitet wird. Geht 
die Hauptschleife in den nächsten Durchgang, dann ist der ADC (meistens) 
längst mit der Arbeit fertig. Man holt sich das Ergebnis ab und startet 
gleich die nächste Wandlung. Während die läuft, bearbeitet man das 
zuletzt geholte Ergebnis. usw. usw.

von Matthias M. (Gast)


Lesenswert?

Danke für die Antwort, mir geht es weniger um die Takte die der 
Controller damit verbringt den AD Wert zu ermitteln sondern vielmehr um 
einen sauberen Code und da ich einfach keine x-Millionen Auswertungen / 
Sekunde brauch.

Sprich die ADC_vect ISR ist nur sinnvoll um die Main zu entlasten und 
dort die Werte des AD's in eine entsprechende Variable zu schreiben?

von Karl H. (kbuchegg)


Lesenswert?

Matthias M. schrieb:
> Danke für die Antwort, mir geht es weniger um die Takte die der
> Controller damit verbringt den AD Wert zu ermitteln sondern vielmehr um
> einen sauberen Code

was ist an
1
  while( 1 ) {
2
    adcval = ADC_Read(0);  // Kanal 0
3
4
    ...
5
  }
unsauber.
So wie ich das sehe, ist das die einfachste, übersichtlichste und am 
leichtesten zu durchschauende Variante.
Jede andere Version (inklusive ISR) fügt nur mehr Komplexität hinzu.

> und da ich einfach keine x-Millionen Auswertungen /
> Sekunde brauch.

Du musst ja nicht bei JEDEM Schleifendurchlauf den ADC abfragen.
Wenn du sagst, du brauchst keine x Millionen pro Sekunde, erhebt sich 
sofort die Frage: ja, wieviele brauchst du denn?
D.h. du musst dir dann sowieso einen Meschanismus einfallen lassen, wie 
du diese spezielle Anzahl an Abfragen zeitmässig realisierst. Zb. mit 
einem Timer, der pro Sekunde y mal den Vorgang anstösst (nach dem 
Vorbild einer blinkenden LED).

> Sprich die ADC_vect ISR ist nur sinnvoll um die Main zu entlasten und
> dort die Werte des AD's in eine entsprechende Variable zu schreiben?

Ist eine Sichtweise.
Eine andere kann sein, dass der ADC mit maximaler Geschwindigkeit 
wandeln und seine Ergebnisse (eine gewisse Anzahl an Ergebnissen) erst 
mal im Speicher ablegen soll.

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.