PIC-Lernbeispiel: ADC mit Komparator aufbauen

PIC16F628

zurück zu Lernbeispiele , PIC-Prozessoren , Elektronik , Homepage

Einige PICs, die keinen ADC haben, können mit Hilfe des Komparators Spannungen messen
In der Application note AN700 findet sich die Beschreibung eines ADC, der mit dem internen Komparator eines PIC aufgebaut wird. Das ermöglicht es auch dem PIC16F62x Spannungen präzise zu messen.


Funktion eines Delta-Sigma-ADC
Ein Delta-Sigma-ADC besteht aus einem Regelkreis, der in einem festen Takt arbeitet. Der Aufbau ist nachfolgender Schaltung zu entnehmen. Am Beginn eines Taktes wird das Eingangssignal des 1-Bit-DAC festgelegt,. und während des Taktes nicht mehr verändert. Dieser liefert jetzt Vdd oder Vss zur Eingangsstufe. Diese Eingangsstufe ermittelt die Differenz aus dem zu messenden Eingangssignal und dem DAC-Ausgangssignal. Diese Differenzspannung geht zum Integrator, der die Spannung während des Taktes integriert. Das Ausgangssignal des Integrators wird von einem Komparator mit einer Referenzspannung verglichen. Je nachdem, ob die Interatorausgangsspannung größer oder kleiner als die Referenzspannung ist, wird für den folgenden Takt die DAC-Ausgangsspannung mit Vdd oder Vss festgelegt. Das wird nun mehrere hundert oder tausend Takte wiederholt. Dabei wird gezählt, in wievielen Takten der DAC Vdd erzeugte. Das Verhältnis der Vdd-Takte zur Gesamttaktzahl entspricht dem Verhältnis der Eingangsspannung zu Vdd. Der Mittelwert des DAC-Ausgangs (gemittelt über die gesamte Messzeit) muss der Eingangsspannung entsprechen.

Die Auflösung (und damit also die Genauigkeit des ADC) hängt von der Anzahl der Arbeitstakte ab. Für 10-Bit-Auflösung benötigt man 1024 Takte. Die Genauigkeit lässt sich beliebig steigern, allerdings verdoppelt jedes zusätzliche Bit die Anzahl der nötigen Takte und damit auch die Zeit für eine Spannungsmessung.
Der Länge eines einzelnen Taktes sind aber nach unten Grenzen gesetzt. In unserem Fall wird ein Prozessor benutzt, um den Wandlungsprozess zu steuern. Es fallen in jedem Takt einige abzuarbeitende Funktionen an. Deshalb mag 20µs (20 Befehle bei 4 MHz PIC-Takt) bzw. 4µs (20 MHz PIC-Takt) als Untergrenze für die Taktlänge gelten. Damit ergeben sich folgende Wandlungszeiten für verschiedene Auflösungen:
 

Auflösung
Anzahl der Takte
Gesamtzeit bei 4 MHz
Gesamtzeit bei 20 MHz
8 Bit
256
5 ms
1 ms
10 Bit
1024
21 ms
5 ms
12 Bit
4096
82 ms
17 ms
14 Bit
16384
328 ms
66 ms
16 Bit
65536
1,3 s
262 ms

Dieser ADC ist also fast 1000 mal langsamer als der in viele PICs integrierte 10-Bit ADC. Trotzdem ist die Geschwindigkeit für die meisten Anwendungen völlig ausreichend, und man kann sogar eine bessere Genauigkeit erreichen als mit der 'Fertiglösung'.
Wer so einen ADC jenseits der 8 Bit ernsthaft einsetzen möchte, sollte sich um eine sehr stabile Betriebsspannung Gedanken machen, da aus dieser die Referenzspannung und die verwendete RA3-Ausgangsspannung abgeleitet wird.



Prinzip am 16F628

Der PIC16F628 enthält schon viele Teile für einen Delta-Sigma-ADC:

Den Spannungs-Differenzstufe für den Eingang kann man mit 2 Widerständen aufbauen, als Integrator genügt ein Kondensator.
Die verbliebenen Funktionen können mit Software realisiert werden:
Nebenstehendes Bild zeigt den Aufbau des ADC am Beispiel des 16F628. Ignorieren wir mal vorübergehend R1 und die beiden R3

Der Komparator (das grüne Dreieck) vergleicht immer Vref (2,5V aus der internen Referenzspannungsquelle) mit der Spannung am Kondensator. In einem festen Zeitraster (z.B. alle 20 µs) liest die Software C1OUT aus, und setzt PORTA,3 (RA3) auf den entgegengesetzten Wert von C1OUT.

Dadurch wird der Kondensator so weit geladen, bis er Vref erreicht, dann entladen bis er Vref unterschreitet, dann wieder geladen..... Die Spannung an RA0 'eiert' also um Vref herum.
Wir lassen den Prozess 1024 Takte lang laufen und zählen dabei die Low Takte. (die Takte mit RA3=0)

Wenn wie hier Vref genau in der Mitte zwischen Vdd und Vss liegt, dann dann wird (wenn man die Regelschleife lange laufen lässt) die Zahl der Takte in denen RA3 low ist gleich der Anzahl der Takte sein, in der RA3 high ist. Nach 1024 Takten gab es 512 Low-Takte.

Nun erinnern wir uns wieder an die Existenz von R1. Wenn wir über R1 eine Spannung an die Schaltung anschließen, dann stört diese Spannung den Regelkreis nicht, wenn sie genau Vref (=2,5V) ist.

Ist die Eingangsspannung aber kleiner als Vref, dann muss RA3 dadurch gegenwirken, dass er öfter high als low führt. Die Zahl der Low-Zyklen sinkt, und sie sinkt noch mehr, wenn die Eingangsspannung noch kleiner wird.
Fall R1 und R2 gleich groß sind, und die Eingangsspannung Vss (0V) beträgt, dann kann das RA3 nur noch dadurch ausgleichen, das es ständig High bleibt. Es gibt 0 Low-Zyklen.

Ist die Eingangsspannung aber größer als Vref, dann muss RA3 dadurch gegenwirken, dass er seltener high als low führt. Die Zahl der Low-Zyklen steigt, und sie steigt noch mehr, wenn die Eingangsspannung noch größer wird.
Fall R1 und R2 gleich groß sind, und die Eingangsspannung Vdd (5V) beträgt, dann kann das RA3 nur noch dadurch ausgleichen, das es ständig Low bleibt. Es gibt 1024 Low-Zyklen.

Die Zahl der Low-Zyklen ist das Messergebnis der AD-Wandlung. Wir konnten sehen:
 
Eingangsspannung
Messwert
0 V
0
2,5 V
512
5 V
1024

Der ADC wandelt Spannungen von 0 V bis 5 V mit 10 Bit Auflösung.

Der Mittelpunkt des Messbereichs liegt bei Vref, und wird durch die Referenzspannung bestimmt. Die Größe des Messbereichs (5V) . Wird durch die Spannungsbereich von RA3 (5V) und das Verhältnis von R1 zu R2 bestimmt.

Messbereichsgröße = Vdd x R1 / R2
Ist z.B. R1 doppelt so groß wie R2, dann hat RA3 vergleichsweise leichtes Spiel, um die störende Eingangsspannung auszugleichen. Die störende Eingangsspannung muss schon doppelt so groß sein, um den ADC auf 0 bzw 1024 (also auf Anschlag) zu zwingen. Der Messbereich ist dann 10V groß. Das ändert aber nichts daran, das der Mittelpunkt bei Vref=2,5V liegt. So ein ADC würde also den Bereich von -2,5V bis +7,5V wandeln können. Dieser Bereich sieht nun doch etwas unsymmetrisch aus.

Wem das nicht gefällt, der muss sich nun an die Existenz von R3 erinnern. Es gibt in der Realität nur einen R3, entweder den grünen oder den roten. Mit R3 lässt sich der ganze Messbereich des ADC zu höheren oder zu niedrigeren Spannungen verschieben. Er bringt eine konstante Störgröße in die Arbeitsschleife. Wie weit der R3 den Bereichsmittelpunkt von Vref wegzieht, das hängt vom Verhältnis von R3 und R2 ab - die beiden kämpfen ja gegeneinander.

Mitte = Vref (1+R1/R3)    ,  für den roten R3 nach Vss

Mitte = Vref (1-R1/R3)     , für den blauen R3 nach Vdd

Den verlockenden Gedanken, einfach Vref zu ändern, sollte man schnell wieder vergessen. Der ADC arbeitet nur linear, wenn Vref genau in der Mitte zwischen low und high des RA3 liegt - also bei 2,5V.


Schaltung
Bauen wir mal einen 10-Bit-ADC für den Spannungsbereich von 0V bis 5V.

Der PIC16F628 benötigt also 2 Pins (z.B. RA0 & RA3), zwei Widerstände und einen Kondensator. (R3 ist nicht nötig)

RA3 ist der Ausgang des 1-Bit DAC. Er liefert Vdd oder Vss durch R2 zum Verbindungspunkt von R1&R2. Dorthin speist R1 auch die Eingangsspannung Ux. Der Mittelwert aus beiden Spannungen wird in C1 integriert. Die integrierte Spannung gelangt an RA0, den Minus-Eingang des Komparators C1. (Dummerweise haben der Kondensator und der Kompensator in diesem Beispiel die gleiche Bezeichnung.) An den Plus-Eingang des Komparators ist die interne Referenzspannungsquelle mit Uref = 2,5V angeschlossen.

Im Gegensatz zum obigen Prinzip-Plan ist der Eingang keine Differenz- sondern eine Summen-Stufe. Das wird durch inverse Ansteuerung des DAC ausgeglichen.

ADC für den Bereich 0V ... 5V


Kalibrierung

Dieser ADC-Typ muss kalibriert werden, da sein Messbereich und sein Nullpunkt von Referenzspannung, Betriebsspannung und den beiden Widerständen abhängen.

Die Größe des Messbereichs (also die Differenz zwischen kleinster und größter Eingangsspannung) hängt vom Spannungshub an RA3 und vom Verhältnis der beiden Widerstände R1 und R2 ab. Da RA3 MOSFET-Treiber hat und nicht nennenswert belastet wird, kann man von einem Hub von Vdd ausgehen:

Messbereichsgröße = Vdd x R1 / R2
Wenn R1 & R2 genau gleich groß sind, ist der Messbereich 5V groß (Vdd=5V). Andere Messbereiche lassen sich durch Variation der Widerstände erreichen.

Der Mittelpunkt des Messbereichs liegt auf dem Wert der Referenzspannung. Sie muss für einen 0V...5V-Messbereich also genau 2,5V betragen. Leider kann die Referenzspannung exemplarabhängig um über 50 mV streuen. Da hilft dann eine externe Referenzspannung (aus einem mit Vdd/Vss gespeistem Potentiometer) oder eine Kalibrierung per Software.

Mein Versuchsexemplar erzeugt z.B. eine etwas zu kleine Referenzspannung von 2,42V. Dadurch liegt der Nullpunkt des ADC bei -0,12 V und der Endanschlag ist bei 4,96V. Dass der Messbereich also etwas größer als 5V (4,96V +0,12V = 5,08V) ist liegt an den beiden Widerständen. Offensichtlich ist bei mir R2 eine Winzigkeit größer als R1.


Programmablauf


Programmlisting

        list p=16f628
;**************************************************************
;*      Pinbelegung
;*      ---------------------------------- 
;*      PORTA:  0 <---------+----+--R1---o U?
;*              1 -         I    I
;*              2 x-Vref    I    C 
;*              3 >----R2---+    I
;*              4 -               Vss 
;*              5 - MCLR
;*              6 - OSC2
;*              7 - OSC1
;*
;*      PORTB:  0 > LED
;*              1 > LED
;*              2 > LED
;*              3 > LED
;*              4 > LED
;*              5 > LED
;*              6 > LED
;*              7 > LED
;*
;* R1 =  47 k
;* R2 =  47 k
;* C  = 100 nF 
;* 
;**************************************************************
;
;sprut (zero) Bredendiek 01/2003
;
; ADC mit Comparator 
;
; Prozessor 16F628 
;
; Prozessor-Takt 4 MHz
;
; LED-Zeile am PortB
;
;**************************************************************

; der code füpr die eigentliche Wandlung wurde aus AN700 übernommen
;
;**************************************************************
; Includedatei für den 16F628 einbinden

        #include <P16f628.INC>

        ERRORLEVEL      -302            ; SUPPRESS BANK SELECTION MESSAGES
 

; Configuration festlegen:
; Power on Timer, kein Watchdog, HS-Oscillator, kein Brown out, kein LV-programming
        __CONFIG        _PWRTE_ON & _WDT_OFF & _HS_OSC & _BODEN_OFF & _LVP_OFF

; Constanten festlegen
#define LED     PORTB

; Variable festlegen
counter         equ     0x20            ; 
result_l        equ     0x22            ; 
result_h        equ     0x23            ; 
 

;**************************************************************
; Programmanfang

        org     0

;**************************************************************
; Initialisierung *********************************************

init
        bsf     STATUS, RP0             ; Bank 1
        clrf    OPTION_REG 
        clrf    TRISB                   ; PortB alle outputs 
        bcf     STATUS, RP0             ; Bank 0
        clrf    PORTB                   ; LEDs aus
        clrf    INTCON                  ; Interrupt disable
        call    InitCAdc                ; ADC vorbereiten

mainloop
        call    CAdc                    ; wandeln
        movfw   result_l
        movwf   PORTB                   ; Low-8-Bit an PORTB anzeigen
        goto    mainloop
 

;**************************************************************
; ADC-code aus AN700*******************************************
; initialisierung des adc**************************************
InitCAdc
        bsf     STATUS, RP0
        movlw   0xEC            ; Vref ein, Vref zu RA2
        movwf   VRCON           ; LowRangeMode, VR = 12 = 2,5 V
        bcf     TRISA, 3        ; RA3 output des 'DAC'
        bcf     STATUS, RP0
        movlw   0x06            ; 2 Komparatoren mit Referenzspannung
        movwf   CMCON           ; Comp1 out = RA3
        return

;**************************************************************
; Wandeln der spannung*****************************************
CAdc
        clrf    counter
        clrf    counter+1
        clrf    result_l
        clrf    result_h
        movlw   0x03            ; 2 Komparatoren mit Referenzspannung
        movwf   CMCON
loop
        btfsc   CMCON, C1OUT    ; ist Komparator High oder low
        goto    complow         ; low
comphigh
        nop                     ; high
        bcf     PORTA, 3        ; 'DAC' = 0
        incfsz  result_l, f     ; zählen der 'Spannung'
        goto    eat2cycles
        incf    result_h, f
        goto    endloop
complow
        bsf     PORTA, 3        ; 'DAC' = 1
        nop
        goto    eat2cycles
eat2cycles
        goto    endloop
endloop
        incfsz  counter, f      ; Zahl der Zyklen Zählen
        goto    eat5cycles
        incf    counter+1, f
        movf    counter+1, w
        andlw   0x04            ; nur Bit 2 einblenden
        btfsc   STATUS, Z       ; Bit 2 =1 ?    1024 Zyklen vorbei?
        goto    loop            ; nein
        goto    exit            ; ja
eat5cycles
        goto    $+1
        nop
        goto    loop
exit
        movlw   0x06            ; 2 Komparatoren mit Referenzspannung
        movwf   CMCON           ; Comp1 out = RA3
        return

        end


zurück zu Lernbeispiele , PIC-Prozessoren , Elektronik , Homepage
Autor: sprut
erstellt: 21.01.2003
letzte Änderung: 25.01.2003
Quelle: microchip AN700