6.2  Einfacher 8-Bit Single-Slope-A/D-Wandler

Wir verwenden den Comparator A nun für ein Millivolt-Meter. Dazu muss der Mikrocontroller zunächst nach Bild 6.5 beschaltet werden. Die Funktionsweise eines Single-Slope-A/D-Wandlers haben wir ja in Kapitel 5.2.3 kennengelernt.


PIC

Bild 6.5.: Beschaltung des MSP430F1612 für ein Millivolt-Meter


Für die A/D-Wandlung wird der Kondensator über den Widerstand durch Setzen des Pins P1.0 auf HIGH geladen. Damit steigt die Spannung über dem Kondensator, die über den Pin P2.3 am positiven Eingang des Comparator-A-Moduls ausgewertet werden kann. An den negativen Eingang (Pin P2.4) wird das analoge Eingangssignal angeschlossen. So lange der Ausgang des Komparators (das Bit CAOUT) 0 ist, ist die Spannung über der Kapazität kleiner als das analoge Eingangssignal. Währenddessen wird mit dem Timer A die Zeit zum Aufladen des Kondensators gemessen. Wenn CAOUT den Zustand auf 1 wechselt, ist die Spannung über dem Kondensator gerade etwas größer als die analoge Eingangsspannung, und der Timer A wird gestoppt. Mit dem Ergebnis aus dem Zählregister des Timers A kann nun der A/D-Wert und damit die Eingangsspannung bestimmt werden.


PIC

Bild 6.6.: Exponentieller Verlauf der Spannung über dem Kondensator UC normiert auf die maximale Spannung UP1.0 aufgetragen über der Zeit t.


Während der A/D-Wandlung muss sichergestellt sein, dass sich das analoge Eingangssignal nicht ändert. Dies ist notwendig, da wir keine Sample-and-Hold-Schaltung vorgesehen haben. Als einfachste Maßnahme sollte zumindest ein Tiefpassfilter am analogen Eingang vorgesehen werden, dessen Grenzfrequenz deutlich unterhalb der längsten Wandlungszeit (fg 12πTmax 13Hz) liegt.

Für eine genaue Berechnung des Spannungswertes müssen wir berücksichtigen, dass die Spannung über dem Kondensator und die Zeit in einem nichtlinearen Zusammenhang stehen (Bild 6.6):

U (t)           -t
-C---- =  1 - e-τ   mit  τ = R ⋅C                   (6.1)
UP1.0

Eine Möglichkeit wäre, die Exponentialfunktion mit einer Reihenentwicklung über i zu approximieren und auf dem MSP in C zu implementieren:

               ∞  (   )i         ( )2   (  )3
UC(t)-        ∑   --τt--    t-  -tτ---  -τt--
UP1.0  =  1 -       i!   = - τ -   2  +   6  + ...           (6.2)
              i=0
Da uns auch hier die Festkomma-Arithmetik stark limitiert, wollen wir auf eine weitere Möglichkeit der Implementierung komplexer Funktionen zurückgreifen: eine sogenannte Look-Up-Tabelle. In dieser sind zu (allen) möglichen Eingangswerten der Funktion die Ausgangswerte vorberechnet und im Speicher abgelegt.

Wir werden nun einen A/D-Wandler mit einer Auflösung von 8 Bit entwickeln. Die kleinste messbare Spannung beträgt dann bei einer maximalen Amplitude von 3.3V etwa 13mV. Wir stellen nun Gleichung 6.1 nach t um und berechnen die Zeiten für die 256 Spannungsschritte:

U(n) =           n *0.01289                           (6.3)
T(n) =         - τln(1- U3,(n3)V )                        (6.4)
                       - 9      -n-
     =  - 10000 ⋅220⋅10   ln (1 - 256)                  (6.5)

Diese Tabelle werden wir in unserem Code als Look-Up-Tabelle verwenden. Zudem müssen wir noch die Zeiten in die Zählerstände des Timers A mit Bezug zur realen Zählerfrequenz umrechnen. In unserem Beispiel soll diese Frequenz 1MHz betragen.

TT imer(n) = T(n )∕0.000001                         (6.6)

Im Listing 6.1 ist die Look-Up-Tabelle für die Auswertung des Comparator-A-Eingangs implementiert.

#include <msp430x16x.h> 
 
int adc_messen() 
{ 
    int t, ad_wert, i; 
 
    // LookUp-Tabelle als const, damit die Daten im Flash stehen 
    const int LookUp[256] = {0, 9, 17, 26, 35, 43, 52, 61, 70, 
       79, 88, 97, 106, 115, 124, 133, 142, 151, 160, 170, 
        ... hier stehen noch die anderen Werte ... 
        8653, 9143, 9773, 10661, 12172}; 
 
    CACTL1 |= CAON;               // Komparator einschalten 
 
    // zuerst müssen wir einen definierten Ausgangszustand 
    // herstellen und den Kondensator entladen. 
    CACTL2 = P2CA0 + CAF; 
    t = 0; 
    P1OUT = 0x00; 
    while(t < 32000) { 
        t++; 
    } 
 
    // Messung vorbereiten 
    CACTL2 = P2CA0 + P2CA1 + CAF; // verbinde Eingänge 
    TAR = 0; 
 
    // jetzt die Messung beginnen 
    P1OUT = 0x01;   // Kondensator laden 
    TACTL |= MC_2;   // Timer starten 
 
    // Warte, so lange die Spannung über dem Kondensator 
    // nicht größer als die Messspannung ist 
    while(!(CACTL2 & CAOUT) && (t < 13000)) { 
        t = TAR; 
    } 
 
    TACTL &=~ MC_2; // Timer stoppen 
    t = TAR;        // gemessene "Zeit" speichern 
    ad_wert = 0;    // AD-Wert aus der Look-Up-Tabelle bestimmen 
    while((t > LookUp[ad_wert]) && (ad_wert < 256)) 
        { ad_wert++; } 
    return ad_wert; 
} 
 
int main ( void ) 
{ 
    int i; 
    int ergebnis; 
 
    WDTCTL  = WDTPW | WDTHOLD;  // Watchdog-Timer ausschalten 
 
    BCSCTL2 |= DIVS_3; Timer-Clock langsamer 
 
    // Timer A konfigurieren 
    TACTL = TASSEL_2;           // Auswahl der SMCLK als Quelle 
    TACTL |= TACLR;             // Rücksetzen des Zählers 
 
    // Comparator A konfigurieren 
    CACTL2 = P2CA0 + P2CA1 + CAF; 
    CACTL1 |= CAON; 
    CACTL2 = P2CA0 + CAF; 
    CACTL1 |= CAON; 
 
    // P2.3 als positiver und P2.4 als negativer Eingang 
    P2DIR = 0x00; 
    P2SEL = BIT3+BIT4; 
 
    // Port P1.0 konfigurieren 
    P1DIR = BIT0; 
    P1OUT = 0x00; 
 
    // Endlosschleife, A/D-Wert mit Debugger überprüfen 
    while(1) { 
        ergebnis = adc_messen(); 
    } 
}
Listing 6.1: Einfacher 8-Bit-Single-Slope-A/D-Wandler