LinkedIn YouTube Facebook
Szukaj

Newsletter

Proszę czekać.

Dziękujemy za zgłoszenie!

Wstecz
Artykuły

Mikrokontrolery AVR XMEGA w praktyce, część 22. Projekt: miernik pojemności

Pomiar komparatorem pojemności kondensatora

Istnieje wiele metod pomiaru pojemności kondensatora, a najprostszą z nich polega na zbudowaniu prostego oscylatora z wykorzystaniem jednego lub dwóch komparatorów. Wszystko, co tylko możliwe, upchniemy wewnątrz procesora, a jedyne co do niego musimy podłączyć, to rezystor 1 k oraz badany kondensator. Schemat układu przedstawia rysunek 1, a jego zdjęcie widoczne jest na fotografii 2.

 

Rys. 1. Schemat miernika pojemności kondensatorów

 

Fot. 2. Sposób podłączenia rezystora i kondensatora

 

Jeśli do kondensatora przyłożymy pewne stałe napięcie zasilające, doprowadzone przez rezystor, to napięcie na kondensatorze zacznie rosnąć w sposób wykładniczy, stopniowo zbliżając się do napięcia zasilającego. Kiedy elektrody kondensatora zewrzemy rezystorem, napięcie na nim będzie stopniowo spadać, również w sposób wykładniczy, aż w końcu osiągnie wartość zerową.

W prezentowanej metodzie, wykorzystuje się pomiar czasu, jaki jest potrzebny na naładowanie i rozładowanie kondensatora pomiędzy dwoma, charakterystycznymi napięciami granicznymi. Napięcie na kondensatorze będzie się naprzemiennie zwiększać się i zmniejszać pomiędzy tymi dwoma napięciami. Wykresy tych napięć przedstawiono na rysunku 3.

 

Rys. 3. Schemat układu mierzącego pojemność i przebiegi w charakterystycznych miejscach

 

Czas ładowania i rozładowania jest tym dłuższy, im większa jest pojemność kondensatora – co najważniejsze, jest to zależność liniowa. Tak więc znając czas, wystarczy go przemnożyć lub podzielić przez odpowiedni współczynnik, by poznać pojemność.

Każdy student elektroniki miał (lub będzie mieć) całkiem zaawansowaną matematyczną teorię na ten temat, więc pozwolę sobie o niej nie pisać i przejdę od razu do sedna problemu. Spójrz na schemat z rysunku 3 oraz na kod programu z listingu 1.

Potrzebujemy dwa źródła napięć granicznych oraz dwa komparatory. Do tego celu idealnie nadaje się tryb okienkowy, pozwalający na sprzężenie dwóch komparatorów. Jakie powinny być wartości napięć granicznych? Ich wartość nie jest krytyczna, ale najważniejsze jest, by były one stabilne. Im większa będzie między nimi różnica, tym większy będzie również czas ładowania i rozładowania kondensatora. W praktycznych rozwiązaniach często ustala się je na 1/3 i 2/3 napięcia zasilającego. W tym przypadku będą to ok. 1 V i 2 V. Zapewne niejeden czytelnik dostrzeże analogię tego rozwiązania do nieśmiertelnego układu NE555.

W celu uzyskania napięć granicznych, do dyspozycji mamy dzielnik napięcia zasilającego, źródło referencyjne (bandgap) oraz przetwornik analogowo-cyfrowy (DAC). Ze względu na stabilność, najlepiej byłoby użyć bandgap oraz przetwornik DAC, jednak w kursie nie był on jeszcze omawiany, dlatego zastosowałem dzielnik napięcia zasilającego.

Kiedy napięcie na kondensatorze osiągnie wartość graniczną, układ nadzorujący komparatory wygeneruje odpowiednie zdarzenie i przerwanie komparatora o nazwie ACA_ACW_vect. Przerwanie ma na celu zmianę stanu pinu A5. Pin ten naprzemiennie zasila kondensator i rozładowuje go.

Pomiar czasu jest realizowany za pomocą timera C0. W chwili rozpoczęcia ładowania/rozładowywania timer jest zerowany i zaczyna liczyć w górę. Kiedy napięcie na kondensatorze osiągnie wartość graniczną, wówczas jest generowane zdarzenie (równocześnie z przerwaniem!). Zdarzenie jest przekazywane przez system zdarzeń do timera, który jest tak skonfigurowany, by dokonywał przechwycenia stanu licznika do rejestru CCA.

Jest tu pewien szkopuł. Mianowicie czas ładowania i rozładowywania nie jest taki sam. Dzieje się tak dlatego, że kondensator jest ładowany/rozładowywany nie tylko przez rezystor podłączony do płytki, ale także przez rezystancję pinu procesora, która jest inna w stanie wysokim i inna w stanie niskim. Powodowałoby to naprzemienne pokazywanie dwóch wyników, nieznacznie różniących się od siebie. Na szczęście projektanci mikrokontrolerów XMEGA dostrzegli ten problem – aby go wyeliminować, należy włączyć tryb pomiaru częstotliwości. Dodatkowym bonusem jest to, że po pełnym cyklu timer zostanie automatycznie wyzerowany.

Istnieje jednak ryzyko, że układ nie wskoczy w rytm naprzemiennego ładowania i rozładowywania kondensatora. Może się tak zdarzyć np. po zresetowaniu procesora. Wtedy kondensator naładuje się do napięcia zasilania lub rozładuje się do zera, a program będzie czekał w nieskończoność. Aby temu zapobiec, najprościej zastosować przerwanie od przepełnienia timera TCC0_OVF_vect – w jego procedurze znajduje się jedynie polecenie zamiany stanu pinu A5. Dzięki temu układ zawsze będzie działał.

W pętli głównej, musimy jedynie cyklicznie wyświetlać wynik. Jest to zadanie bardzo proste (wyświetlacz LCD omówiono w części 5). Jedyny problem to przeliczenie cykli zegarowych, zmierzonych przez timer, na pojemność w dowolnej jednostce (ja wybrałem mikrofarady) – musimy skalibrować nasz miernik. Jako kondensator wzorcowy użyłem zwyczajnego elektrolitu 100?F. Na wyświetlaczu uzyskałem wynik ok. 31000. Dlatego w kodzie programu, w linii oznaczonej numerem (1) wartość rejestru TCC0.CCA dzielę przez 31. Uzyskany w ten sposób wynik stanowi pojemność w mikrofaradach, ale pomnożoną przez 10 – aby przy pomocy funkcji LcdDecComma uzyskać jedno miejsce po przecinku. Dzięki takiemu dość pokrętnemu przeliczeniu, udało się uniknąć stosowania liczb zmiennoprzecinkowych typu float oraz funkcji printf, które przerażająco marnują zasoby procesora. Zachęcam do takiego kombinowania, gdyż przy odrobinie wprawy obliczenia z liczbami ułamkowymi nie będą stanowić problemu, a brak zmiennych float oznacza często wiele kilobajtów zaoszczędzonej pamięci procesora!

Dodatkowo, w każdym obiegu pętli głównej wpisujemy stan pinu A5 do rejestru wyjściowego PORTX.OUT, aby widzieć, kiedy kondensator jest ładowany, a kiedy rozładowywany. Można zaobserwować, że dioda mruga tym szybciej, im mniejsze kondensatory podłączamy do układu.

Miernik pojemności zbudowany tym sposobem jest w stanie mierzyć kondensatory o pojemności od 1 ?F do 150 ?F. Aby zwiększyć zakres pomiarowy, należy zmienić preskaler timera, by mierzyć mniejsze lub większe interwały czasowe. Czas ładowania zależy również od rezystora. Dobrym pomysłem jest zastosowanie kilku rezystorów o różnych wartościach, podłączonych do różnych pinów i napisanie algorytmu, który by automatycznie wybierał rezystor. Dokładność miernika zależy także od stabilności generatora sygnału zegarowego. My w tym odcinku wykorzystaliśmy wbudowany generator RC, a wiele lepszym pomysłem, lecz niewiele bardziej skomplikowanym, jest zastosowanie generatora kwarcowego (opisanego w części).

Listing 1. Kod miernika częstotliwości

#include 
#include 
#include "extrino_portx.h"
#include "hd44780.h"

int main(void) {
    
    PortxInit();                                  // inicjalizacja portu X
    LcdInit();                                    // inicjalizacja wyświetlacza LCD
    
    // konfiguracja komparatora 0 w porcie A
    ACA.AC0MUXCTRL    =    AC_MUXPOS_PIN6_gc |    // wejście + PIN A6
                           AC_MUXNEG_SCALER_gc;   // wejście - dzielnik napięcia
    ACA.CTRLB         =    38;                    // 2/3 napięcia Vcc jako odniesienie
    ACA.AC0CTRL       =    AC_HYSMODE_LARGE_gc|   // duża histereza
                           AC_ENABLE_bm;          // włączenie komparatora
                        
    // konfiguracja komparatora 1 w porcie A
    ACA.AC1MUXCTRL    =    AC_MUXPOS_PIN6_gc |    // wejście + PIN A6
                           AC_MUXNEG_BANDGAP_gc;  // 1V bandgap źródło nap odniesienia
    ACA.AC1CTRL       =    AC_HYSMODE_LARGE_gc|   // duża histereza
                           AC_ENABLE_bm;          // włączenie komparatora
                        
    // konfiguracja komparatora okienkowego
    ACA.WINCTRL       =    AC_WEN_bm|             // uruchomienie trybu okienkowego
                           AC_WINTMODE_INSIDE_gc| // wybór przerwania
                           AC_WINTLVL_LO_gc;      // wybór priorytetu przerwania
    
    // pin do ładowania/rozładowania kondensatora
    PORTA.DIRSET      =    PIN5_bm;               // A5 jako wyjście
    PORTA.OUTSET      =    PIN5_bm;               // stan niski w celu rozładowania kondensatora
    
    // system zdarzeń
    EVSYS.CH0MUX      =    EVSYS_CHMUX_ACA_CH1_gc;// zdarzenie komparatora jest przesyłane kanałem 0 systemu zdarzeń
    
    // konfiguracja timera
    TCC0.CTRLD        =    TC_EVACT_FRQ_gc |      // tryb pomiaru częstotliwości
                           TC_EVSEL_CH0_gc;       // zdarzenie wywołuje kanał 0
    TCC0.CTRLB        =    TC0_CCAEN_bm|          // przechwycenie do rejestru CCA
                           TC_WGMODE_NORMAL_gc;   // zwykły tryb zliczania impulsów
    TCC0.CTRLA        =    TC_CLKSEL_DIV8_gc;     // preskaler i uruchomienie timera
    TCC0.INTCTRLA     =    TC_OVFINTLVL_LO_gc;    // pritrytet przerwania od przepełnienia
    
    // konfiguracja przerwań
    PMIC.CTRL         =    PMIC_LOLVLEN_bm;       // włączenie przerwań o priorytecie LO
    sei();
    
    uint16_t wynik;                               // zmienna przechowująca pojemność kondensatora
    
    while(1) {
        
        // wyświetlanie wyniku
        Lcd1;                                     // kursor na początek 1 linii
        wynik = TCC0.CCA / 31;                    // komentarz w tekście (1)
        LcdDecComma(wynik,1);                     // wyświetlenie liczby stałoprzecinkowej
        Lcd("uF       ");
        
        // wyświetlenie stanu pinu A5 na diodach PORTX
        PORTX.OUT     =    PORTA.OUT & PIN5_bm;        
    }
}

ISR(ACA_ACW_vect) {                               // przerwanie komparatora
    
    if(PORTA.OUT & PIN5_bm) {                     // jeżeli teraz jest ładowanie
        ACA.WINCTRL   =    AC_WEN_bm|                
                           AC_WINTMODE_BELOW_gc|
                           AC_WINTLVL_LO_gc;        
    } else {                                      // jeżeli teraz jest rozładowywanie
        ACA.WINCTRL   =    AC_WEN_bm|                
                           AC_WINTMODE_ABOVE_gc|
                           AC_WINTLVL_LO_gc;
    }
    
    PORTA.OUTTGL      =    PIN5_bm;               // zmiana stanu pinu A5 na przeciwny
}

ISR(TCC0_OVF_vect) {                              // przepełnienie timera C0
    PORTA.OUTTGL      =    PIN5_bm;               // zmiana stanu pinu A5 na przeciwny
}

 

Dystrybutorem zestawu X3-DIL64 jest KAMAMI.pl.

 

Dominik Leon Bieczyński

http://leon-instruments.blogspot.com

 

Do pobrania