AVR-GCC-Tutorial/Analoge Ein- und Ausgabe

Wechseln zu: Navigation, Suche

Analoge Eingangswerte werden in der Regel über den AVR Analog-Digital-Converter (AD-Wandler, ADC) eingelesen, der in vielen Typen verfügbar ist (typisch 10bit Auflösung). Durch diesen werden analoge Signale (Spannungen) in digitale Zahlenwerte gewandelt. Bei AVRs, die über keinen internen AD-Wandler verfügen (z. B. ATmega162), kann durch externe Beschaltung (R/C-Netzwerk und Zeitmessung) die Funktion des AD-Wandlers simuliert werden.

Es gibt innerhalb der ATMega- und ATTiny-AVR Reihe keine Typen mit eingebautem Digital-Analog-Konverter (DAC) - diese Funktion ist erst ab der XMEGA-Reihe der AVR-Familie verfügbar, die aber wegen ihrer vielen Unterschiede im Umfang dieses Tutorials nicht behandelt wird. Die Umsetzung zu einer analogen Spannung muss daher durch externe Komponenten vorgenommen werden. Das kann z. B. durch PWM und deren Filterung zu (fast) DC, oder einem sogenannten R2R-Netzwerk erfolgen.

Unabhängig davon besteht natürlich immer die Möglichkeit, spezielle Bausteine zur Analog-Digital- bzw. Digital-Analog-Wandlung zu nutzen und diese über eine digitale Schnittstelle (z.b. SPI oder I2C) mit einem AVR anzusteuern.

AC (Analog Comparator)[Bearbeiten]

Der Comparator vergleicht 2 Spannungen an den Pins AIN0 und AIN1 und gibt einen Status aus welche der beiden Spannungen größer ist. AIN0 Dient dabei als Referenzspannung (Sollwert) und AIN1 als Vergleichsspannung (Istwert). Als Referenzspannung kann auch alternativ eine interne Referenzspannung ausgewählt werden.

Liegt die Vergleichsspannung (IST) unter der der Referenzspannung (SOLL) gibt der Comparator eine logische 1 aus. Ist die Vergleichsspannung hingegen größer als die Referenzspannung wird eine logische 0 ausgegeben.

Der Comparator arbeitet völlig autark bzw. parallel zum Prozessor. Für mobile Anwendungen empfiehlt es sich ihn abzuschalten sofern er nicht benötigt wird, da er ansonsten Strom benötigt. Der Comparator kann interruptgesteuert abgefragt werden oder im Pollingbetrieb.

Das Steuer- bzw. Statusregister ist wie folgt aufgebaut:

ACSR - Analog Comparator Status Register
Bit 7 6 5 4 3 2 1 0
Name ACD ACBG ACO ACI ACIE ACIC ACIS1 ACIS0
R/W R/W R/W R R/W R/W R/W R/W R/W
Initialwert 0 0 n/a 0 0 0 0 0
Bit 7 ACD
Analog Comparator Disable: 0 = Comparator ein, 1 = Comparator aus. Wird dieses Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muss das Bit 3 ACIE ggf. abgeschaltet werden.
Bit 6 ACBG
Analog Comparator Bandgap Select: Ermöglicht das umschalten zwischen interner und externer Referenzspannung. 1 = interne (~1,3 Volt), 0 = externe Referenzspannung (an Pin AIN0)
Bit 5 ACO
Analog Comparator Output: Hier wird das Ergebnis des Vergleichs angezeigt. Es liegt typischerweise nach 1-2 Taktzyklen vor.
IST < SOLL → 1
IST > SOLL → 0
Bit 4 ACI
Analog Comparator Interrupt Flag: Dieses Bit wird von der Hardware gesetzt, wenn ein Interruptereignis, das in Bit 0 und 1 definiert ist, eintritt. Dieses Bit löst noch keinen Interrupt aus! Die Interruptroutine wird nur dann ausgeführt, wenn das Bit 3 ACIE gesetzt ist und global Interrupts erlaubt sind (I-Bit in SREG gesetzt). Das Bit 4 ACI wird wieder gelöscht, wenn die Interruptroutine ausgeführt wurde oder wenn es manuell auf 1! gesetzt wird. Das Bit kann für Abfragen genutzt werden, steuert oder konfiguriert aber nicht den Comparator.
Bit 3 ACIE
Analog Comparator Interrupt Enable: Ist das Bit auf 1 gesetzt, wird immer ein Interrupt ausgelöst, wenn das Ereignis das in Bit 1 und 0 definiert ist, eintritt.
Bit 2 ACIC
Analog Comparator Input Capture Enable: Wird das Bit gesetzt, wird der Comparatorausgang intern mit dem Counter 1 verbunden. Es könnten damit z.b. die Anzahl der Vergleiche im Counter1 gezählt werden. Um den Comparator an den Timer1 Input Capture Interrupt zu verbinden, muss im Timerregister das TICIE1 Bit auf 1 gesetzt werden. Der Trigger wird immer dann ausgelöst, wenn das in Bit 1 und 0 definierte Ereignis eintritt.
Bit 1,0 ACIS1,ACIS0
Analog Comparator Interrupt select: Hier wird definiert, welche Ereignisse einen Interrupt auslösen sollen:
  • 00 = Interrupt auslösen bei jedem Flankenwechsel
  • 10 = Interrupt auslösen bei fallender Flanke
  • 11 = Interrupt auslösen bei steigender Flanke

Werden diese Bit geändert, kann ein Interrupt ausgelöst werden. Soll dies vermieden werden, muss das Bit 3 gelöscht werden.

ADC (Analog Digital Converter)[Bearbeiten]

Der Analog-Digital-Konverter (ADC) wandelt analoge Signale in digitale Werte um, welche vom Controller interpretiert werden können. Einige AVR-Typen haben bereits einen mehrkanaligen Analog-Digital-Konverter eingebaut. Die Feinheit, mit welcher ein analoges Signal aufgelöst werden kann, wird durch die Auflösung des ADC, d.h. durch die Anzahl der verwendeten Bits angegeben. So sind derzeit bspw. 8-Bit- oder 10-Bit-ADC im Einsatz. ADCs, die in AVRs enthalten sind, haben zur Zeit eine maximale Auflösung von 10-Bit.

Ein ADC mit 8 Bit Auflösung kann somit das analoge Signal in Abstufungen von 1/256 des Maximalwertes digitalisieren. Wenn wir nun mal annehmen, wir hätten eine Eingangspannung zwischen 0 und 5 Volt, eine Referenzspannung von 5 V und eine Auflösung von 3 Bit, dann könnten Intervalle mit den Grenzen 0 V, 0.625 V, 1.25 V, 1.875 V, 2.5 V, 3.125 V, 3.75 V, 4.375 V, 5 V entsprechend folgender Tabelle unterschieden werden:

Eingangsspannung am ADC / V Entsprechender Messwert
0 – 0.625 0
0.625 – 1.25 1
1.25 – 1.875 2
1.875 – 2.5 3
2.5 – 3.125 4
3.125 – 3.75 5
3.75 – 4.375 6
4.375 – 5 7

Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des Analog-Digital-Konverters ist, also, je mehr Bits er hat, desto genauer kann der jeweilige Wert erfasst werden.

Der interne ADC im AVR[Bearbeiten]

Oft sind auch mehrere Kanäle verfügbar. Kanäle heißt in diesem Zusammenhang, dass zwar bis zu zehn analoge Eingänge am AVR vorhanden sind, aber nur ein "echter" Analog-Digital-Wandler zur Verfügung steht. Vor der eigentlichen Messung ist also festzulegen, welcher Kanal ("Pin") mit dem Wandler verbunden und gemessen wird.

Die Umwandlung innerhalb des AVR basiert auf der schrittweisen Näherung. Beim AVR müssen die Pins AGND und AVCC beschaltet werden. Für genaue Messungen sollte AVCC über ein L-C Netzwerk mit VCC verbunden werden, um Spannungsspitzen und -einbrüche vom Analog-Digital-Wandler fernzuhalten. Im Datenblatt findet sich dazu eine Schaltung, die 10µH und 100nF vorsieht.

Das Ergebnis der Analog-Digital-Wandlung wird auf eine Referenzspannung bezogen. Aktuelle AVRs bieten drei Möglichkeiten zur Wahl dieser Spannung:

  • Eine externe Referenzspannung von maximal Vcc am Anschlusspin AREF. Die minimale (externe) Referenzspannung darf jedoch nicht beliebig niedrig sein, vgl. dazu das (aktuelle) Datenblatt des verwendeten Controllers.
  • Verfügt der AVR über eine interne Referenzspannung, kann diese genutzt werden. Alle aktuellen AVRs mit internem AD-Wandler sollten damit ausgestattet sein (vgl. Datenblatt: 2,56V oder 1,1V je nach Typ). Das Datenblatt gibt auch über die Genauigkeit dieser Spannung Auskunft.
  • Es kann die Spannung AVcc als Referenzspannung herangezogen werden

Bei Nutzung von AVcc oder der internen Referenz wird empfohlen, einen Kondensator zwischen dem AREF-Pin und GND anzuordnen. Die Festlegung, welche Spannungsreferenz genutzt wird, erfolgt z. B. beim ATmega16 mit den Bits REFS1/REFS0 im ADMUX-Register. Die zu messende Spannung muss im Bereich zwischen AGND und AREF (egal ob intern oder extern) liegen.

Der ADC kann in zwei verschiedenen Betriebsarten verwendet werden:

Einfache Wandlung (Single Conversion) 
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestoßen für jeweils eine Messung.
Frei laufend (Free Running) 
In dieser Betriebsart erfasst der Wandler permanent die anliegende Spannung und schreibt diese in das ADC Data Register.

Die Register des ADC[Bearbeiten]

Der ADC verfügt über eigene Register. Im Folgenden die Registerbeschreibung eines ATMega16, welcher über 8 ADC-Kanäle verfügt. Die Register unterscheiden sich jedoch nicht erheblich von denen anderer AVRs (vgl. Datenblatt).

ADCSRA    ADC Control and Status Register A.

In diesem Register stellen wir ein, wie wir den ADC verwenden möchten.
Das Register ist wie folgt aufgebaut:

Bit 7 6 5 4 3 2 1 0
Name ADEN ADSC ADFR ADIF ADIE ADPS2 ADPS1 ADPS0
R/W R/W R/W R/W R/W R/W R/W R/W R/W
Initialwert 0 0 0 0 0 0 0 0

ADEN (ADC Enable)

Dieses Bit muss gesetzt werden, um den ADC überhaupt zu aktivieren. Wenn das Bit nicht gesetzt ist, können die Pins wie normale I/O-Pins verwendet werden.

ADSC (ADC Start Conversion)

Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden Betriebsart muss das Bit gesetzt werden, um die kontinuierliche Messung zu aktivieren.
Wenn das Bit nach dem Setzen des ADEN-Bits zum ersten Mal gesetzt wird, führt der Controller zuerst eine zusätzliche Wandlung und erst dann die eigentliche Wandlung aus. Diese zusätzliche Wandlung wird zu Initialisierungszwecken durchgeführt.
Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung erfolgt ist und geht danach auf 0.

ADFR (ADC Free Run select)

Mit diesem Bit wird die Betriebsart eingestellt.
Ist das Bit auf 1 gesetzt arbeitet der ADC im "Free Running"-Modus. Dabei wird das Datenregister permanent aktualisiert. Ist das Bit hingegen auf 0 gesetzt, macht der ADC nur eine "Single Conversion".


ADIF (ADC Interrupt Flag)

Dieses Bit wird vom ADC gesetzt, sobald eine Umwandlung erfolgt ist und das ADC Data Register aktualisiert wurde.
Wenn das ADIE Bit sowie das I-Bit im AVR Statusregister gesetzt ist, wird der ADC Interrupt ausgelöst und die Interrupt-Behandlungsroutine aufgerufen.
Das Bit wird automatisch gelöscht, wenn die Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1! in das Register geschrieben wird.

ADIE (ADC Interrupt Enable)

Wenn dieses Bit gesetzt ist und ebenso das I-Bit im Statusregister SREG, dann wird der ADC-Interrupt aktiviert.

ADPS2...ADPS0 (ADC Prescaler Select Bits)

Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz und dem Eingangstakt des ADC.
Der ADC benötigt einen eigenen Takt, welchen er sich selber aus der CPU-Taktfreqenz erzeugt. Der ADC-Takt sollte zwischen 50 und 200kHz liegen.
Der Vorteiler muss also so eingestellt werden, dass CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert im Bereich (50-200)kHz ergibt.
Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir

\begin{matrix}
TF_{min}=\frac{CLK}{200\,\mathrm{kHz}}=\frac{4000000}{200000}=\mathbf{20}
\\
\\
TF_{max}=\frac{CLK}{50\,\mathrm{kHz}}=\frac{4000000}{50000}=\mathbf{80}
\end{matrix}
Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im Interesse der schnelleren Wandlungszeit werden wir hier den Faktor 32 einstellen.
ADPS2 ADPS1 ADPS0 Teilungsfaktor
0 0 0 2
0 0 1 2
0 1 0 4
0 1 1 8
1 0 0 16
1 0 1 32
1 1 0 64
1 1 1 128
ADCL

ADCH

ADC Data Register

Wenn eine Umwandlung abgeschlossen ist, befindet sich der gemessene Wert in diesen beiden Registern. Von ADCH werden nur die beiden niederwertigsten Bits verwendet. Es müssen immer beide Register ausgelesen werden, und zwar immer in der Reihenfolge: ADCL, ADCH. Der effektive Messwert ergibt sich dann zu:

x = ADCL;       // mit uint16_t x
x += (ADCH<<8); // in zwei Zeilen (LSB/MSB-Reihenfolge und
                // C-Operatorpriorität sichergestellt)

oder

x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)
ADMUX   ADC Multiplexer Select Register

Mit diesem Register wird der zu messende Kanal ausgewählt. Beim 90S8535 kann jeder Pin von Port A als ADC-Eingang verwendet werden (=8 Kanäle).
Das Register ist wie folgt aufgebaut:

Bit 7 6 5 4 3 2 1 0
Name REFS1 REFS0 ADLAR MUX4 MUX3 MUX2 MUX1 MUX0
R/W R/W R/W R/W R/W R/W R/W R/W R/W
Initialwert 0 0 0 0 0 0 0 0

REFS1...REFS0 (ReferenceSelection Bits)

Mit diesen Bits kann die Referenzspannung eingestellt werden. Bei der Umstellung sind Wartezeiten zu beachten, bis die ADC-Hardware einsatzfähig ist (Datenblatt und [1]):
REFS1 REFS0 Referenzspanung
0 0 Externes AREF
0 1 AVCC als Referenz
1 0 Reserviert
1 1 Interne 2,56 Volt
Beim ATtiny25, ATtiny45 und ATtiny85 gilt:
REFS2 REFS1 REFS0 Referenzspanung
X 0 0 VCC als Referenz
X 0 1 Externes AREF
X 1 0 Interne 1,1 Volt
X 1 1 Reserviert
1 1 0 Interne 2,56 Volt
1 1 1 Interne 2,56 Volt mit bypass Kapazität am AREF Pin


ADLAR (ADC Left Adjust Result)

Das ADLAR Bit verändert das Aussehen des Ergebnisses der AD-Wandlung. Bei einer logischen 1 wird das Ergebnis linksbündig ausgegeben, bei einer 0 rechtsbündig. Eine Änderung in diesem Bit beeinflusst das Ergebnis sofort, ganz egal ob bereits eine Wandlung läuft.


MUX4...MUX0

Mit diesen 5 Bits wird der zu messende Kanal bestimmt. Wenn man einen einfachen 1-kanaligen ADC verwendet wird einfach die entsprechende Nummer des ADC-Ports (z.B. 0010 -> ADC2) in die Bits 0...4 eingeschrieben (je nach Anzahl der Wandler des AVR, bei 8 AD-Kanälen halt nur 0...2).
Wenn das Register beschrieben wird, während eine Umwandlung läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu berücksichtigen.
Eine Empfehlung ist deswegen diese, dass der frei laufende Betrieb nur bei einem einzelnen zu verwendenden Analogeingang verwendet werden sollte, wenn man sich Probleme bei der Umschalterei ersparen will.

Nutzung des ADC[Bearbeiten]

Um den ADC zu aktivieren, müssen wir das ADEN-Bit im ADCSRA-Register setzen. Im gleichen Schritt legen wir auch die Betriebsart fest.

Im Folgenden ein kleines Beispiel für den "single conversion"-Mode bei einem ATmega169 und Nutzung der internen Referenzspannung (beim '169 1,1V bei anderen AVRs auch 2,56V, vgl. Datenblatt). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls muss es mit einem Spannungsteiler verringert werden. Das Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1023 für V_ref-Volt. Beim Programmstart wir der ADC konfiguriert und aktiviert und dann auf verschiedenen Kanälen die Spannung gemessen. Initialisierung des ADC und die Wandlungen sollte man trennen, denn das Einschalten des ADC und vor allem der Referenzspannung dauert ein paar Dutzend Mikrosekunden. Außerdem ist das erste Ergebnis nach dem Einschalten ungültig und muss verworfen werden.

// Diese Beispiel zeigt die Anwendung des ADC eines ATmega169
// unter Verwendung der internen Referenzspannung von nominell 1,1V.
// Zur Anpassung an andere AVR und/oder andere Referenzspannungen
// siehe Erläuterungen in diesem Tutorial und im Datenblatt

/* ADC initialisieren */
void ADC_Init(void)
{
  // die Versorgungsspannung AVcc als Referenz wählen:
  ADMUX = (1<<REFS0);    
  // oder interne Referenzspannung als Referenz für den ADC wählen:
  // ADMUX = (1<<REFS1) | (1<<REFS0);

  // Bit ADFR ("free running") in ADCSRA steht beim Einschalten
  // schon auf 0, also single conversion
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren

  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */

  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung 
  while (ADCSRA & (1<<ADSC) ) {         // auf Abschluss der Konvertierung warten
  }
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
     Wandlung nicht übernommen. */
  (void) ADCW;
}

/* ADC Einzelmessung */
uint16_t ADC_Read( uint8_t channel )
{
  // Kanal waehlen, ohne andere Bits zu beeinflußen
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
  while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung warten
  }
  return ADCW;                    // ADC auslesen und zurückgeben
}

/* ADC Mehrfachmessung mit Mittelwertbbildung */
/* beachte: Wertebereich der Summenvariablen */
uint16_t ADC_Read_Avg( uint8_t channel, uint8_t nsamples )
{
  uint32_t sum = 0;

  for (uint8_t i = 0; i < nsamples; ++i ) {
    sum += ADC_Read( channel );
  }

  return (uint16_t)( sum / nsamples );
}

...

/* Beispielaufrufe: */

int main( void )
{
  uint16_t adcval;
  ADC_Init();

  while( 1 ) {
    adcval = ADC_Read(0);  // Kanal 0
    // mach was mit adcval

    adcval = ADC_Read_Avg(2, 4);  // Kanal 2, Mittelwert aus 4 Messungen
    // mach was mit adcval
  }
}

Im Beispiel läuft der ADC ständig. Für den Fall, dass man Strom sparen will, z.B. mittels Verwendung des Sleep Modes, muss man den ADC nach jeder Messung abschalten und vor der nächsten Messung wieder einschalten, wobei auch dann wieder eine kleine Pause und Anfangswandlung nötig sind.


Externe Referenzspannung[Bearbeiten]

Die minimale (externe) Referenzspannung des ADC darf nicht beliebig niedrig sein, vgl. dazu das (aktuelle) Datenblatt des verwendeten Controllers. z. B. beim ATmega8 darf sie laut Datenblatt[1] 2,0V nicht unterschreiten.[2]

Meiner eigenen Erfahrung nach kann man aber (auf eigene Gefahr und natürlich nicht für Seriengeräte) durchaus noch ein klein wenig weiter heruntergehen, bei dem von mir unter die Lupe genommenen ATmega8L (also die Low-Voltage-Variante) funktioniert der ADC bei 5V Betriebsspannung mit bis zu VREF=1,15V hinunter korrekt, ab 1,1V und darunter digitalisiert er jedoch nur noch Blödsinn). Ich würde sicherheitshalber nicht unter 1,5V gehen und bei niedrigeren Betriebsspannungen mag sich die Untergrenze für VREF am Pin AREF ggf. nach oben verschieben.

In der letzten Revision des Datenblatts ist außerdem korrigiert, dass ADC4 und ADC5 sehr wohl 10 Bit Genauigkeit bieten — und nicht bloß 8 Bit, wie in älteren Revisionen irrtümlich angegeben.

Analog-Digital-Wandlung ohne internen ADC[Bearbeiten]

Messen eines Widerstandes[Bearbeiten]

Poti.gif

Analoge Werte lassen sich ohne Analog-Digital-Wandler auch indirekt ermitteln. Im Folgenden wird die Messung des an einem Potentiometer eingestellten Widerstands anhand der Ladekurve eines Kondensators erläutert. Bei dieser Methode wird nur ein Portpin benötigt, ein Analog-Digital-Wandler oder Analog-Comparator ist nicht erforderlich. Es wird dazu ein Kondensator und der Widerstand (das Potentiometer) in Reihe zwischen Vorsorgungsspannung und Masse/GND geschaltet (sogen. RC-Netzwerk). Zusätzlich wird eine Verbindung der Leitung zwischen Kondensator und Potentiometer zu einem Portpin des Controllers hergestellt. Die folgende Abbildung verdeutlicht die erforderliche Schaltung.

Wird der Portpin des Controllers auf Ausgang konfiguriert (im Beispiel DDRD |= (1<<PD2)) und dieser Ausgang auf Logisch 1 ("High", PORTD |= (1<<PD2)) geschaltet, liegt an beiden "Platten" des Kondensators das gleiche Potential VCC an und der Kondensator somit entladen. (Klingt komisch, mit Vcc entladen, ist aber so, da an beiden Seiten des Kondensators das gleiche Potential anliegt und somit eine Potentialdifferenz von 0V besteht => Kondensator ist entladen).

Nach einer gewissen Zeit ist der Kondensator entladen und der Portpin wird als Eingang konfiguriert (DDRD &= ~(1<<PD2); PORTD &= ~(1<<PD2)), wodurch dieser hochohmig wird. Der Status des Eingangspin (in PIND) ist Logisch 1 (High). Der Kondensator lädt sich jetzt über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti unter die Threshold-Spannung des Eingangspins (2/5 Vcc, also ca. 2V), wird das Eingangssignal als LOW erkannt (Bit in PIND wird 0). Die Zeitspanne zwischen der Umschaltung von Entladung auf Aufladung und dem Wechsel des Eingangssignals von High auf Low ist ein Maß für den am Potentiometer eingestellten Widerstand. Zur Zeitmessung kann einer der im Controller vorhandenen Timer genutzt werden. Der 220Ω Widerstand dient dem Schutz des Controllers. Es würde sonst bei Maximaleinstellung des Potentionmeters (hier 0Ω) ein zu hoher Strom fließen, der die Ausgangsstufe des Controllers zerstört.

Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B: 0...100 % oder so) umzurechnen.


ADC über Komparator[Bearbeiten]

ADC ueber Komparator.gif

Es gibt einen weiteren Weg, eine analoge Spannung mit Hilfe des Komparators, welcher in fast jedem AVR integriert ist, zu messen. Siehe dazu auch die Application Note AVR400 von Atmel.

Dabei wird das zu messende Signal auf den invertierenden Eingang des Komparators geführt. Zusätzlich wird ein Referenzsignal an den nicht invertierenden Eingang des Komparators angeschlossen. Das Referenzsignal wird hier auch wieder über ein RC-Glied erzeugt, allerdings mit festen Werten für R und C.

Das Prinzip der Messung ist nun dem vorhergehenden recht ähnlich. Durch Anlegen eines LOW-Pegels an Pin 2 wird der Kondensator zuerst einmal entladen. Auch hier muss darauf geachtet werden, dass der Entladevorgang genügend lang dauert. Nun wird Pin 2 auf HIGH gelegt. Der Kondensator wird geladen. Wenn die Spannung über dem Kondensator die am Eingangspin anliegende Spannung erreicht hat, schaltet der Komparator durch. Die Zeit, welche benötigt wird, um den Kondensator zu laden, kann nun auch wieder als Maß für die Spannung an Pin 1 herangezogen werden.

Ich habe es mir gespart, diese Schaltung auch aufzubauen, und zwar aus mehreren Gründen:

  1. 3 Pins notwendig.
  2. Genauigkeit vergleichbar mit einfacherer Lösung.
  3. War einfach zu faul.

Der Vorteil dieser Schaltung liegt allerdings darin, dass damit direkt Spannungen gemessen werden können.

DAC (Digital Analog Converter)[Bearbeiten]

Mit Hilfe eines Digital-Analog-Konverters (DAC) können wir nun auch Analogsignale ausgeben. Es gibt hier mehrere Verfahren.

DAC über mehrere digitale Ausgänge[Bearbeiten]

Wenn wir an den Ausgängen des Controllers ein entsprechendes Widerstandsnetzwerk aufbauen haben wir die Möglichkeit, durch die Ansteuerung der Ausgänge über den Widerständen einen Addierer aufzubauen, mit dessen Hilfe wir eine dem Zahlenwert proportionale Spannung erzeugen können. Das Schaltbild dazu kann etwa so aussehen:

DAC R2R.gif

Es sollten selbstverständlich möglichst genaue Widerstände verwendet werden, also nicht unbedingt solche mit einer Toleranz von 10% oder mehr. Weiterhin empfiehlt es sich, je nach Anwendung den Ausgangsstrom über einen Operationsverstärker zu verstärken.

PWM (Pulsweitenmodulation)[Bearbeiten]

Wir kommen nun zu einem Thema, welches in aller Munde ist, aber viele Anwender verstehen nicht ganz, wie PWM eigentlich funktioniert.

Wie wir alle wissen, ist ein Mikrocontroller ein rein digitales Bauteil. Definieren wir einen Pin als Ausgang, dann können wir diesen Ausgang entweder auf HIGH setzen, worauf am Ausgang die Versorgungsspannung Vcc anliegt, oder aber wir setzen den Ausgang auf LOW, wonach dann 0V am Ausgang liegt. Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen HIGH und LOW umschalten? - Richtig, wir erhalten eine Rechteckspannung, wie die folgende Abbildung zeigt:

PWM Theorie 1.gif

Diese Rechteckspannung hat nun einen arithmetischen Mittelwert, der je nach Pulsbreite kleiner oder größer ist.

PWM Theorie 2.gif

Wenn wir nun diese pulsierende Ausgangsspannung noch über ein RC-Glied filtern/"glätten", dann haben wir schon eine entsprechende Gleichspannung erzeugt.

Mit den AVRs können wir direkt PWM-Signale erzeugen. Dazu dient der 16-Bit Zähler, welcher im sogenannten PWM-Modus betrieben werden kann.

Hinweis
In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt, weshalb ein Blick ins entsprechende Datenblatt dringend angeraten wird.

Um den PWM-Modus zu aktivieren, müssen im Timer/Counter1 Control Register A TCCR1A die Pulsweiten-Modulatorbits PWM10 bzw. PWM11 entsprechend nachfolgender Tabelle gesetzt werden:

PWM11 PWM10 Bedeutung
0 0 PWM-Modus des Timers ist nicht aktiv
0 1 8-Bit PWM
1 0 9-Bit PWM
1 1 10-Bit PWM

Der Timer/Counter zählt nun permanent von 0 bis zur Obergrenze und wieder zurück, er wird also als sogenannter Auf-/Ab Zähler betrieben. Die Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:

Auflösung Obergrenze Frequenz
8 255 fTC1 / 510
9 511 fTC1 / 1022
10 1023 fTC1 / 2046

Zusätzlich muss mit den Bits COM1A1 und COM1A0 desselben Registers die gewünschte Ausgabeart des Signals definiert werden:

COM1A1 COM1A0 Bedeutung
0 0 Keine Wirkung, Pin wird nicht geschaltet.
0 1 Keine Wirkung, Pin wird nicht geschaltet.
1 0 Nicht invertierende PWM.

Der Ausgangspin wird gelöscht beim Hochzählen und gesetzt beim Herunterzählen.

1 1 Invertierende PWM.

Der Ausgangspin wird gelöscht beim Herunterzählen und gesetzt beim Hochzählen.

Der entsprechende Befehl, um beispielsweise den Timer/Counter als nicht invertierenden 10-Bit PWM zu verwenden, heißt dann:

alte Schreibweise (PWMxx wird nicht mehr akzeptiert)

TCCR1A = (1<<PWM11)|(1<<PWM10)|(1<<COM1A1);

neue Schreibweise

TCCR1A = (1<<WGM11)|(1<<WGM10)|(1<<COM1A1);

Damit der Timer/Counter überhaupt läuft, müssen wir im Control Register B TCCR1B noch den gewünschten Takt (Vorteiler) einstellen und somit auch die Frequenz des PWM-Signals bestimmen.

CS12 CS11 CS10 Bedeutung
0 0 0 Stop. Der Timer/Counter wird gestoppt.
0 0 1 CK
0 1 0 CK / 8
0 1 1 CK / 64
1 0 0 CK / 256
1 0 1 CK / 1024
1 1 0 Externer Pin 1, negative Flanke
1 1 1 Externer Pin 1, positive Flanke

Also um einen Takt von CK / 1024 zu generieren, verwenden wir folgenden Befehl:

TCCR1B = (1<<CS12) | (1<<CS10);

Jetzt muss nur noch der Vergleichswert festgelegt werden. Diesen schreiben wir in das 16-Bit Timer/Counter Output Compare Register OCR1A.

OCR1A = xxx;

Die folgende Grafik soll den Zusammenhang zwischen dem Vergleichswert und dem generierten PWM-Signal aufzeigen.

PWM Theorie 3.gif

PWM Theorie 4.gif

Ach ja, fast hätte ich's vergessen. Das generierte PWM-Signal wird am Output Compare Pin OC1 des Timers ausgegeben und leider können wir deshalb auch beim AT90S2313 nur ein einzelnes PWM-Signal mit dieser Methode generieren. Andere AVR-Typen verfügen über bis zu vier PWM-Ausgänge. Zu beachten ist außerdem, das wenn der OC Pin aktiviert ist, er nichtmehr wie üblich funktioniert und z. B. nicht einfach über PINx ausgelesen werden kann.

Ein Programm, welches an einem ATmega8 den Fast-PWM Modus verwendet, den Modus 14, könnte so aussehen

#include <avr/io.h>

int main()
{
  // OC1A auf Ausgang
  DDRB = (1 << PB1 );  //ATMega8
  // DDRD = (1 << PD5 ); //ATMega16
  //
  // Timer 1 einstellen
  //  
  // Modus 14:
  //    Fast PWM, Top von ICR1
  //
  //    WGM13    WGM12   WGM11    WGM10
  //      1        1       1        0
  //
  //    Timer Vorteiler: 1
  //     CS12     CS11    CS10
  //       0        0       1
  //
  //  Steuerung des Ausgangsport: Set at BOTTOM, Clear at match
  //     COM1A1   COM1A0
  //       1        0
 
  TCCR1A = (1<<COM1A1) | (1<<WGM11);
  TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS10);
 
  //  den Endwert (TOP) für den Zähler setzen
  //  der Zähler zählt bis zu diesem Wert

  ICR1 = 0x6FFF;
 
  // der Compare Wert
  // Wenn der Zähler diesen Wert erreicht, wird mit
  // obiger Konfiguration der OC1A Ausgang abgeschaltet
  // Sobald der Zähler wieder bei 0 startet, wird der
  // Ausgang wieder auf 1 gesetzt
  //
  // Durch Verändern dieses Wertes, werden die unterschiedlichen
  // PWM Werte eingestellt.

  OCR1A = 0x3FFF;

  while (1) {}
}
PWM-Mode Tabelle aus dem Datenblatt des ATmega8515
Mode WGM13 WGM12 WGM11 WGM10 Timer/Counter Mode of Operation TOP Update of OCR1x at TOV1 Flag set on
0 0 0 0 0 Normal 0xFFFF Immediate MAX
1 0 0 0 1 PWM, Phase Correct, 8-Bit 0x00FF TOP BOTTOM
2 0 0 1 0 PWM, Phase Correct, 9-Bit 0x01FF TOP BOTTOM
3 0 0 1 1 PWM, Phase Correct, 10-Bit 0x03FF TOP BOTTOM
4 0 1 0 0 CTC OCR1A Immediate MAX
5 0 1 0 1 Fast PWM, 8-Bit 0x00FF BOTTOM TOP
6 0 1 1 0 Fast PWM, 9-Bit 0x01FF BOTTOM TOP
7 0 1 1 1 Fast PWM, 10-Bit 0x03FF BOTTOM TOP
8 1 0 0 0 PWM, Phase an Frequency Correct ICR1 BOTTOM BOTTOM
9 1 0 0 1 PWM, Phase an Frequency Correct OCR1A BOTTOM BOTTOM
10 1 0 1 0 PWM, Phase Correct ICR1 TOP BOTTOM
11 1 0 1 1 PWM, Phase an Frequency Correct OCR1A TOP BOTTOM
12 1 1 0 0 CTC ICR1 Immediate MAX
13 1 1 0 1 Reserved - - -
14 1 1 1 0 Fast PWM ICR1 BOTTOM TOP
15 1 1 1 1 Fast PWM OCR1A BOTTOM TOP

Für Details der PWM-Möglichkeiten muss immer das jeweilige Datenblatt des Prozessors konsultiert werden, da sich die unterschiedlichen Prozessoren in ihren Möglichkeiten doch stark unterscheiden. Auch muss man aufpassen, welches zu setzende Bit in welchem Register ist. Auch hier kann es sein, dass gleichnamige Konfigurationsbits in unterschiedlichen Konfigurationsregistern (je nach konkretem Prozessortyp) sitzen.

Anmerkungen[Bearbeiten]

  1. ATmega8: S.245, Tabelle 103, Zeile "VREF"
  2. Diese Information findet sich erst in der letzten Revision (Rev. 2486O-10/04) des Datenblatts.