Skocz do zawartości

ATmega168 i pomiar ADC co sekundę


TomekT

Pomocna odpowiedź

Witam, zacząłem się bawić atmegą168 i w nocie znalazłem ciekawą właściwość, a mianowicie pomiar adc generowany zdarzeniem ustalonym wg bitów ADTS0-2. W programie ustawilem rozpoczęcie pomiaru przy każdym przerwaniu od timera1 w trybie ctc (co sekundę). Nawet działa, ale czy to oznacza ze nie muszę już dodawać pętli oczekującej na zakończenie pomiaru np.

ADCSRA |= (1<<ADSC);//ROZPOCZNIJ KONW
while(!(ADCSRA & (1<<ADIF)));//czekaj na zakonczenie

Czy automatycznie zostanie zrealizowany pomiar i nie muszę juz się zajmować oczekiwaniem?

Oto mój program (odczytujący temperaturę z LM35, Vref=AVCC, przerwanie timera1 co sekundę)

#include <avr\io.h>
#include <util\delay.h>
#include <avr\interrupt.h>
#include <stdlib.h>
#include <stdio.h>
#include "LCD/lcd44780.h"

float pm;
volatile uint16_t flaga;

int main(void)
{
lcd_init();
lcd_cls();

ADMUX |= (1<<REFS0);
ADCSRA |= (1<<ADEN) | (1<<ADPS1) | (1<<ADPS2) | (1<<ADATE);
ADCSRB |= (1<<ADTS0) | (1<<ADTS2);
DDRC &= ~(1<<PC0);

TCCR1A |= (1<<COM1B0);
TCCR1B |= (1<<WGM12) | (1<<CS12);
OCR1A=31250;
TIMSK1 |= (1<<OCIE1B);
DDRB |= (1<<PB2);

sei();

while(1)
{
	if(flaga)
	{
		lcd_cls();
		lcd_locate(0,0);
		pm=ADCW;
		pm *= 0.0049;
		pm *= 100;// bity na volty
		pm -= 273;
		lcd_int(pm);
		flaga=0;
	}
}
}
ISR(TIMER1_COMPB_vect)
{
flaga=1;
}
Link do komentarza
Share on other sites

Timer jest tylko "wyzwalaczem" pomiaru wykonywanego przez ADC a to oznacza, że jeśli się zasadzisz na przerwanie od timera to dostaniesz informację o rozpoczęciu konwersji. Jeśli chcesz poprawnie ustawiać flagę gotowości nowego wyniku to musisz ją obsługiwać w przerwaniu od ADC. To bardzo wygodne i często tak robię. Żeby pętla główna "nie zniżała" się do poziomu grzebania w rejestrach możesz w obsłudze przerwania od ADC wczytać wynik do jakiejś zmiennej statycznej (+volatile) i dopiero ustawiać flagę. Wtedy reszta kodu będzie mogła korzystać z wartości ostatniego pomiaru.

Zamiast używania operacji zmiennoprzecinkowych możesz zrobić tak:

stopnie = ((63*ADCW) >> 7) - 273;

Nie wiem jak dokładnie Twój współczynnik musi być równy 0.49 ale mnożenie przez 63 a potem przesunięcie o 7 bitów w prawo daje iloraz 63/128=0.492. Taką operację spokojnie możesz zrobić w obsłudze przerwania i oddawać dalej już gotowe stopnie. Przy okazji kod będzie znacznie krótszy. Gdziekolwiek masz obliczenia ze współczynnikami stałymi zawsze próbuj je wyrażać jako ułamek k/(2^n) - wtedy nie musisz dzielić a przesuwanie jest znacznie "tańsze" obliczeniowo. Jeżeli na dodatek uda Ci się w mianowniku wstawić 256 (albo 65536) będzie jeszcze szybciej, bo procesor zamiast przesuwać bity po prostu przestawi całe bajty. Tutaj nie można było tego chwytu użyć, bo wyszedłby ułamek 125/256 a mnożenie liczby 10-bitowej przez 125 wymagałoby rozszerzenia int-ów do 32 bitów z powodu możliwego nadmiaru. To oczywiście i tak jest znacznie lepsze (w AVR) niż float.

Takie "jednokrotne" wykorzystanie przetwornika sugeruje, że wierzysz w jego nieomylność i brak szumu nałożonego na mierzony sygnał. Radziłbym albo zrobić częstsze pomiary (np. 8 lub 16 na sekundę, sumować w przerwaniu a następnie raz na sekundę policzyć średnią i dopiero przeliczyć na stopnie) albo w przebudować to tak, by w obsłudze przerwania od timera "ręcznie" startować ADC w trybie start-stop, zrobić kilkanaście szybkich pomiarów no a dalej to tak samo. To drugie produkuje długą, nieprzerywalną sekcję krytyczną ale w tak prostym kodzie to nie przeszkadza.

Link do komentarza
Share on other sites

Ogólnie powinieneś poczekać, albo rozwiązać to inaczej. W przypadku użycia wyzwalania konwersjie ADC przy pomocy sygnału z timera to nie wiele się różni od wyzwolenia ręcznego, nadal masz około 13 cykli przetwornika ADC zanim uzyskasz pomiar. Jednak w twoim przypadku może to działać ponieważ operacje na wyświetlaczu są czasochłonne i te 13 cykli może minąć. Jednak jeśli zmienisz strukturę programy może okazać się że pomiar zmienia ci się z opóźnieniem.

Kolejna sprawa jest taka, że konwersja nie jest wyzwalana przerwaniem, tylko zboczem narastającym FLAGI od przerwania, co za tym idzie nie trzeba fizycznie obsługiwać przerwania przy wyzwoleniu konwersji jedynie trzeba pamiętać o zerowaniu flagi, zapisując ją jedynką.

Moja propozycja jest taka, abyś darował sobie obsługę przerwania od timera, (wtedy będziesz musiał zerować flagę od przerwania ręcznie) na rzecz przerwania od ukończenia konwersji.

Twój program nie wiele się zmieni bo w minimalistycznej wersji trzeba będzie tylko zmienić wektor przerwania w ISR i dodać linijkę do zerowania flagi w timerze oczywiście pamiętając o zezwoleniu na przerwanie od ukończenia konwersji podczas uruchamiania ADC.

Link do komentarza
Share on other sites

Dziękuję za wyczerpujące podpowiedzi:)

[ Dodano: 12-02-2013, 14:01 ]

W takim razie zapytam jeszcze czy mój tok rozumowania jest poprawny:

1. ustawiam tryb free running

2. odblokowuję przerwanie adc

3. w przerwaniu (które się włącza po zakończeniu pomiaru) odczytuję wynik do zmiennej volatile i ustawiam flagę

4. w mainie w pętli while sprawdzam flagę i wyrzucam wynik np na lcd

oto mój zmieniony kod:

#include <avr\io.h>
#include <util\delay.h>
#include <avr\interrupt.h>
#include <stdlib.h>
#include <stdio.h>
#include "LCD/lcd44780.h"
uint32_t wynik;
volatile uint16_t pm;
uint8_t czd, czu;
volatile uint16_t flaga;

int main(void)
{
lcd_init();
lcd_cls();

ADMUX |= (1<<REFS0); //Vref=avcc, channel 0
ADCSRA |= (1<<ADEN); //włączenie przetwornika
ADCSRA |= (1<<ADPS1) | (1<<ADPS2); //preskaler=64
ADCSRA |=  (1<<ADATE); //tryb auto trigger
ADCSRA |= (1<<ADIE); //wlaczenie przerwania adc
//ADCSRB |= (1<<ADTS0) | (1<<ADTS2); //wyzwolenie konwersji(ctc timer1)
DDRC &= ~(1<<PC0);
ADCSRA |= (1<<ADSC); //rozpoczęcie pomiaru



sei();

while(1)
{
	if(flaga)
	{
		lcd_cls();
		lcd_locate(0,0);
		//pm=ADCW;
		//pm *= 0.0049;
		//pm *= 100;// bity na volty
		//pm -= 273;
		wynik=pm*49;
		czd=(wynik/100)%100;
		czu=(wynik/10)%10;
		lcd_int(czd);
		lcd_char('.');
		lcd_int(czu);
		lcd_str(" V");
		flaga=0;
	}
}
}

ISR(ADC_vect)
{
pm=ADCW;
flaga=1;
}

wiem że ten float jest tutaj mocno niepożądany i ta flaga będzie się ustawiać za często w stosunku do możliwości lcd, ale nie chciałem na razie nic zmieniać żeby się dowiedzieć czy poprawnie teraz wykorzystuje przetwornik (poźniej planuję uśredniać próbki)

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Ciężko odpowiedzieć na pytanie czy używasz poprawnie bo wszystko zależy od tego co naprawdę chcesz osiągnąć. Odpaliłeś ADC w trybie free running musisz rozwiązać jeszcze sprawę wyznaczania chwili kiedy faktycznie skorzystasz przetwornika i faktycznie odczytasz te kilka kolejnych próbek. Możesz tak kożystać z ADC, ale moim zdaniem jest to nie efektywne, zwróć uwagę na to że bez względu na to czy korzystasz z pomiarów czy nie (a miałeś z nich korzystać co sekundę) przetwornik generuje cyklicznie przerwanie i zajmuje mikrokontroler, nie jest tragicznie jeśli masz ustawiony duży prescaler dla zegara taktującego bo w tedy 13 cyknięć zegara ADC dla miktrokontrolera to wieczność. Wtedy te kilka taktów wykorzystanych na cykliczną obsługę przerwania się gubi. Jednak jest to mało eleganckie 🙂

marek1707'owi chodziło o wykorzystanie trybu single conversion mode i ręcznym wyzwoleniu kilku pomiarów po sobie, które następnie uśrednisz. Druga propozycja opierała się na wykorzystaniu trybu w którym wyzwalasz konwersję przez timer i robić to kilkukrotnie częściej niż odświeżasz oficjalny pomiar. Obie metody są ciekawe, natomiast różnie obciążają sprzęt i zastanów się czy jest to dla Ciebie krytyczne.

Link do komentarza
Share on other sites

gohandi, przerwanie od ADC następuje po zakończeniu konwersji 🙂

Moim zdaniem wystarczy co sekunde włączyć konwersje ADC, i a przerwaniu odczytywać wynik i rozpoczynać nową konwersje. I tak kilka razy po czym nie włączać nowej konwersji i wyliczyć średnią.

Link do komentarza
Share on other sites

Harnas, nie bardzo wiem o co Ci chodzi, powiedziałem gdzieś inaczej? Jedyne co mi przychodzi to głowy to może nie fortunne sformułowanie

musisz rozwiązać jeszcze sprawę wyznaczania chwili kiedy faktycznie skorzystasz przetwornika i faktycznie odczytasz te kilka kolejnych próbek

Uściślając, przerwanie od skończenia konwersji wywoływane jest cyklicznie i pomiary są robione najszybciej jako mogą być jednak temat wątku jest o pomiarach "co sekundę" i to co sekundę musisz sobie Autor wyznaczyć. W skrócie wyznaczenia chwili w której faktycznie skorzysta z pomiarów przetwornika.

Link do komentarza
Share on other sites

gohandi,

przetwornik generuje cyklicznie przerwanie i zajmuje mikrokontroler, nie jest tragicznie jeśli masz ustawiony duży prescaler dla zegara taktującego bo w tedy 13 cyknięć zegara ADC dla miktrokontrolera to wieczność

Wynika z tego że w przerwaniu procek oczekuje te 13 taktów 🙂

Link do komentarza
Share on other sites

gohandi, faktycznie chyba lepiej będzie skorzystać z pojedyńczych pomiarów, w takim razie chcę tylko wyklarować swoją wiedzę, czy jak rozpoczyna się konwersja wywołana jakimś innym zdarzeniem automatycznym (ale nie free running) to czy jest ona wykonywana równolegle do działania programu (jak przerwanie), czy zatrzymuje program główny.

Link do komentarza
Share on other sites

Moim zdaniem wynika coś innego, nie odniosłem się w tym zdaniu do czekania jedynie co do upływu czasu. Mianowicie taktując adc z częstotliwością 200KHz przerwanie od ukończenia konwersji jest wywoływane z częstotliwością 15KHz a to znaczy ze 15000*(~20) taktów na obsługę przerwania idzie w eter co dla procesora taktowanego 1MHz stanowi 30% wszystkich taktów w jednej sekundzie. Czyli zajmuje mu zupełnie bez sensu uC. Stąd zwróciłem uwagę, że w tym trybie jest istotna właśnie ta proporcja na ile taktów zegara przypada jedno przerwanie od ukończenia konwersji. Dla dużej wartości prescalera mamy dużą ilość taktów miedzy kolejnymi konwersjami, co symbolicznie nazwałem wiecznością.

Edit

Przerwanie przerywa wykonywanie głównego programu na czas jego obsługi następnie powraca do miejsca w którym wystąpiło, natomiast konwersja jest realizowana przez przetwornik ADC zupełnie niezależnie od głównego programu.

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.