davidpi Napisano Styczeń 29, 2012 Udostępnij Napisano Styczeń 29, 2012 Pozwolę sobie jeszcze raz nadużyć Waszej uprzejmości i znowu zwracam się z prośbą o pomoc Sprawa dotyczy sterownika do serwonapędu (na razie jednego, później rozbuduje do 8). Taktowanie 8MHz. Oto kod programu: #include<avr/io.h> #include<avr/interrupt.h> #include<HD44780.c> #include<util/delay.h> #include<stdlib.h> //------------ F_CPU 8MHz -----------// #define SERWPORT PORTB #define SERWDIR DDRB void PORT_Init() { SERWDIR = 0xFF; SERWPORT = 0x00; } void TIMER1_Init() { TCCR1B |= (1<<WGM12) | (1<<CS11); //Tryb CTC, Preskaler przez 8 OCR1A = 1500; } void TIMER2_Init() { TCCR2 |= (1<<WGM21) | (1<<CS20) | (1<<CS21) | (1<<CS22); //Tryb CTC, preskaler przez 1024 OCR2 = 156; //Odmierzanie 20ms TIMSK |= (1<<OCIE2); //Zezwolenie na przerwanie CTC } ISR(TIMER1_COMPA_vect) { SERWPORT = 0x00; TIMSK &= ~_BV(OCIE1A); } ISR(TIMER2_COMP_vect) { TCNT1=0; SERWPORT = 0xFF; TIMSK |= _BV(OCIE1A); } void LCD() { char buf[5]; itoa(OCR1A,buf,10); LCD_Clear(); LCD_WriteText(buf); _delay_ms(1); } int main() { LCD_Initalize(); PORT_Init(); TIMER1_Init(); TIMER2_Init(); sei(); while(1) { LCD(); } return 0; } Serwo podłączone do dowolnego pinu PORTB. TIMER2 odmierza 20ms i generuje przerwanie. W przerwaniu zerowany jest TIMER1, ustawiany jest PORTB i włączane jest zezwolenie na przerwanie od TIMER1. Mija czas zależny od wartości OCR1A ( ustawiłem na razie na 1,5ms). Po tym czasie TIMER1 generuje przerwanie. W tym przerwaniu PORTB jest zerowany i wyłączane jest zezwolenie na przerwanie od TIMER1. Program się kompiluje. Przerwania się uruchamiają. Jednak serwo ucieka w skrajne położenie i próbuje iść dalej, tak jakby OCR1A była z poza zakresu pracy serwa. Czy ktoś ma jakiś pomysł co może być źle w tym programie? Pozdrawiam Link do komentarza Share on other sites More sharing options...
marek1707 Styczeń 29, 2012 Udostępnij Styczeń 29, 2012 Moim zdaniem błędem jest to, że nie pozwalasz timerowi 2 liczyć. Podzielnik=0x07 oznacza w nim taktowanie z wejścia T2. Ponieważ prawdopodobnie nie masz tam żadnego zegara, timer stoi a na wyjściach SERWPORT będzie ciągłe zero. Muszę sprawdzić moje serwa ale one chyba nie wariują przy braku impulsów PPM. (Trzy minuty później..) Jest tak: niektóre serwa stoją bez ruchu (np. Esky8g), inne poruszają się po włączeniu zasilania kilka-kilkanaście stopni w przypadkową stronę zanim się opamiętają (małe 4.5grama) a jedno (GWS pico std) jedzie dosłownie kilka stopni ale zawsze w tę samą stronę. Twoje chyba są jeszcze innego rodzaju 🙂 Przy okazji: w trybie CTC timer zeruje się PO komparacji z odpowiednim OCR a to oznacza, że licznik będzie miał n+1 stanów. Jeśli chcesz mieć podział przez 156 to musisz wpisać 155. Niby nic ale.. no wiesz, sztuka cierpi 🙂 EDIT: A nie chciałbyś zrobić tego wszystkiego na jednym timerze kręcącym się w kółko (modulo 2^16) z wykorzystaniem funkcji Output Compare? W każdym przerwaniu mógłbyś zmieniać stan swojego automatu, zmieniać stan pinów, obliczać (na podstawie aktualnej wartości i żądanego odstępu czasu) i wpisywać odpowiednią wartość do OCR i spokojnie czekać na następne przerwanie. U mnie to działa bez pudła. Link do komentarza Share on other sites More sharing options...
davidpi Styczeń 29, 2012 Autor tematu Udostępnij Styczeń 29, 2012 Moim zdaniem błędem jest to, że nie pozwalasz timerowi 2 liczyć. Podzielnik=0x07 oznacza w nim taktowanie z wejścia T2. Ponieważ prawdopodobnie nie masz tam żadnego zegara, timer stoi a na wyjściach SERWPORT będzie ciągłe zero. Muszę sprawdzić moje serwa ale one chyba nie wariują przy braku impulsów PPM. Hmm nie rozumiem czemu TIMER2 miałby nie liczyć. Taktowany jest z wewnętrznego zegara przez prescaler 1024. Timer 2 pracuje poprawnie. Sprawdziełem to w ten sposób, że wyłączyłem TIMER1, a do obsługi przerwania TIMER2_COMP_vect wstawiłem taki program: SERWPORT=0xFF; _delay_us(1500); SERWPORT=0x00; i wszystko działało poprawnie. Myślę, że problem leży po stronie TIMER1. Tylko nie wiem gdzie. EDIT: A nie chciałbyś zrobić tego wszystkiego na jednym timerze kręcącym się w kółko (modulo 2^16) z wykorzystaniem funkcji Output Compare? W każdym przerwaniu mógłbyś zmieniać stan swojego automatu, zmieniać stan pinów, obliczać (na podstawie aktualnej wartości i żądanego odstępu czasu) i wpisywać odpowiednią wartość do OCR i spokojnie czekać na następne przerwanie. U mnie to działa bez pudła. Hmm mógłbyś bardziej rozwinąć ideę wykorzystania tylko jednego licznika?? Zaciekawił mnie ten pomysł. Link do komentarza Share on other sites More sharing options...
marek1707 Styczeń 29, 2012 Udostępnij Styczeń 29, 2012 Ups, akurat piszę kod na megę128 i tam przy CS20=CS21=CS22=1 timer 2 napędzany jest z pinu T2, przepraszam. W mniejszych prockach nie ma wejścia T2 i gradacja podzielników rozciąga się na 7 możliwości. Idea jednego timera jest prosta. Puszczasz np. timer 1 swobodnie, z zegarem np. 1us - będzie się łatwiej liczyło a maksymalny czas to 65msec. Wykorzystujesz któryś OCR ale nie pozwalasz mu na sprzętową zmianę pinów - ma tylko zgłaszać przerwania. W odpowiednim ISR piszesz automat "wymyślający" wartość timera przy której ma być zgłoszone następne przerwanie. Może zacznijmy od początku: w inicjalizacji ustawiasz jakiś licznik wyjść na pierwsze wyjście, stan automatu na - nazwijmy to "FSM_SET", wpisujesz do OCR cokolwiek (np. 1000), zerujesz i puszczasz timer. Od tej pory nigdy nie będziesz już modyfikował jego zawartości 'ręcznie". Za 1ms dostaniesz pierwsze przerwanie. Automat będzie w stanie "FSM_SET" więc ustawi wyjście o numerze pobranym z licznika. Na OCR musi zrobić proste dodawanie: OCR += długość_impulsu_PPM[licznik]; Zmieniasz stan automatu na "FSM_RESET" i koniec. Następne przerwanie przyjdzie wtedy, gdy powinieneś wyzerować wyjście. Robisz to bo automat jest w stanie FSM_RESET a OCR modyfikujesz tak: OCR += (2000 - długość_impulsu_PPM[licznik]); Dzięki temu będziesz miał początki impulsów PPM równo rozmieszczone co 2msec. Inkrementujesz licznik modulo liczba wyjść, zmieniasz stan automatu na FSM_SET i koniec. Jeżeli dojdziesz do ostatniego wyjścia modyfikujesz OCR tak, by odczekać przerwę dopełniającą całość do 20ms. Mam nadzieję, że jakoś tego bardzo nie pogmatwałem i coś z tego zrozumiesz. W rzeczywistości w moich projektach używam zewnętrznego dekodera "zezwalanego" odpowiednim wyjściem OC i adresowanego licznikiem wyjść. Dzięki temu program w ogóle nie rusza pinami bo robi to za mnie komparator sprzężony z wyjściem OC a dokładność impulsów jest bezwzględna. 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
davidpi Styczeń 29, 2012 Autor tematu Udostępnij Styczeń 29, 2012 Rozumiem o co chodzi w tej idei z jednym Timerem. dzięki za pomysł. Jednak nadal nie udało mi się rozwiązać mojego problemu. Link do komentarza Share on other sites More sharing options...
BlackJack Styczeń 29, 2012 Udostępnij Styczeń 29, 2012 Przejrzyj sobie ten temat: http://forum.elportal.pl/viewtopic.php?t=10442&postdays=0&postorder=asc&start=30 co prawda rozwiązania w BASCOM AVR, ale ide powinna ci sie sprawdzić, jest tam rozwiązanie na 2 Timerach, jak i na jednym. Link do komentarza Share on other sites More sharing options...
marek1707 Styczeń 29, 2012 Udostępnij Styczeń 29, 2012 Może teraz pójdzie mi lepiej 🙂 Timer pracujący w trybie CTC nie zgłasza przerwania od komparacji z OCR1A (OCF1A) mimo, że de facto porównanie się dokonuje tylko ustawia flagę TOV1, bo się "przepełnia i zeruje". Musisz swoją funkcję "podpiąć" pod inny wektor przerwania. Chyba już kiedyś na to nadepnąłem.. Poza tym praca w trybie CTC spowoduje wielokrotne przerwania (co 1.5ms) i mimo, że ich nie obsługujesz bo są zablokowane, po odblokowaniu wejdą "od razu" i impuls będzie trwał kilka us. Puść timer 1 wolno w trybie normal albo go zatrzymaj albo kasuj flagę przed odblokowaniem przerwań. Link do komentarza Share on other sites More sharing options...
davidpi Styczeń 29, 2012 Autor tematu Udostępnij Styczeń 29, 2012 Timer pracujący w trybie CTC nie zgłasza przerwania od komparacji z OCR1A (OCF1A) mimo, że de facto porównanie się dokonuje tylko ustawia flagę TOV1, bo się "przepełnia i zeruje". Musisz swoją funkcję "podpiąć" pod inny wektor przerwania. Chyba już kiedyś na to nadepnąłem.. Poza tym praca w trybie CTC spowoduje wielokrotne przerwania (co 1.5ms) i mimo, że ich nie obsługujesz bo są zablokowane, po odblokowaniu wejdą "od razu" i impuls będzie trwał kilka us. Puść timer 1 wolno w trybie normal albo go zatrzymaj albo kasuj flagę przed odblokowaniem przerwań. To może być przyczyną. Te przerwania od TIMER1 mogą się nawarstwiać i po odblokowaniu się wykonują wszystkie. Jutro przetestuję to na urządzeniu i dam znać. Link do komentarza Share on other sites More sharing options...
marek1707 Styczeń 29, 2012 Udostępnij Styczeń 29, 2012 "Nawarstwi" i obsłuży się tylko jedno - choć w czasie 20ms przyjdzie ich więcej - bo tylko 1-bitowe są flagi zgłoszeń przerwań. W każdym razie to wystarczy, żeby zepsuć impuls skracając go do kilku us. 1 Link do komentarza Share on other sites More sharing options...
davidpi Styczeń 30, 2012 Autor tematu Udostępnij Styczeń 30, 2012 "Nawarstwi" i obsłuży się tylko jedno - choć w czasie 20ms przyjdzie ich więcej - bo tylko 1-bitowe są flagi zgłoszeń przerwań. W każdym razie to wystarczy, żeby zepsuć impuls skracając go do kilku us. Tak. Miałeś racje. Impuls wypełnienia wynosił ok 4us. Rozwiązałem problem poprzez wyłączanie TIMER1 gdy nie jest potrzebny. Oto poprawiony kod, może się komuś przyda. #include<avr/io.h> #include<avr/interrupt.h> #include<HD44780.c> #include<util/delay.h> #include<stdlib.h> //------------ F_CPU 8MHz -----------// #define SERWPORT PORTB #define SERWDIR DDRB #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)); #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)); int dana=0; int Odczyt=0; void PORT_Init() { SERWDIR = 0xFF; SERWPORT = 0x00; } void TIMER1_Init() { TCCR1B |= (1<<WGM12); //Tryb CTC, Preskaler przez 8 OCR1A = 2200; } void TIMER2_Init() { TCCR2 |= (1<<WGM21) | (1<<CS20) | (1<<CS21) | (1<<CS22); //Tryb CTC, preskaler przez 1024 OCR2 = 156; //Odmierzanie 20ms TIMSK |= (1<<OCIE2); //Zezwolenie na przerwanie CTC } ISR(TIMER1_COMPA_vect) { TCCR1B &= ~_BV(CS11); SERWPORT = 0x00; TIMSK &= ~_BV(OCIE1A); } ISR(TIMER2_COMP_vect) { TCNT1=0; TCCR1B |= _BV(CS11); //Uruchomienie TIMER1 z preskalerem 8 SERWPORT = 0xFF; TIMSK |= _BV(OCIE1A); } void LCD() { char buf[5]; itoa(OCR1A,buf,10); LCD_Clear(); LCD_WriteText(buf); _delay_ms(5); } int main() { LCD_Initalize(); PORT_Init(); TIMER1_Init(); TIMER2_Init(); sei(); while(1) { LCD(); } return 0; } 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ę »