wtorek, 29 marca 2011

ADC - Precyzyjne ustawianie częstotliwości próbkowania

Autor: drzasiek
Redakcja: dondu


Drzaśkowy pamiętnik: Spis treści

Czasami może być potrzebne precyzyjne ustawienie częstotliwości otrzymywania pomiarów z ADC. Niestety mikrokontrolery mają ograniczone możliwości w tym zakresie i trzeba się czasami posiłkować dodatkowymi technikami, by cel osiągnąć.

Warto przeczytać także ten artykuł: ADC - Ile da się wycisnąć? - czyli eksperymenty z ADC

Preskalery ADC może być jak widać z tabeli potęgą liczby 2:



Dla tych co nie wiedzą, co to jest preskaler: Prescaler, postscaler - Co to takiego?

Poprzez wybranie preskalera ustawić można częstotliwość taktowania przetwornika, a nie sama częstotliwość próbkowania. Aby obliczyć częstotliwość próbkowania trzeba uwzględnić także czas konwersji (patrz artykuł z linku powyżej). W efekcie częstotliwości próbkowania są dość niewygodne.

Zacząłem się więc zastanawiać jak uzyskać dokładnie interesującą mnie częstotliwość próbkowania.

Do głowy przyszły mi dwa pomysły:
  1. dobrać odpowiednio taktowanie procesora, np. z zewnętrznego generatora,
  2. wykorzystać Timer.
Jako, że pierwszy sposób wydał mi się bardziej kłopotliwy i mniej dokładny, skorzystałem ze sposobu drugiego.

Uruchomiłem ADC w trybie FREE RUN, bez zgłaszania przerwań po zakończonej konwersji. Ustawiłem Timer tak, aby zgłaszał przerwanie z interesującą mnie częstotliwością. W procedurze obsługi przerwania od Timera przepisywałem wartość pomiaru ADC do zmiennej.

Przykład pomiaru z częstotliwością 20 kHz (20 kSPS):

//kod5

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>   
#include "HD44780.h"
//definicja napiecia referencyjnego
#define VREF 5.0
//definicja ADCIN (wejście ADC)
#define ADCIN PC5 

volatile uint8_t adc;//zmienna do pomiaru ADC
 

void main(void)
{
 char wynik[]="    ";//bufor tekstowy, wyczyszczenie bufora
    
 LCD_Initalize();   //inicjalizacja LCD
 LCD_GoTo(1, 0);      //Ustawienie kursora w pozycji (0,0)
 LCD_WriteText("76,9 kHz 8 bit");
 //Inicjalizacja ADC
 ADCSRA = (1<<ADEN)      // ADC Enable (uruchomienie przetwornika)
          |(1<<ADFR)      //tryb Free run
          |(1<<ADSC)      //rozpoczęcie konwersji
          |(1<<ADPS2);   //ADPS2: (ustawienie preskalera) preskaler= 16

 ADMUX  = (1<<ADLAR)     //Wyrównanie wyniku do lewej
          |(1<<REFS0)            //VCC jako napięcie referencyjne
          |(1<<MUX2) | (1<<MUX0);   //Wybór wejścia (ADC5 - Pin 5 )

 //Inicjalizacja Timera
 TIMSK |= (1<<TOIE0) | (1<<TOIE1); //Przerwanie overflow przepełnienie timera
 TCCR0 |= (1<<CS01); // źródłem CLK, preskaler 8 (2000000 Hz)
 TCNT0 = 155; //Początkowa wartość licznika 

 DDRC &=~ (1<<ADCIN);               //Ustawienie pinu wejściowego ADC

 sei();//Globalne uruchomienie przerwań 

 for(;;)//główna pętla programu
 {
    LCD_GoTo(5, 1);         //Ustawienie kursora w pozycji (5,1)
    LCD_WriteText("    ");  //Czyszczenie poprzednij wartości
    itoa(adc,wynik,10);     //konwersja wyniku do tablicy char
    LCD_GoTo(5, 1);         //Ustawienie kursora w pozycji (5,1)
    LCD_WriteText(wynik);   //Wyświetlenie wyniku
    _delay_ms(100);         //opóźnienie
 }
}


ISR(TIMER0_OVF_vect)
{
    adc=ADCH;             //odczytaj tylko starszy bajt pomiaru
    TCNT0 = 155;          //Początkowa wartość licznika
}
Pobierz program: prog5.rar

Schemat układu testowego znajdziesz tutaj: ADC - Ile da się wycisnąć? - czyli eksperymenty z ADC

Przeczytaj także:

8 komentarzy:

  1. Witam. Mam problem z napisaniem kodu. Potrzebuje plynnie sterowac predkoscia silnika za pomoca potencjometru. w Atmega32. W jaki spodob by to mozna bylo zrobic?

    OdpowiedzUsuń
  2. Witaj, najprościej przeszukać forum Elektroda.pl, później spróbować coś samemu napisać, a dalej już tak: Fora dyskusyjne są bardzo pomocne

    To naprawdę najlepsza droga :-)
    Powodzenia!

    OdpowiedzUsuń
  3. Problem z tym kodem jest taki, że ADC w trybie Free run będzie obliczał wykonywał konwersję asynchronicznie do przerwań zegara a więc wartość pobrana w przerwaniu "ISR(TIMER0_OVF_vect)" może być niekompletna albo niewłaściwa (itp.).
    Kod będzie prawidłowy jeśli ustawimy ADIF oraz w przerwaniu TIMER0_COMP_vect skopiujemy wartość wygenerowaną przez ADC do zmiennej globalnej - tu:adc. Po takim działaniu mamy zapewnione, że wartość pomiaru będzie prawidłowa (zapewni nam to sposób w jaki są obsługiwane przerwania).




    OdpowiedzUsuń
    Odpowiedzi
    1. W programie Drzasiek nie odczytuje stanu całego rejestru ADC, a jedynie ADCH. Poza tym, choć nie wynika to bezpośrednio z dokumentacji, to rejestr ADCH w trakcie trwania pomiaru pamięta poprzednią wartość. Można więc odczytywać ADCH w dowolnym momencie.

      Pomysł "ręcznego" ustawiania flagi ADIF jest bardzo dziwny :-)

      Powyższy program można oczywiście napisać inaczej bez trybu Free Running:

      1. Timer ustawiony na przerwania z częstotliwością 20kHz,
      2. przerwanie timera rozpoczyna konwersję ADC,
      3. przerwanie z ADC obrabia wynik pomiaru,
      4. pętla główna wyświetla dane na LCD.

      Usuń
    2. Istnieje jeszcze inne rozwiązanie - ADC może być wyzwalany zdarzeniem przepełnienia lub compare match timera. Nowsze ATMegi tak potrafią (np. ATMega88). Wtedy próbkowanie określa precyzyjnie timer np. w trybie CTC. Warto też pomyśleć np. o XMEGA, gdzie ADC może być wyzwalany przez event system, w efekcie próbkowanie może być wyzwalane przez cokolwiek tylko chcemy.

      Usuń
  4. Czym tutaj jest tak na prawdę wyświetlany wynik i jak go zinterpretować żeby otrzymać częstotliwość z jaką pracuje nasz przetwornik? Wynik wyświetla mi się nawet gdy nie mam niczego na wejściu przetwornika wtedy jest to wynik około 110.

    OdpowiedzUsuń