Redakcja: dondu
Drzaśkowy pamiętnik: Spis treści
Mikrokontrolery wyposażone w przetwornik ADC niezależnie od producenta z reguły posiadają co najmniej kilka wejść pomiarowych, zwanych kanałami.
Tak własnie jest w przypadku Atmega8, który posiada jeden przetwornik ADC i może mierzyć napięcie na kilku kanałach dzięki multiplekserowi (przełącznikowi), który znajduje się na wejściu ADC. Jego zadaniem jest przełączanie wejścia pomiarowego przetwornika ADC pomiędzy kanałami, które podłączone są do pinów mikrokontrolera i nie tylko (patrz rysunek poniżej).
Rzućmy okiem na schemat multipleksera ADC.
Spróbowałem więc wykonać pomiar z 4 wejść.
Pobierz schemat: schemat1.rar
Do tego program, który wykonuje na przemian pomiary 4 kanałów wyświetlając je na LCD:
#include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> #include <stdlib.h> #include "HD44780.h" //definicje wejść ADC #define wej_1 PC2 #define wej_2 PC3 #define wej_3 PC4 #define wej_4 PC5 volatile uint8_t adc1;//zmienna do pomiaru ADC wej_1 volatile uint8_t adc2;//zmienna do pomiaru ADC wej_2 volatile uint8_t adc3;//zmienna do pomiaru ADC wej_3 volatile uint8_t adc4;//zmienna do pomiaru ADC wej_4 volatile uint8_t wejscie=2;//zmienna do zmiany wejścia, początkowo pc2 //############################################################################## void main(void) { char wynik[]=" ";//bufor tekstowy, wyczyszczenie bufora LCD_Initalize(); //inicjalizacja LCD //numerowanie pomiarów LCD_GoTo(0, 0); LCD_WriteText("1:"); LCD_GoTo(7, 0); LCD_WriteText("2:"); LCD_GoTo(0, 1); LCD_WriteText("3:"); LCD_GoTo(7, 1); LCD_WriteText("4:"); //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 |wejscie; //Wybór wejścia początkowego czyli PC2 //Inicjalizacja Timera TIMSK |= (1<<TOIE0); //Przerwanie overflow przepełnienie timera TCCR0 |= (1<<CS01); // źródłem CLK, preskaler 8 (2000000 Hz) TCNT0 = 155; //Początkowa wartość licznika DDRC &=~ (1<<wej_1); //Ustawienie pinów wejściowych ADC DDRC &=~ (1<<wej_2); //Ustawienie pinów wejściowych ADC DDRC &=~ (1<<wej_3); //Ustawienie pinów wejściowych ADC DDRC &=~ (1<<wej_4); //Ustawienie pinów wejściowych ADC sei();//Globalne uruchomienie przerwań for(;;)//główna pętla programu { LCD_GoTo(3, 0); //Ustawienie kursora w pozycji (3,0) LCD_WriteText(" "); //Czyszczenie poprzednij wartości itoa(adc1,wynik,10); //konwersja wyniku do tablicy char LCD_GoTo(3, 0); //Ustawienie kursora w pozycji (3,0) LCD_WriteText(wynik); //Wyświetlenie wyniku LCD_GoTo(10, 0); //Ustawienie kursora w pozycji (10,0) LCD_WriteText(" "); //Czyszczenie poprzednij wartości itoa(adc2,wynik,10); //konwersja wyniku do tablicy char LCD_GoTo(10, 0); //Ustawienie kursora w pozycji (10,0) LCD_WriteText(wynik); //Wyświetlenie wyniku LCD_GoTo(3, 1); //Ustawienie kursora w pozycji (3,1) LCD_WriteText(" "); //Czyszczenie poprzednij wartości itoa(adc3,wynik,10); //konwersja wyniku do tablicy char LCD_GoTo(3, 1); //Ustawienie kursora w pozycji (3,1) LCD_WriteText(wynik); //Wyświetlenie wyniku LCD_GoTo(10, 1); //Ustawienie kursora w pozycji (10,1) LCD_WriteText(" "); //Czyszczenie poprzednij wartości itoa(adc4,wynik,10); //konwersja wyniku do tablicy char LCD_GoTo(10, 1); //Ustawienie kursora w pozycji (10,1) LCD_WriteText(wynik); //Wyświetlenie wyniku _delay_ms(100); //opóźnienie } } ISR(TIMER0_OVF_vect) { //Przerwanie przepełnienia Timer0 switch(wejscie) { case 2://gdy PC2 adc1=ADCH; //odczytaj tylko starszy bajt pomiaru break; case 3://gdy PC3 adc2=ADCH; //odczytaj tylko starszy bajt pomiaru break; case 4://gdy PC4 adc3=ADCH; //odczytaj tylko starszy bajt pomiaru break; case 5://gdy PC5 adc4=ADCH; //odczytaj tylko starszy bajt pomiaru break; } if(wejscie<5) wejscie++; else wejscie=2; ADMUX = 0; //kasowanie rejestru ADMUX = (1<<ADLAR) |(1<<REFS0)| wejscie; //Ustawianie nowych wartości TCNT0 = 155; //Początkowa wartość licznika }Pobierz program: 07_ADC_LCD_Prog6.zip (kopia)
A tak wygląda wynik pracy programu:
Przeczytaj także:
Czy rzeczywiście ten timer jest tu potrzebny? Przecież ADC może działać w trybie free running mode i może zgłaszać przerwanie po zakończeniu pomiaru - to wtedy można zapisać wynik do zmiennej globalnej i przełączyć kanał na inny.
OdpowiedzUsuńMoim zdanie rzeczywiście bez timera można się tutaj obyć, ale proszę zwrócić uwagę, że jest to rozbudowany kod z wcześniejszego artykułu. Prawdopodobnie pan Drzasiek zaoszczędził po prostu trochę czasu, po co implementować przetwornik drugi raz skoro tematem artykułu jest przełączanie kanałów.
OdpowiedzUsuńBtw dzięki za następne części pamiętnika :)
Te trzy tematy z ADC które ukazały się dzisiaj, były jednym wielkim artykułem, który ja jako redaktor postanowiłem podzielić, na trzy osobne, by były bardziej strawne (możecie zauważyć, że programy do pobrania mają kolejne numery).
OdpowiedzUsuńDlatego też ostatni temat dot. kanałów jest faktycznie dalszym ciągiem tego samego kodu, co zauważył kol. MrLol.
Tryb Free Running Drzasiek omówił we wcześniejszym artykule: ADC - Prezentacja wyniku na LCD
Ale czasami zależy nam aby pomiary odbywały się co ściśle określony czas a nie wtedy gdy przetwornik zakończył przetwarzanie.
OdpowiedzUsuńPo prostu próbkować.
Np. gdy chcemy zrobić oscyloskop.
Linia: "TIMSK |= (1<<TOIE0) | (1<<TOIE1);" włącza przerwania zarówno dla Timer0 (8bit) i Timer1(16bit). Wystarczy "TIMSK |= (1<<TOIE0);" w w/w przykładzie.
OdpowiedzUsuńPozdrawiam, Grzesiek.
Mimo wszystko warto w przerwaniu sprawdzić flagę zajętości ADC, w takiej formie jak jest obsłużony przetwornik, w niektórych przerwaniach będzie czytał "śmieci" z przetwornika...
OdpowiedzUsuńNie jest konieczne sprawdzanie flagi zajętości ADC - jeden warunek - odstęp pomiędzy przerwaniami timera musi być dłuższy niż czas przetwarzania. W przeciwnym przypadku w ogóle należałoby zrezygnować z timera i puścić ADC w tryb free running ze zgłaszaniem przerwań po każdej konwersji.
UsuńMoże ktoś mi wytłumaczyć co dzieje się w tym przerwaniu ? Co robi tam funkcja switch i case. Jak to odczytuje tam wartości z kolejnych pinow
OdpowiedzUsuńWszystko jest opisane w komentarzach. Czego konkretnie nie rozumiesz?
UsuńW jaki sposób prosto możemy przeliczyć te wartości na Volty?
OdpowiedzUsuńPróbuje w funkcji sprintf niestety nie daje to rezultatu.
Ogólnie połączyłem ten program z programem o USART, dane wyświetlają się na terminalu przez BT. Tylko mam problem z konwersją oraz czyszczeniem ekranu w ten sposób, żeby pomiary wyskakiwały cały czas w tym samym miejscu jeden pod drugim.
Proszę o pomoc. Pozdrawiam.
Przykład przeliczania pokazany jest w innej części Kursu AVR: ADC - Prezentacja wyniku na LCD
UsuńJaki terminal i jak to robisz.
Od razu muszę przyznać, że jestem początkującym w dziedzinie programowania, a muszę zrealizować projekt, który będzie odczytywał wartości napięcia z kilku kanałów ADC i wysyłał te dane do aplikacji na androida.Dodatkowo z telefonu muszę odbierać znaki do avr, żeby móc sterować ustawieniami digipota po SPI, ale to na później. Chwilowo dane zamiast na telefonie odbieram sobie przez moduł HC-05 na terminalu na komputerze. Proszę o jakąkolwiek pomoc. (Zakupiłem książke Tomasza Francuza, ale niestety nie zdążę się w tym momencie z nią zapoznać. Swoja drogą Dondu też musisz chyba coś skrobnąć, bo na stronie nieźle Ci to idzie ;)). Program, który mam tak jak wspomniałem to zlepek http://mikrokontrolery.blogspot.com/2011/03/rs-232-atmega8-komputer-terminal.html i http://mikrokontrolery.blogspot.com/2011/03/adc-multipleksowanie.html
UsuńPozdrawiam.
Najlepiej by było, gdybyś opisał swój problem na forum, a tutaj wstawił link do tematu, byśmy mogli tam trafić.
UsuńProszę link do tematu:
Usuńhttp://www.elektroda.pl/rtvforum/topic3149820.html
Wkleiłem również cały kod, który w tym momencie posiadam.
Dzięki za zainteresowanie ;)
Witam, mam dwa poważne problemy z powyższym kodem:
OdpowiedzUsuńPo pierwsze zmiana wartości na jednym kanale powoduje zmianę wartości na pozostałych wejściach
Po drugie przetwornik nawet bez podpięcia przewodów do wejścia szczytuje jakieś śmieci. Ale ten problem może być spowodowany podłączeniem układu na płytce stykowej.
Witam
OdpowiedzUsuńNie wiem czy dobrze zauważyłem ale w części odpowiedzialnej za inicjalizację ADC przed znakiem "=" brakuje "|". U mnie efekt był bardzo podobny do tego który opisuje "brylaas". wszystko działa poprawnie gdy dodałem |.
Testuje na EVB4.3 z Atmega16.
Pozdrawiam Autora (Bardzo pomocny materiał)
Karol