mopsiok Napisano Marzec 20, 2012 Udostępnij Napisano Marzec 20, 2012 Witam wieczorową porą i z góry przepraszam forumową brać za tak prymitywny problem 😋. Chodzi o to, że nie mogę się dogadać z moją Atmegą jeśli chodzi o Timer0. Nie mam nawet pojęcia dlaczego. Poczytałem sporo artykułów, przerobiłem książkę pana Borkowskiego, nie ominąłem również poczciwej dokumentacji Atmela. I nic. Nawet najprostszy kod nie działa. Mam na myśli coś takiego: #include <avr/io.h> #define F_CPU 16000000 #include <avr/interrupt.h> ISR (TIMER0_OVF_vect) { PORTA = 0; } ISR (BADISR_vect) {} int main(void) { DDRA = 255; DDRC = 255; PORTA = 255; PORTC = 0; TCNT0 = 0; //ustaw na poczatek TCCR0 = (1<<CS01); //ustaw preskaler timera0 na 8 TIMSK = (1<<TOIE0); //zezwol na przerwanie przy przepelnieniu sei(); for(;;) return 0; } Ten kod jest tylko testowy - po podłączeniu zasilania dioda podłączona pod port A powinna zaświecić na ułamek sekundy, a potem na stałe zgasnąć. Niestety świeci od pół godziny 🤣. Ogólnie to chciałbym wywoływać przerwanie co 40us, do czego zamierzam wykorzystać timer0 w trybie porównywania TCNT0 z OCR0. Wtedy ustawiając OCR0 na 80 powinienem otrzymać przerwanie co dokładnie 40us. No ale nie mogę tego osiągnąć jeśli timer nie chce mi działać nawet w domyślnej konfiguracji... Czy ma ktoś jakiekolwiek przypuszczenia co może być przyczyną? Siedzę nad tym kilka godzin i już średnio wytrzymuję nerwowo. Dzięki i pozdrawiam mopsiok Link do komentarza Share on other sites More sharing options...
_ender88 Marzec 20, 2012 Udostępnij Marzec 20, 2012 ISR (TIMER0_OVF_vect) { Usuń spację między ISR a nawiasem i spróbuj jeszcze raz. Link do komentarza Share on other sites More sharing options...
mopsiok Marzec 20, 2012 Autor tematu Udostępnij Marzec 20, 2012 Niestety, nic nie pomogło... W książce Borkowskiego jest napisane, że raz na kilka miesięcy programy w C po prostu nie chcą działać 🤣. Czy w tym wypadku może właśnie o to chodzić? Link do komentarza Share on other sites More sharing options...
rezolut Marzec 20, 2012 Udostępnij Marzec 20, 2012 Dziś dopiero zacząłem bliżej poznawać co to C, ale nie podoba mi się ta linia for(;;) Czy nie powinna być taka? for(;;) { } 1 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
mopsiok Marzec 20, 2012 Autor tematu Udostępnij Marzec 20, 2012 Ooo nie wierzę 🤯... po prostu nie wierzę. Taki durny błąd... powiem szczerze że akurat tę linijkę napisałem na szybciora i nawet się nad nią nie zastanowiłem myśląc, że bez klamer też będzie okej. Mój Mistrzu, leci Piwo Mocy bo naprawdę się należy! 🙂 Proszę jeszcze nie wywalać tematu, bo czeka mnie jeszcze sprawienie, by przerwanie występowało co 40us, więc mogą pojawić się problemy. Dzięki Panowie za zainteresowanie i pomoc. //Edit: Dobra, problem rozwiązany w stu procentach. Na wypadek gdyby komuś się przydało - wrzucam kod: #include <avr/io.h> #define F_CPU 16000000 #include <avr/interrupt.h> int licznik = 0; ISR(TIMER0_COMP_vect) { licznik++; if (licznik == 25000) { //40us * 25000 = 1s; sprawdzenie dzialania PORTA = ~PORTA; licznik = 0;} } ISR (BADISR_vect) {} int main(void) { DDRA = 255; DDRC = 255; PORTA = 255; PORTC = 0; TCNT0 = 0; //ustaw na poczatek OCR0 = 80; //gdy licznik dojdzie do 80, wykonaj kod z przerwania TCCR0 = (1<<WGM01) | (1<<CS01); //preskaler 8, tryb porownania z OCR0 TIMSK = (1<<OCIE0) | (1<<TOIE0); //odblokowanie przerwania porownania i przepelnienia sei(); for(;;) return 0; } Link do komentarza Share on other sites More sharing options...
koval_blazej Marzec 21, 2012 Udostępnij Marzec 21, 2012 albo for(;😉; ale najlepiej jakby był sprawdzany jakikolwiek warunek więc raczej while(1); W książce Borkowskiego jest napisane, że raz na kilka miesięcy programy w C po prostu nie chcą działać 🤣. Czy w tym wypadku może właśnie o to chodzić?To raczej subiektywne wrażenie. Zwykle po kilku godzinach ślepienia w kod znajdujesz błąd tak głupi że po prostu wstyd się przyznać; Hm, wkleiłeś to samo co było 😉 Link do komentarza Share on other sites More sharing options...
marek1707 Marzec 21, 2012 Udostępnij Marzec 21, 2012 Ale zaraz, to ja też chcę zrozumieć. Znaczy kapuję, że instrukcja for potraktowała instrukcję return jako ciało pętli i zechciała ją powtarzać. Conajmniej raz to się udało, bo return od razu zwróciło sterowanie z main do "rozbiegówki" która tę funkcję wywołała. Tylko, że tam jest tylko skok do samego siebie. Jeżeli nie ma tam globalnej blokady przerwań a o ile pamiętam nie ma, wszystko powinno być OK. Za odliczony czas powinno wejść przerwanie i zgasić diodę. Więc jak to było naprawdę? No dobra, sprawdziłem, po wyjściu z main przerwania są blokowane i procesor "zamiera" w nieskończonej pętli - pamięć zaczyna mnie zawodzić. Link do komentarza Share on other sites More sharing options...
rezolut Marzec 21, 2012 Udostępnij Marzec 21, 2012 Zamiera w nieskończonej pętli, czy po prostu kończy działanie programu? Jeśli return wykonał się raz, to wskaźnik programu (czy jak on się fachowo nazywa?) powinien wskazać, na następną instrukcję po wywołaniu funkcji main a takowych instrukcji już nie ma, więc nastąpił end. Czy nie dlatego przerwania zostały zablokowane? Link do komentarza Share on other sites More sharing options...
dondu Marzec 21, 2012 Udostępnij Marzec 21, 2012 1. Zmienna licznik powinna być volatile: http://mikrokontrolery.blogspot.com/2011/04/problemy-c-volatile.html 2. Return 0 Return jest zbędny ponieważ nie jest to program działający pod nadrzędnym systemem operacyjnym, który go wywałał i po skończeniu realizacji program zwraca 0. W Twoim przypadku mikrokontroler ma kręcić się w kółko w pętli. Dla porządku przejście do nowej linii bez zakończenia poprzedniej znakiem średnika: for(;😉 return 0; oznacza, że wykonujesz tak naprawdę: for(;😉 return 0; czyli pętlę nieskończoną wywołań rozkazu return 0. Ale ponieważ wywołanie return 0 wprowadza mikrokontroler w maliny, to pętla nie jest wykonywana, a mikrokontroler zaczyna robić głupoty, skacząc do bliżej nieokreślonej części pamięci programu - sprawdź w symulatorze. Prawidłowe pętle główne to: for(;😉; //lub while(1); lub z programem w środku: for(;😉{ ... jakiś program } //lub while(1){ ... jakiś program } Wywal Return 0. Reasumując: Return 0 w main() jest zabronione, podobnie jak inne każde inne Return 🙂 Link do komentarza Share on other sites More sharing options...
rezolut Marzec 21, 2012 Udostępnij Marzec 21, 2012 A czy gdyby po int main(void) { DDRA = 255; DDRC = 255; PORTA = 255; PORTC = 0; return 0; } była inna funkcja, np. wywoływana z main int funkcja() { return 0; } nie byłaby ona wykonana po wyskoczeniu z main? Co zatem kończy wykonywanie programu? W Bascom po "End" program się kończy i procesor nie robi nic. Tzn. nie wiem, czy nic nie robi i nie potrafię tego jednoznacznie stwierdzić. Podejrzewam tylko, że trafiając na ostatni adres rozkazu do wykonania, po jego wykonaniu nie zwiększa wskaźnika programu, czyli chyba de facto "zawiesza się". Link do komentarza Share on other sites More sharing options...
dondu Marzec 21, 2012 Udostępnij Marzec 21, 2012 Co zatem kończy wykonywanie programu? W Bascom po "End" program się kończy i procesor nie robi nic. Po pierwsze dlaczego miałby się zakończyć i stanąć? Cóż to za projekt? Po drugie BASCOM-owe End nie czyni cudów zatrzymując mikrokontroler, tylko zapewne wyłączyło przerwania globalne i wprowadziło mikrokontroler w pętlę nieskończoną. I Ty zrób to samo. Return w funkcji powoduje wyjście z funkcji. Return w main powoduje to co opisałem w poprzednim poście. Podejrzewam tylko, że trafiając na ostatni adres rozkazu do wykonania, po jego wykonaniu nie zwiększa wskaźnika programu, czyli chyba de facto "zawiesza się". Co znaczy "zawiesza się"? Jest taki rozkaz w datasheet mikrokontrolera? BASCOM to nie cudotwórca i takiej funkcji mikrokontrolerowi nie wciśnie w jego tranzystory. To właśnie przykład złych nawyków, które uczy BASCOM, a które ciągną się przy przesiadce na dowolny inny język wyższego poziomu. Link do komentarza Share on other sites More sharing options...
marek1707 Marzec 21, 2012 Udostępnij Marzec 21, 2012 "Jeśli return wykonał się raz, to wskaźnik programu (czy jak on się fachowo nazywa?) powinien wskazać, na następną instrukcję po wywołaniu funkcji main a takowych instrukcji już nie ma, więc nastąpił end. Czy nie dlatego przerwania zostały zablokowane?" Nazywa się "licznik instrukcji" ale tłumaczeń zwrotu Program Counter jest wiele. Ależ oczywiście, że wskazuje na następną instrukcję po instrukcji wywołania funkcji main() i oczywiście są tam jakieś instrukcje bo nie może ich "nie być" - każda kombinacja bitów w pamięci programu jest jakąś instrukcją. Inna sprawa, czy prawidłową i czy sensownie wstawioną. Nie,nie daltego zostały zablokowane, że nie ma żadnej instrukcji tylko dlatego, że szkielet programu w C (to coś co wykonuje sie przed main i zaraz po nim) przewiduje wykonanie instrukcji zablokowania przerwań i skok do samego siebie wykonywany w nieskończoność aż do resetu lub wyłączenia zasilania. "Ale ponieważ wywołanie return 0 wprowadza mikrokontroler w maliny," "Return 0 w main() jest zabronione, podobnie jak inne każde inne Return " Nie wprowadza w żadne maliny i nie pisz nie sprawdzonych informacji. Zajrzyj do zrzutu asemblerowego (plik .lss) i zobacz co tam się dzieje po main. Z tego powodu return w main nie jest zabronione i jak najbardziej dopuszczalne. Sterowanie oddawane jest z powrotem do "rozbiegówki" czyli fragmentu kodu który po resecie ustawia wskaźnik stosu, zeruje zmienne które mają być wyzerowane i wpisuje wartości do zmiennych statycznych którym wartości muszą być nadane przed startem funkcji main. Potem wywoływana jest funkcja main normalną instrukcją CALL a po powrocie z tamtąd wykonywany jest skok do etykiety _exit gdzie znajdziesz instrukcję wyłączenia przerwań i skok do samego siebie, co de facto zawiesza procesor do końca (jego) życia. Nawiasem mówiąc istnieją specjalnie przewidziane do tego sekcje kodu, do których możemy przy pomocy specjalnych atrybutów wpisywać nasz własny kod (choćby w C) i wykonywać go przed lub po main(). To przydaje się np. do sytuacji, gdy mamy duży zewnętrzny RAM dołączony do ATmegi. Jak z niego skorzystać w sposób naturalny w C, gdy domyślnie ta pamięć nie jest widoczna i trzeba odblokować ją wpisami do rejestru procesora? Jeśli zrobimy to w main, już będzie za późno. Zmienne tam przechowywane nie dostaną swoich wartości początkowych bo "rozbiegówka" nie ma dostępu do tej pamięci. "wyjscie z maina nie powoduje zakończenia programu, a przejsćie do pamięci niekoniecznie przeznaczonej na program" Nieprawda. Wyjście z maina jest kontrolowane i wykonywany jest ściśle określony ciąg instrukcji. Nie ma żadngo wyjścia do pamięci niekoniecznie przeznaczonej na program. Z resztą w architekturach typu Harvard (a takie są nasze AVRy) nie ma szans wykonywać kodu z pamięci danych. "Podejrzewam tylko, że trafiając na ostatni adres rozkazu do wykonania, po jego wykonaniu nie zwiększa wskaźnika programu, czyli chyba de facto "zawiesza się"." AVR nie ma instrukcji STOP albo HALT jak niegdysiejsze procesory zatrzymującej sekwencer instrukcji. Nie licząc trybów uśpienia (ale to zupenie inna bajka i z innego powodu wprowadzona) po każdej instrukcji PC zwiększa się i zaczynany jest cykl pobrania następnego kodu operacyjnego. Procesor zawieszany jest poprzez wykonanie instrukcji wyłączenia wszelkich przerwań i nieskończone wykonywanie prostego skoku do samego siebie. Nie ma czegoś takiego jak "ostatni adres rozkazu do wykonania" - skąd procesor miałby niby wiedzieć, co jest ostatnie do wykonania? Chłopaki, co jest? Ilu ludzi tyle poglądów i zabobonów? Może by tak który zajrzał do kodu wynikowego i listy instrukcji zamiast pleść takie rzeczy? 1 Link do komentarza Share on other sites More sharing options...
rezolut Marzec 21, 2012 Udostępnij Marzec 21, 2012 Dzięki za obszerne wyjaśnienie. Tak mógł napisać tylko ktoś kto zna asm 😃 Link do komentarza Share on other sites More sharing options...
dondu Marzec 21, 2012 Udostępnij Marzec 21, 2012 Nie wprowadza w żadne maliny i nie pisz nie sprawdzonych informacji. Zajrzyj do zrzutu asemblerowego (plik .lss) i zobacz co tam się dzieje po main. No to popatrzy 🙂 Program: #include <avr/io.h> int main(void) { return 0; } kod wynikowy: ---- UNKNOWN_FILE --------------------------------------------------------------------------------- 0: File not found +00000000: 940C002A JMP 0x0000002A Jump +00000002: 940C0034 JMP 0x00000034 Jump +00000004: 940C0034 JMP 0x00000034 Jump +00000006: 940C0034 JMP 0x00000034 Jump +00000008: 940C0034 JMP 0x00000034 Jump +0000000A: 940C0034 JMP 0x00000034 Jump +0000000C: 940C0034 JMP 0x00000034 Jump +0000000E: 940C0034 JMP 0x00000034 Jump +00000010: 940C0034 JMP 0x00000034 Jump +00000012: 940C0034 JMP 0x00000034 Jump +00000014: 940C0034 JMP 0x00000034 Jump +00000016: 940C0034 JMP 0x00000034 Jump +00000018: 940C0034 JMP 0x00000034 Jump +0000001A: 940C0034 JMP 0x00000034 Jump +0000001C: 940C0034 JMP 0x00000034 Jump +0000001E: 940C0034 JMP 0x00000034 Jump +00000020: 940C0034 JMP 0x00000034 Jump +00000022: 940C0034 JMP 0x00000034 Jump +00000024: 940C0034 JMP 0x00000034 Jump +00000026: 940C0034 JMP 0x00000034 Jump +00000028: 940C0034 JMP 0x00000034 Jump +0000002A: 2411 CLR R1 Clear Register +0000002B: BE1F OUT 0x3F,R1 Out to I/O location +0000002C: E5CF LDI R28,0x5F Load immediate +0000002D: E0D4 LDI R29,0x04 Load immediate +0000002E: BFDE OUT 0x3E,R29 Out to I/O location +0000002F: BFCD OUT 0x3D,R28 Out to I/O location +00000030: 940E0036 CALL 0x00000036 Call subroutine <---- skok do main() +00000032: 940C0039 JMP 0x00000039 Jump <---- skok do wyłączenia przerwań +00000034: 940C0000 JMP 0x00000000 Jump <--- skok do początku pamięci programu @00000036: main ---- PROBY.c -------------------------------------------------------------------------------------- 4: { +00000036: E080 LDI R24,0x00 Load immediate <--- początek main() +00000037: E090 LDI R25,0x00 Load immediate +00000038: 9508 RET Subroutine return <---- return z programu w C 8: } +00000039: 94F8 CLI Global Interrupt Disable +0000003A: CFFF RJMP PC-0x0000 Relative jump <---- skok do początku programu Czyli robi dokładnie to co napisał marek1707: Sterowanie oddawane jest z powrotem do "rozbiegówki" czyli fragmentu kodu który po resecie ustawia wskaźnik stosu, zeruje zmienne które mają być wyzerowane i wpisuje wartości do zmiennych statycznych którym wartości muszą być nadane przed startem funkcji main. ... Ale czy to nie są właśnie maliny? Ja uważam, że właśnie są to maliny w szczególności dla autora tematu któremu podpowiadam, ponieważ return 0 nie oznacza tego samego co RESET mikrokontrolera. Nawet jeżeli przyjąć, że ktoś świadomie chce zresetować programowo mikrokontroler, to wykonanie Return 0 jest co najmniej niebezpieczne dlatego, że taki "restart" nie zeruje chociażby bitów w rejestrach przez co można się naciąć na nieprawidłowe działanie programu po takim restarcie. Dlatego nadal podtrzymuję, że: Reasumując: Return 0 w main() jest zabronione, podobnie jak każde inne Return w szczególności dla początkującego w C, którym jest autor tematu. Dodatkowa mniej istotna uwaga: Taki kod powstaje w kompilatorze GCC, ale nie koniecznie w innym. Pomijam już fakt, że autorowi tematu nie chodziło o to, by zaczynał program od nowa, tylko by zatrzymał się w miejscu tak jak robił to do tej pory w BASCOM. ------------------------------- BTW: Wyznaję zasadę, że początkujący oczekuje minimum informacji bez zbędnych szczegółów, co zresztą pochwaliłeś: Podziwiam Cię dondu za umiejętność ograniczenia się do niezbędnego minimum 🙂 tutaj: https://www.forbot.pl/forum/postlink/61253.htm#61253 Takim minimum dla początkującego jest zasada: Return 0 w main() jest zabronione, podobnie jak każde inne Return BTW 2: Umieszczę tę zasadę na blogu 😉 w odpowiednim artykule. Link do komentarza Share on other sites More sharing options...
rezolut Marzec 21, 2012 Udostępnij Marzec 21, 2012 Czy +0000003A: CFFF RJMP PC-0x0000 Relative jump <---- skok do początku programu nie jest raczej zapętleniem programu na adresie 3A? To chyba cos innego niż skok do początku programu? 😐 A jeszcze wcześniej piszesz: Ale ponieważ wywołanie return 0 wprowadza mikrokontroler w maliny, to pętla nie jest wykonywana, a mikrokontroler zaczyna robić głupoty, skacząc do bliżej nieokreślonej części pamięci programu - sprawdź w symulatorze To ja, jako zupełnie zielony pytam: jak to w końcu jest? Bo na moje niewprawione w asm oko Return 0 niczego tu nie psuje, ale mogę się mylić. 🙁 2 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ę »