Elvis Napisano Lipiec 27, 2010 Udostępnij Napisano Lipiec 27, 2010 Poprzednia lekcja Lekcja 6 - Zegary i przerwania (ciąg dalszy) Następna lekcja W poprzedniej lekcji uruchomiliśmy PLL oraz Timer0. Jeśli było to nieco niezrozumiałe, nie ma czym się przejmować. Pętli PLL nie będziemy więcej ruszać. Wystarczy wywołać procedurę uruchamiającą i można o PLL (na razie) zapomnieć. Podobnie będzie z Timer0. Zmienimy tylko częstotliwość wywoływania przerwań i więcej nie będziemy się zagłębiać w działanie timer-a. Obecna konfiguracja to wywoływanie przerwania co 1s. Jest to zdecydowanie za rzadko. Ustalmy jakiś inny okres - przykładowo 100us (czyli 10.000 razy na sekundę). W tym celu zmieniamy linijkę: T0_MR0 = 1500; Od tej pory nie będziemy wracali do procedury timer0_init(). Nasze przerwanie jest wołane 10000 na sekundę, więc do sterowania diodami raczej się nie nadaje. Wykorzystamy je jako licznik czasu. Nowa procedura będzie wyglądała następująco: volatile unsigned int t0_counter = 0; void timer0(void) __attribute__ ((interrupt ("IRQ"))); void timer0(void) { if (T0_IR&0x01) { if (t0_counter) t0_counter--; } T0_IR = 0xff;//0x01; VICVectAddr = 0; } Zmienna t0_counter przechowuje nasz licznik. Jest on co 100us zmniejszany o 1. Gdy osiągnie 0 jest zatrzymywany. Do czego nam się to może przydać? Wreszcie możemy napisać znane z AVR procedury delay_us() oraz delay_ms(). Niestety nie będziemy mogli odmierzać czasu z dokładnością do pojedynczych mikrosekund. void delay_100us(unsigned int dly) { t0_counter = dly; while (t0_counter); } void delay_ms(unsigned int ms) { t0_counter = ms*10; while (t0_counter); } Procedury działają praktycznie tak samo - ustawiają naszą zmienną licznikową, po czym czekają, aż zostanie wyzerowana. Bardzo ważne jest zadeklarowanie zmiennej t0_counter jako volatile. Inaczej optymalizator może wyrzucić nasze pętle while z kodu. Teraz możemy napisać bardzo prosty program migający diodami co pół sekundy: int main(void) { GPIO1_IODIR |= LED_MASK; pll_init(); timer0_init(); while (1) { led_swap(); delay_ms(500); } } Pełny kod znajdziemy w pliku program10.zip Program10.zip Link do komentarza Share on other sites More sharing options...
OldSkull Lipiec 28, 2010 Udostępnij Lipiec 28, 2010 Nie do końca się zgodzę, że pomiar wartości będących wielokrotnościami 100us będzie dokładny: z racji iż funkcja opóźniająca jest wywoływana w losowej chwili czasu, a timer będzie naliczał co 100us, to będziemy mieć niedokładność w przedziale 0-100us (rozkład ciągły prawdopodobieństwa), średnio 50us. Należałoby w funkcjach opóźniajacych dodać uruchamianie timera. Może starczy dodać na poczatku każdej z nich: timer0_init(); albo po prostu T0_TC=0; Jak sprawdzić czy to się zgadza? Zostawcie przerwanie co 1s, zostawcie stary celay i w while(1) dajcie: delay(500000); led_swap(); delay_100us(1); Prawidłowo całkowity delay powienien wynosić 1+nie_wiadomo_ile_z_delay(500000) - sprawdźcie z zegarkiem, bez zerowania licznika Timera będzie iść takt w takt ze wskazówką. Jeśli dodacie zerowanie, będzie sie spóźniać, ale jeśli usuniecie dodatkowe opóźnienie, będzie ok. Link do komentarza Share on other sites More sharing options...
Elvis Lipiec 28, 2010 Autor tematu Udostępnij Lipiec 28, 2010 W tekście usunąłem informację o dużej dokładności. Błąd może wynosić 100µs, ale przy dłuższych opóźnieniach (rzędu setek ms) nie ma to znaczenia. Nie chciałem modyfikować licznika timera ponieważ w dalszych częściach ten sam timer będzie wykorzystywany do kilku funkcji. Nadal wolny jest Timer1, więc jeśli potrzebujemy bardzo dokładnie mierzyć czasy rzędu mikrosekund można go wykorzystać. Link do komentarza Share on other sites More sharing options...
OldSkull Lipiec 29, 2010 Udostępnij Lipiec 29, 2010 Hmm. W przypadku ATmeg dało się naprawdę dopracować delaya z pętlą - dokładność do 0.5 promila (wg symulatora). Tutaj dałoby się uzyskać podobną dokładność(która jest gorsza od tej przy użyciu Timera, ma sporo wad, ale nie wykorzystuje się Timera)? Czasami aż szkoda wykorzystywać dodatkowo Timer :/ Głównie chodzi mi o to, czy on sobie jakoś dodatkowo przyspiesza? W AVRach była instrukcja na takt zegara (przez co to działało), tutaj zdaje się czasami wykona więcej instrukcji w jednym takcie. Link do komentarza Share on other sites More sharing options...
Polecacz 101 Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Zarejestruj się lub zaloguj, aby ukryć tę reklamę. 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
Elvis Lipiec 29, 2010 Autor tematu Udostępnij Lipiec 29, 2010 Aktywne pętle to nie najlepszy pomysł. Po pierwsze jeśli chcemy, aby były dokładne musimy wyłączyć przerwania. Inaczej błędy będą dowolnie duże - w zależności ile razy wywołane zostanie przerwanie. Kolejny problem to pobór prądu. Procesor bierze dużo prądu, gdy czeka w pętli. Dla ARM najlepszym rozwiązaniem jest RTOS i co prawda mniejsza dokładność odmierzania czasu, ale za to wykorzystanie mocy procesora. Jeśli potrzebujemy bardzo krótkie, dokładne pętle (czasy pojedynczych µs), to można zrobić jak na AVR. Najlepiej jest napisać to w asemblerze, inaczej kompilator (dokładniej - optymalizator) popsuje nam wyliczenia. Link do komentarza Share on other sites More sharing options...
Pomocna odpowiedź
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ę »