mikrocontroller.net

Forum: Compiler & IDEs A/D-Routine im Atmega128


Autor: Owen Senmeis (senmeis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Servus,

ich verwende alle 8 A/D-Kanäle vom Atmega128. Bisher sieht meine 
A/D-Routine wie folgt aus:
ISR(ADC_vect) 
{ 
   static UINT8 adc_channel;
   static UINT16 m[8]; 
   
   m[adc_channel] = (3 * m[adc_channel] + ADC) >> 2;  // Low Pass Filter 
   adc_result[adc_channel] = (m[adc_channel] - 512) * 64;  // convert to INT16
   adc_channel++; 
   if (adc_channel == 8) 
       adc_channel = 0; 
   ADMUX = (ADMUX & 0xE0) | adc_channel; 
 
   ADCSRA |= (1 << ADSC);   // start next conversion 
}
D.h. A/D-Wandlung mit A/D-Interrupt direkt nacheinander aufrufen. Es 
gibt einen Nachteil, dass man die Abtastrate nicht feststellen kann. 
Frage, kann man stattdessen einen 1ms-Timer wie folgt einsetzen:
void 1ms_TimerInterrupt(void)
{
  ADCSRA |= (1 << ADSC);   // start next conversion
}

ISR(ADC_vect) 
{ 
   static UINT8 adc_channel;
   static UINT16 m[8]; 
   
   m[adc_channel] = (3 * m[adc_channel] + ADC) >> 2;  // Low Pass Filter 
   adc_result[adc_channel] = (m[adc_channel] - 512) * 64;  // convert to INT16
   adc_channel++; 
   if (adc_channel == 8) 
       adc_channel = 0; 
   ADMUX = (ADMUX & 0xE0) | adc_channel;  
}
D.h. die Abtastrate ist nun 1000Hz/8 = 125Hz.

Welche Methode ist üblicher?

Gruss
Senmeis

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Methode mit Timer ist IMHO eleganter und wenn man einen Timer dafür 
frei hat oder einen mitbenutzen kann....

Aber was anderes: Nach der Kanalumschaltung legt dein Quelltext ja 
direkt mit der Messung los und verwendet keine üblicherweise empfohlene 
Dummyreadouts. Sind deine Messungen trotzdem genau?

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan B. schrieb:

> Aber was anderes: Nach der Kanalumschaltung legt dein Quelltext ja
> direkt mit der Messung los und verwendet keine üblicherweise empfohlene
> Dummyreadouts.

Das ist doch nur ein (anscheinend nicht tot zu kriegender) Mythos(*).
Dummyreads sind nötig nach dem Umschalten der Referenz, nicht dem Kanal 
(außer im Differential-Mode).

(*) Oder war das vielleicht mal bei den Uralt-AVRs nötig?

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke! Es ist im Atmega128 Datenblatt so beschrieben, wie du schreibst! 
Dummyread bei Referenzwechsel oder Wechsel bei differential channels

Autor: Owen Senmeis (senmeis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank. Die Voraussetzung für die Methode mit Timer ist jedoch, 
dass die Bearbeitungsdauer der A/D-Wandlung kürzer als 1ms sein muss. 
Ich denke, dies wird immer erfüllt.

Cu
Senmeis

Autor: Matthias Lipinsky (lippy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Vielen Dank. Die Voraussetzung für die Methode mit Timer ist jedoch,
>dass die Bearbeitungsdauer der A/D-Wandlung kürzer als 1ms sein muss.
>Ich denke, dies wird immer erfüllt.
void 1ms_TimerInterrupt(void)

ISR(ADC_vect) 

Ich würde den ADC-Int weglassen und das ADC-Ergebnis einfach im nächsten 
1ms-Int abholen, vor dem Start der nächsten Wandlung.

Autor: Werner B. (werner-b)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Matthias Lipinsky schrieb:
>>Vielen Dank. Die Voraussetzung für die Methode mit Timer ist jedoch,
>>dass die Bearbeitungsdauer der A/D-Wandlung kürzer als 1ms sein muss.
>>Ich denke, dies wird immer erfüllt.
>
>
> void 1ms_TimerInterrupt(void)
> 
> ISR(ADC_vect)
> 
>
> Ich würde den ADC-Int weglassen und das ADC-Ergebnis einfach im nächsten
> 1ms-Int abholen, vor dem Start der nächsten Wandlung.

Im Gegenteil. Den ADC durch den Timer triggern lassen und optional, 
falls der Timer Interrupt nicht verwendet wird (den benötigt man 
eigentlich nicht) in der darauffolgenden ADC ISR das Interrupt-Flag des 
Timer zurücksetzen. Das ergibt die genauesten Abtastintervalle die auf 
einem AVR möglich sind, da nicht einmal mehr die zufällige Interrupt 
Latency Time des Timer Interrupts eine Rolle spielt.

Ausschnitt einer ADC Initilisierung (für einen ATMega32)
...
  // ADC Trigger Source = Timer0 Compare Match
  SFIOR |= (1 << ADTS1) | (1 << ADTS0);

  ADCSRA = ADC_DIVISOR |      // Pick the fastest sample frequency (smallest divisor) we can use 
     _BV(ADEN)|      // ADC an
     _BV(ADSC)|      // Beginne mit der Konvertierung
     _BV(ADIE)|      // ADC-Interrupt an
     _BV(ADATE);     // Auto trigger on
  TIFR =  _BV(OCF0); // Reset OCF0 - Flag
...

Und der zugehörigen ADC-ISR
...
ISR(ADC_vect)
{
  int16_t adc = ADCW;      // ADC einlesen

  TIFR =  _BV(OCF0);      // Reset OCF0 - Flag
...
}
...

Autor: SF (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So ähnlich würde ich es auch machen, nur leider hat der Mega128 keine 
Auto Trigger Funktionalität. Der ist einfach zu alt dafür...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.