Przetwornik ADC

Nasze polecane książki, kursy, strony internetowe, fora itp. pomocne przy przyswajaniu wiedzy związanej z PIC i pisaniem programów dla nich.
Awatar użytkownika
Marcin
User
User
Posty: 162
Rejestracja: środa 09 wrz 2015, 19:30

Przetwornik ADC

Postautor: Marcin » środa 21 lut 2018, 00:47

Witam

Dziś chciałbym podzielić się zdobytymi informacjami i wiedzą na temat przetwornika ADC w mikrokontrolerze PIC.

Słowem wstępu o przetworniku ADC. Układy cyfrowe używają dwóch stanów napięć, logicznej jedynki czyli poziomu napięcia zbliżonego do napięcia zasilania oraz logcznego zera czyli poziomu napięcia zbliżonego do zera. Niejednokrotnie zachodzi potrzeba użycia elementu lub czujnika, w którym na wyjściu otrzymujemy napięcie proporcjonalne do wartości mierzonej. Aby w mikrokontrolerze użyć takiej wartości musimy ją zamienić na jej cyfrową reprezentację. Tu z pomocą przychodzi pzetwornik ADC czyli przetwornik analogowo - cyfrowy (z angielskiego Analog to Digital Converter) który zamienia analogową wartość napięcia na jej cyfrową reprezentację.

Przejdźmy do szczegółów. Jako modelowy mikrokontroler użyłem PIC16F18346, mikrokontroler ten zawiera jeden przetwornik ADC o 10-bitowej rozdzielczości. W przykładowym mikrokontrolerze mamy tylko jeden przetwornik ADC, jednak za pomocą multipleksera możemy wybierać różne źródła mierzonego sygnału, mogą to być zewnętrzne źródła (piny mikrokontrolera) jak i również wewnętrzne sygnały dostarczone z Fixed Voltage Reference (FVR) czyli wbudowanego źródła napięcia odniesienia, czujnika temperatury lub DAC czyli przetwornika cyfrowo-analogowego. W przypadku napięcia odniesienia w ADC możemy wybrać dodatnie jak i ujemne napięcie odniesienia. Jako dodatniego napięcia odniesienia możemy użyć zewnętrznego napięcia dostarczonego do pinu Vref+, napięcia zasilania Vdd lub jednego z dwóch napięć (2.048 lub 4.096V) wygenerowanych przez wewnętrzne stabilne źródła napięcia odniesienia. Jako ujemne napięcie możemy wybrać zewnętrzne napięcie dostarczone do pinu Vref- lub napięcie Vss czyli poziom masy. Wynik pomiaru to 10-bitowa wartość, która po pomiarze zapisywana jest w 16-bitowym rejestrze, przetwornik pozwala na sprzętowe wyrównanie wyniku do prawej lub lewej strony. Sygnał taktujący ADC może pochodzić z wewnętrznego sygnału zegarowego podzielonego w zakresie 2-64 lub dedykowanego oscylatora RC.

Z sygnałem taktującym związany jest jeden bardzo ważny parametr, Tad czyli czas w przeliczeniu potrzebny do ukończenia jednego bita konwersji.

Pierwszym ważnym krokiem to konfiguracja przetwornika i włączenie przetwornika, polega ona:

- wybraniu częstotliwości taktowania ADC (bity ADCS<2:0> rejestru ADCON1)
- wybraniu napięcia referencyjnego (bity ADPREF<1:0> oraz ADNREF w rejestrze ADCON1)
- wybrania kanału czyli źródła mierzonego sygnału, który ADC będzie mierzył (bity CHS<5:0> w rejestrze ADCON0)
- opcjonalnie możemy zmienić formatowanie wyniku, czyli wyrównanie do prawej lub lewej (bit ADFM w rejestrze ADCON1) Domyślnie jest wyrównanie do lewej.

Konfigurując ADC należy zwrócić uwagę na dobór częstotliwości sygnału zegarowego taktującego ADC zapewniającego właściwy czas konwersji. Dokumentacja dla mojego mikrokontrolera PIC16F18346 podaje że wymagany czas konwersji w przeliczeniu dla jednego bita (Tad) powiniem mieścić się w przedziale 1-9us. Dobierając sygnał taktujący ADC warto posikować się dokumentacją, która w tabeli zestawia zegar ADC vs Tad.

Konfigurując pin jako pin wejściowy dla ADC należy skonfigurować go jako analogowy pin wejściowy.

Aby włączyć ADC należy ustawić bit ADON w rejestrze ADCON0. Po właczeniu przetwornik jest gotowy do pracy.

Warto w tym miejscu przed omówieniem pomiarów wspomnieć o jeszcze jednym ważnym aspekcie, mianowicie parametrem acquisition time czyli czasem po właczeniu / zmianie kanału pomiarowego niezbędnym do osiągnięcia pełnego badanego napiecia na układzie pomiarowym ADC. Na czas ten wpływa na kilka czynników, miedzy innymi impedancja wyjściowa badanego źródla czy temperatura pracy. Dokumentacja podaje dla założonych warunków, czyli impedancja 10kohm, temp 50 st C oraz Vdd 5V czas Tacq to ok 5us. Po zmianie kanału pomiarowego (źródła mierzonego napięcia) przed rozpoczęciem samego pomiaru należy odczekać te kilka us celem stalenia się pełnego mierzonego napięcia na obwodzie pomiarowym ADC.

Przejdźmy do samych pomiarów. Pomiar możemy rozpocząć programowo poprzez ustawienie bitu GO/DONE w rejestrze ADCON0. Po zakończeniu pomiaru ADC skasuje bit GO/DONE, ustawi flagę ADIF w rejestrze PIR (o czym za chwilę szerzej opowiem) oraz zachowując ustawione w czasie konfiguracji formatowanie załaduje zmierzoną wartość do rejestrów ADRESH i ADRESL. Po formatowaniu bity w rejestrze wyjściowym nie należące do wyniku są uzupełniane zerami. Wydaje mi się że najlepiej zasadzę formatowania pokaże sam przykład z dokumentacji.

ADC RES.PNG


Co możemy powiedzieć o czasie konwersji, jest on uzależniony od taktowania ADC, dokumentacja podaje że jest on równy 12xTad. Dla przykładu, jeśli dla naszej konfiguracji Tad wynosił 1us, to całkowity czas konwersji to 12 x 1us co daje nam 12us.

Przetwornik ADC kończąc pomiar może wygenerować przerwanie, aby mu to umożliwić należy włączyć globalną obsługę przerwań, obsługę przerwań od układów peryferyjnych oraz przerwania od samego ADC (o przerwaniach pisałem w poprzednim tutoralu podając jako przykład właśnie ADC) Jeśli przerwania są włączone, zdarzenie zakończenia konwersji ustawiając flagę w ADIF rejestrze PIR pozwoli na wygenerowanie przerwania. Dzięki fladze ADIF możemy określić w funkcji obsługi przerwania źródło wywołujące przerwanie. Pamiętajmy o programowym skasowaniu flagi ADIF przed opuszczeniem funkcji obsługi przerwania. Przerwania od ADC pozwalają nam na wyeliminowanie programowego oczekiwania na zakończenie konwersji dokonywanej przez ADC (oczekiwania na skasowanie bitu GO/DONE) oraz są wręcz niezastąpione przy cyklicznych sprzętowych wywołaniach pomiarów ADC.

Pomiar przez ADC może być również automatycznie inicjowany z różnych sprzętowych źródeł, źródłami tymi mogą być timery, wyjścia z CLC, CCP czy komparatorów, źródła wyzwalające pomiar ADC konfigurujemy w rejestrze ADACT, bity ADACT<4:0> Rozpoczęcie konwersji w takim trybie jest wywoływane zboczem narastającym z wybranego źródła. Często jest niezależnie od toku programu głównego, tu mało efektywne byłoby sprawdzanie czy zainicjowano nową konwersję oraz czy ją już zakończono, z pomocą przychodzą nam przerwania, w wygenerowanym przerwaniu po zakończeniu każdej konwersji możemy odczytać wynik pomiaru przez ADC.

Myślę że więcej nie będę się rozpisywał o ADC w PICu. W swoim tutorialu wymieniłem trochę rejestrów, niektórych może to przerazić, na szczęście w MPLab jest dostepna wtyczka MPLab Code Configurator, która pozwala na znacznie prostszą graficzną konfigurację ADC czy innych bloków. A cała konfiguracja ADC z poziomu wtyczki MCC sprowadza się do:

ADC config.PNG


Teraz trochę praktyki:

adc.c

Kod: Zaznacz cały

#define ACQ_US_DELAY 5

/**
  Section: ADC Module APIs
*/

void ADC_Initialize(void)
{
    // set the ADC to the options selected in the User Interface
   
    // ADGO stop; ADON enabled; CHS ANA0;
    ADCON0 = 0x01;
   
    // ADFM right; ADNREF VSS; ADPREF VDD; ADCS FOSC/2;
    ADCON1 = 0x80;
   
    // ADACT TMR1_overflow;
    ADACT = 0x04;
   
    // ADRESL 0;
    ADRESL = 0x00;
   
    // ADRESH 0;
    ADRESH = 0x00;
   
    // Enabling ADC interrupt.
    PIE1bits.ADIE = 1;
}

void ADC_SelectChannel(adc_channel_t channel)
{
    // select the A/D channel
    ADCON0bits.CHS = channel;   
    // Turn on the ADC module
    ADCON0bits.ADON = 1; 
}

void ADC_StartConversion()
{
    // Start the conversion
    ADCON0bits.ADGO = 1;
}


bool ADC_IsConversionDone()
{
    // Start the conversion
   return ((bool)(!ADCON0bits.ADGO));
}

adc_result_t ADC_GetConversionResult(void)
{
    // Conversion finished, return the result
    return ((adc_result_t)((ADRESH << 8) + ADRESL));
}

adc_result_t ADC_GetConversion(adc_channel_t channel)
{
    // select the A/D channel
    ADCON0bits.CHS = channel;   
   
    // Turn on the ADC module
    ADCON0bits.ADON = 1;

    // Acquisition time delay
    __delay_us(ACQ_US_DELAY);

    // Start the conversion
    ADCON0bits.ADGO = 1;

    // Wait for the conversion to finish
    while (ADCON0bits.ADGO)
    {
    }

    // Conversion finished, return the result
    return ((adc_result_t)((ADRESH << 8) + ADRESL));
}

void ADC_TemperatureAcquisitionDelay(void)
{
    __delay_us(200);
}

void ADC_ISR(void)
{
    // Clear the ADC interrupt flag
    PIR1bits.ADIF = 0;
    uint16_t data = ADC_GetConversionResult();
    CCPR1 = data;
}
/**
 End of File
*/


przerwania

Kod: Zaznacz cały

void interrupt INTERRUPT_InterruptManager (void)
{
    // interrupt handler
    if(INTCONbits.PEIE == 1)
    {
        if(PIE1bits.ADIE == 1 && PIR1bits.ADIF == 1)
        {
            ADC_ISR();
        }
        else
        {
            //Unhandled Interrupt
        }
    }     
    else
    {
        //Unhandled Interrupt
    }
}


i na końcu main.c

Kod: Zaznacz cały

void main(void)
{
    // initialize the device
    SYSTEM_Initialize();


    //właczamy przerwania ustawiając bity
    //w rej. INTCON
    INTCON |= _INTCON_GIE_MASK | _INTCON_PEIE_MASK;
   
    ADC_SelectChannel(0xF);

    while (1)
    {
        // Add your application code
    }
}


Zaznaczam że przykłady konfiguracji podałem dla użytego mikrokontrolera, czyli PIC16F18346. Jako że nie lubię bawić się na płytkach stykowych zrobiłem sobie własną płytkę, której projekt też udostępnię w innym dziale, może komuś się przyda do testów. Jeśli macie inny mikrokontroler dokładnie sprawdźcie konfigurację ADC w datasheet, może się różnić od tego co tu podałem.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Wróć do „Źródła wiedzy na temat PIC”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 1 gość