Kampis Elektroecke

XMega – ADC

XMega Blockdiagramm

Ein ADC ermöglicht es dem Mikrocontroller analoge Spannungen einzulesen. Der XMega besitzt zwei ADCs mit jeweils 8 Eingängen und einer Auflösung von 12 Bit. Zusätzlich verfügt jeder ADC über 4 Messkanäle. Dadurch wird es dem ADC ermöglicht bis zu vier Wandlungen durchführen zu können, ohne das die CPU des Mikrocontrollers den ADC unterbrechen muss.

ADC konfigurieren:

In diesem Tutorial verwende ich den ADC vom Port A. Das Ergebnis soll per USART an einem Computer geschickt und dort auf einem Terminal ausgegeben. Durch das Löschen des CTRLB-Registers

wird die Auflösung auf 12 Bit gesetzt, das Messergebnis rechtseitig ausgerichtet und der unsigned-Modus ausgewählt.

Im REFCTRL-Register des ADC wird die Referenzspannung zum Messen eingestellt.

Die Referenzspannung soll in diesem Beispiel 1 V betragen. Dem entsprechend muss auch dieses Register gelöscht werden.

Als nächstes muss noch der Prescaler eingestellt werden. Der Wert des Prescalers muss an die verwendete Taktfrequenz angepasst werden, da der ADC laut Datenblatt nicht mehr als 2 MS/s schafft. In diesem Beispiel stelle ich den Prescaler auf 32.

Nun wird der ADC noch aktiviert:

Messen einer Spannung:

In diesem Beispiel soll die Spannung des Lichtsensors auf dem Board gemessen werden. Dieser Lichtsensor befindet sich am Pin A0. Dieser muss zuerst als Eingang konfiguriert werden:

Für die Messung soll der Kanal 0 des ADCs verwendet werden. Dieser muss ebenfalls konfiguriert werden. Dazu wird als erstes der Modus und die Verstärkung eingestellt. Dies erfolgt über das CTRL-Register des Kanals. In diesem Beispiel soll eine Verstärkung von 1 und der Single-Ended Modus (da ich eine Spannung gegen GND messen möchte) verwendet werden.

Bei der Konfiguration des Eingangsmodus muss der Modus des ADCs berücksichtigt werden. Falls der signed-Modus verwendet, ergeben sich andere Konfigurationsmöglichkeiten für den Eingangsmodus des Kanals.

Über das MUXCTRL-Register wird nun der Eingangspin für den Kanal festgelegt. Auch hier ist die Funktion wieder vom gewählten Modus des ADCs und vom Eingangsmodus des Kanals abhängig (siehe Datenblatt).

Wenn alles eingestellt worden ist, kann eine Messung gestartet werden. Dazu wird im CTRL-Register das START-Bit gesetzt.

Der Abschluss einer Messung wird im INTFLAGS-Register angezeigt. Solange das IF-Bit nicht gesetzt ist, ist eine Messung aktiv.

Sobald die Messung beendet worden ist, muss das Flag durch ein erneutes Setzen gelöscht werden. Dann kann das Messergebnis aus dem RES-Register ausgelesen werden.

Zum Schluss wird das Ergebnis in einen String umgewandelt und per USART an einen Computer gesendet, wo das Ergebnis mittels Terminal ausgegeben werden kann.

ADC kalibrieren:

Die Genauigkeit des ADC kann durch die Verwendung der abgelegten Kalibrationsdaten erhöht werden. Die Werte können mittels NVM-Controller aus den abgelegten Produktionsdaten ausgelesen und dann in die Kalibrationsregister kopiert werden.

Das komplette Beispielprojekt steht in meinem GitLab-Repository zum Download bereit.

Last Updated on

16 Kommentare

  1. Hallo, ich habe fast eins zu eins deinen code kopiert aber leider bin ich trotzdem nicht im Stande das es funktioniert. Kannst du mir vielleicht helfen?

    1. Hallo,

      ich habe deinen Code mal entfernt, da der Kommentar sonst zu lang wird. Sowas bitte demnächst per Mail schicken.
      Auf den ersten Blick ist dein Code ziemlich durcheinander. Ich schaue mal ob ich ihn vernünftig zusammengebastelt bekomme.
      Du hast auch kein richtiges Hauptprogramm und alles.
      Wenn du mir deine E-Mailadresse gibst, kann ich dir den Code gerne zuschicken.
      Laut AVR Studio ist er in Ordnun….was die Syntax angeht. Ob er dann bei dir funktioniert ist ein anderes Thema :)

  2. s.reps@gmx.at

    Sorry das es so ein starkes durcheinander ist, normalerweiße programmier ich nicht so, nur normalerwieße benutz ich einen pic und da ist für mich alles einfacher :)
    Ich hab in dem programm schon einiges probiert und deswegen dann nichts mehr sortiert und auch nicht mehr alles herausgelöscht. :S

    Ich hoffe du kannst mir helfen bei einer einfachen ausgabe auf die uart vom adc.
    Danke

  3. Tolle, wirklich tolle Seite!!!
    Habe sie leider erst viel zu spät entdeckt :) nachdem ich mich schon durch gefühlte hundert Atmel User Guides durch gelesen habe.

    Weiter so!!!

  4. Hallo zusammen,
    kann nur meinen Vorgängern zustimmen, wirklich eine tolle Seite die ihr hier habt.

    Ich habe bisher immer mit dem ATmega programmiert und bin soweit klar gekommen. Seit kurzem bin ich auf den Xmega umgestiegen und komme bei dem ADC Initialisierung nicht weiter.

    Bitte deshalb um hilfe
    Könn

    1. Hey,

      da ich jetzt endlich ein neues XMega Board habe, kann ich auch endlich weiter machen :)
      Schick mir das Programm mal bitte per E-Mail zu.
      Ich gucke dann die Tage / Wochenende drüber (bin erst wieder Freitag zu Hause).

      Gruß
      Daniel

  5. Diese Zeile funktioniert bei mir offenbar nicht:
    while(!Channel->INTFLAGS);
    Wenn ich statt dessen eine Millisekunde warte, dann geht es.

    1. Habe inzwischen rausgefunden woran es wirklich lag.
      In der Funktion ADCA_Conversion() fehlt das Ruecksetzen der INTFLAGS.
      Es sollte also noch folgende Zeile eingefuegt werden:
      Channel->INTFLAGS = ADC_CH_CHIF_bm; //INTFLAGS loeschen

      Ausserdem ist noch die Zeile “int Result = 0x00;” ueberfluessig.
      (Ergibt eine entsprechende Compiler-Warnung)

      Nach dem Umrechnen der gelesenen Werte in Millivolts bekomme
      ich immer einen signifikanten Offset. Wobei es keine Rolle spielt ob
      ADCA_Cal() benutzt oder nicht.
      Der Offset ist auch noch leicht unterschiedlich ob PA1 oder PA2 benutzt.
      Ich habe mir mit diesem Unterprogramm fuer die Umrechnung beholfen:

      uint adc_millivolt(uint adwert,int offset)
      {
      long mvlong = (adwert<=offset) ? 0 : (long)REFERENZSPANNUNG*(adwert-offset);
      return (mvlong+2048)/4096;
      }

      Aufgerufen wird es dann jeweils so:
      sprintf(text,"%4d mV %4d mV ",adc_millivolt(adwert1,156),adc_millivolt(adwert2,166));

      Statt mit dem Uart gebe ich es jeweils auf ein LCD-Modul aus.

  6. Hallo Kampi,

    danke für deinen Code. Hab den ADC vom Xmega mal ausprobiert. Jetzt ist mir aufgefallen: Der Offset beträgt bei mir 129, ist das nicht ein bisschen arg viel? Hab jetzt schon zwei Controller ausprobiert, beide ziemlich identisch. Wie viel beträgt der Offset bei dir?

    Lg Andreas

    1. Hey Andreas,

      der ADC hat 12-Bit Auflösung. Das macht bei einer Referenzspannung von Vcc, also 3,3V, etwa 0,0008056640625V. Das mit 129 multipliziert ergibt 0,1039306640625V. Du siehst, das es nicht ganz so viel ist. Der Fehler kann schon durch eine fehlende Kalibration auftreten oder wenn deine Referenzspannung (hier exemplarisch dein Vcc) schwankt.
      Gerade wenn du mehrere Controller getestet hast, würde ich es mit der Versorgungsspannung / fehlender Kalibration in Verbindung bringen.

      Gruß
      Daniel

      1. Also das Kalibrationsregister hab ich sogar ausgelesen, dachte auch dass es daran liegt. Dadurch hat sich aber nix geändert.
        Ja wahrscheinlich liegts an der Stromversorgung. Ich stabilisiere die Eingangsspannung mit einem LM317 auf 3,3V, vielleicht sollt ich mal die Pufferkondensatoren vergrößern.

  7. Hallo,
    super Seite erst mal.

    Ich habe dein Beispiel versucht und es funktioniert Grundsetzlich. Allerdings habe ich folgendes Problem. Wenn mein ADC auf GNG liegt zeigt es immer noch LSB von 11 an.
    ich habe den ADC auf 8Bit laufen, Single Ended.Habe auch schon mit den Referenzspg. gespielt. Meine Ref erzeuge ich über einen Spannungsteiler der an einem OP als Buffer geschaltet hängt. Mein µC ist ein XMEGA 32D3.
    Vielleicht kennst du das Problem.

    1. Hallo Matze,

      hast du den ADC mal kalibriert?
      Eventuell ist es eine fehlende Kalibration / Rauschen im ADC?
      Eine andere Idee hätte ich jetzt auch nicht.

      Gruß
      Daniel

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.