Im vorherigen Abschnitt haben wir einen Überblick über die Einstellungsmöglichkeiten und die Initialisierung der A/D-Wandlung kennengelernt. Im Folgenden wollen wir uns etwas mehr mit dem konkreten Einsatz des A/D-Wandlers in der Praxis befassen. Angefangen mit einem einfachen Beispiel wird im zweiten Abschnitt dann eine komplexe Steuerung inklusive Interrupts und Energiesparmodus erklärt.
In diesem einfachen Beispiel werden wir die A/D-Wandlung in einer Endlosschleife ausführen. Daran lässt sich die Funktionalität des A/D-Wandlers sehr gut verdeutlichen. Für energieeffiziente Anwendungen eignet sich dieses Beispiel jedoch nicht, doch dazu mehr in Kapitel 5.4.2.
Zuerst müssen wir den A/D-Wandler und die Eingänge konfigurieren. Unser Beispielprozessor ist wieder der MSP430F1612. Der A/D-Wert soll am Port P6.0 (also Kanal A0) erfasst werden. Dazu muss im P6SEL-Register die Spezialfunktion für P6.0 aktiviert werden. Als Referenzspannung wird die interne 2.5V-Spannung gewählt. Wir dürfen natürlich nicht vergessen, den Referenzspannungsgenerator mit REFON zu aktivieren. Für eine optimierte Auflösung wählen wir die maximale Sample-and-Hold-Time aus (SHT0_15).
Listing 5.5: | Initialisierung der A/D-Wandlung |
Bisher haben wir nur den A/D-Wandler initialisiert. Beim ADC12 haben wir insgesamt 15 Ergebnisregister für die A/D-Wandler. Jedem Einzelnen können wir einen anderen Eingangskanal zuordnen.
Mit der Anweisung
haben wir gleichzeitig die Bits CSTARTADDx = 0 gesetzt. Damit legen wir fest, dass wir bei einer einzigen A/D-Wandlung das Ergebnis in das ADC12MEM0- Register speichern. Wir müssen nun den Kanal des Registers und die dazu gehörende Referenzspannung auswählen:
Listing 5.6: | Auswahl des A/D-Kanals und der Referenzspannung |
Wir wählen dadurch als Referenzspannung die interne Referenzspannung mit den vorher initialisierten 2.5V aus. Mit INCH_0 wird das ADC12MEM0-Register mit dem Kanal A0, also mit dem Port P6.0, verknüpft. Jetzt können wir kontinuierlich einzelne A/D-Werte in einer Endlosschleife erfassen.
Listing 5.7: | Initialisierung der A/D-Wandlung |
Schließen wir jetzt einen Potentiometer, wie in Bild 5.20 gezeigt, an den richtigen Einganspin an, so können wir auf der Basis des eben entwickelten Programms die Spannung am Potentiometerabgriff messen. Da wir die 2.5V-Referenzspannung auswählen, müssen wir die maximale Eingangsspannung durch einen Spannungsteiler auf Umax = 3.3V ⋅ 10k∕(10k + 3k3) = 2.48V limitieren.
Möchte man die Spannung in Millivolt ausgeben, so muss der Ergebniswert noch zusätzlich skaliert werden, wie in dem nun folgenden Beispiel. Numerisch korrekt müsste man UAD = ADWert∕4095 * 2500 berechnen. Dies beinhaltet jedoch eine Division, die umfangreich zu berechnen ist. Eine einfache Näherung ist dafür UAD = ADWert * (10∕16 + 1∕64). Damit erreicht man einen maximalen relativen Fehler von 2 o, der in etwa auch der maximalen Rechengenauigkeit der Integer-Arithmetik entspricht. Gleichzeitig spart man aber viele Rechenschritte, da die Division vom Compiler durch eine Shift-Operation ersetzt wird. Das Ergebnis kann mit dem Debugger durch Auslesen der Variablen U_ad kontrolliert werden.
Listing 5.8: | Umrechnung des A/D-Wandler-Ergebnisses in Millivolt |
Im folgenden Beispiel soll über einem Drehpoti die Blinkfrequenz einer an P1.0 angeschlossenen Low-Current-Diode eingestellt werden können. Die Schaltung ist in Bild 5.21 dargestellt.
Das Blinken der LED wird mit Hilfe des Timers A im Up-Mode realisiert. Dafür wird der Wert der A/D-Wandlung in das Register TACCR0 kopiert, mit dem die Überlauf-Schwelle des Timers und damit auch die Blinkfrequenz festgelegt wird. Der Timer inkrementiert das Zählregister, bis der Wert TACCR0 erreicht wurde und ruft dann einen Interrupt auf. In der Interrupt-Service-Routine des Timers A wird die LED dann getoggelt. Um das Programm energiesparend auslegen zu können, wählen wir für den Timer A als Taktquelle ACLK, die wir auch für das ADC12-Modul verwenden. Das komplette Beispielprogramm wird in Listing 5.9 vorgestellt. Bei niedrigen Spannungen über R2 blinkt die LED so schnell, dass das Ergebnis nur mit einem Oszilloskop sichtbar ist.
Listing 5.9: | Beispielprogramm für das ADC12-Modul |
Wir werden im folgenden einfachen Beispiel über den Port P1.2 ein analoges Signal mit einem Interrupt einlesen und auswerten. Dafür definieren wir zunächst die Interrupt-Service-Routine (Listing 5.10).
Listing 5.10: | Interrupt-Service-Routine für SD16_A |
Eine LED am Port P1.0 soll bei einer Spannung über 0.5V leuchten. Bei der Verwendung der internen Referenzspannung beträgt die maximale Eingangsspannung 0.6V, was einem A/D-Wert von 65535 entspricht. Den korrespondierenden A/D-Wert für den Grenzwert von 0.5V berechnen wir durch die Multiplikation der Auflösung 65535∕0.6V mit 0.5V:
Nun fehlen noch die Einstellungen für den SD16_ A. Zunächst wählen wir mit dem Register SD16CTL die Taktquelle aus und aktivieren den internen Referenzspannungsgenerator. Als Eingangskanal wird im Register SD16INCTL0 der Kanal A1 (A1+ = P1.2) ausgewählt. Für den unipolaren Betrieb (SD16CCTL0 = SD16UNI) wird A1 automatisch auf Masse gelegt. Gleichzeitig aktivieren wir die Interrupt-Funktion für denSD16_ A im SD16CCTL0-Register. Die Einstellungen sind Listing 5.11 zu entnehmen.
Listing 5.11: | Einstellungen für den SD16_ A |
Die Referenzspannung kann für jeden Kanal unabhängig mit dem Register SD16AE festgelegt werden. Für den Kanal A1 muss daher SD16AE = SD16AE1 gesetzt werden. Alle anderen Kanäle werden darüber hinaus durch diese Zeile deaktiviert. Als komplettes Listing erhalten wir dann für das Interrupt-Beispiel mit dem SD16_ A Listing 5.12.
Listing 5.12: | Interrupt-Beispiel für den SD16_A |