Autor: Dondu
Bardzo często początkujący trafiają na sugestie wykorzystania niestandardowych narzędzi programistycznych pozwalających na pisanie programów (np. Eclipse) lub starych narzędzi typu WinAVR.
I choć niektóre z nich jako zwykłe edytory kodu programu są przyzwoite, o tyle nie posiadają dodatkowych funkcji bardzo istotnych w szczególności dla początkującego. Jedną z takich funkcji jest symulator mikrokontrolerów AVR dostępny w oryginalnych narzędziach AVR Studio i Atmel Studio.
Symulator jest bardzo pożytecznym narzędziem ułatwiającym znalezienie błędów w pisanym programie. Ma on olbrzymie możliwości symulowania pracy mikrokontrolera. Niniejszy artykuł jest jedynie zachętą do wykorzystania tego narzędzia, które oszczędza czas i zmniejsza frustrację początkujących, dodatkowo pokazując jak mikrokontroler realizuje powierzone mu zadania.
Przykład problemu
Na forum pojawił się wpis pod tytułem Atmega8 - Czy mam uszkodzony uC?
Program (po usunięciu zbędnych linijek) wyglądał tak (formatowanie widoczne jak w temacie na forum):
#include <avr/io.h> int main(void) { DDRD = 0xFF; DDRC = 0x00; PORTC = 0x0f; while(1) if(!(PINC&(1<<PC4))) PORTD = (1<<PD0); if(!(PINC&(1<<PC2))) PORTD = (1<<PD2); if(!(PINC&(1<<PC3))) PORTD = (1<<PD1); }
Autor nie mógł znaleźć przyczyny takiego zachowania programu:
polarczn
Wgrywam ten kod i ciągle pali mi się 1 dioda led. Dlaczego?
Wgrywam ten kod i ciągle pali mi się 1 dioda led. Dlaczego?
Podczas kompilacji program nie generował żadnych warningów (ostrzeżeń), ani błędów:
AVR Memory Usage
----------------
Device: atmega8
Program: 80 bytes (1.0% Full)
(.text + .data + .bootloader)
Data: 0 bytes (0.0% Full)
(.data + .bss + .noinit)
Build succeeded with 0 Warnings...
----------------
Device: atmega8
Program: 80 bytes (1.0% Full)
(.text + .data + .bootloader)
Data: 0 bytes (0.0% Full)
(.data + .bss + .noinit)
Build succeeded with 0 Warnings...
W związku z tym autor miał utrudnioną sytuację, by błąd odnaleźć. W takiej sytuacji idealnie pomocnym narzędziem jest właśnie symulator.
Poniżej króciutkie filmy pokazujące wykorzystanie symulatora do określenia przyczyn niepoprawnego działania powyższego programu w środowiskach AVR Studio 4.18 i Atmel Studio.
Program został minimalnie inaczej sformatowany - dodałem Enter po pierwszym warunku, by móc zaobserwować działanie symulatora i być pewnym, kiedy symulator realizuje sprawdzanie warunku, a kiedy wykonuje warunek (gdy jest on spełniony).
Jeżeli bym tego nie zrobił, to podczas pracy symulatora nie byłoby widać co dokładnie w linii z warunkiem symulator wykonuje.
Oddzieliłem także Enterem dwa pozostałe warunki, by łatwiej było obserwować wykonywanie programu.
Jeżeli bym tego nie zrobił, to podczas pracy symulatora nie byłoby widać co dokładnie w linii z warunkiem symulator wykonuje.
Oddzieliłem także Enterem dwa pozostałe warunki, by łatwiej było obserwować wykonywanie programu.
Program nadal jest oczywiście taki sam, a na potrzeby symulacji wygląda więc następująco:
#include <avr/io.h> int main(void) { DDRD = 0xFF; DDRC = 0x00; PORTC = 0x0f; while(1) if(!(PINC & (1<<PC4))) PORTD = (1<<PD0); if(!(PINC & (1<<PC2))) PORTD = (1<<PD2); if(!(PINC & (1<<PC3))) PORTD = (1<<PD1); }
AVR Studio 4.18
... i to samo, ale w Atmel Studio
Jak widzisz bez programowania mikrokontrolera, bardzo szybko można się zorientować jak działa program stwierdzając, że nie wykonuje się znaczna jego część, czyli dwa ostatnie warunki if().
Na filmach zapomniałem pokazać jak stwierdzić, że dioda LED świeci. Wystarczy po prostu podglądać stan odpowiednich pinów portu D (kliknij PORTD), do którego są podłączone diody.
Czy widzisz już na czym polega błąd w programie?
Znamy już skutki i teraz należy ustalić, co w programie powoduje działanie inne niż autor się spodziewał, a przyczyna jest prosta: Brak nawiasów klamrowych określających, która część programu ma być realizowana w pętli głównej while().
Ponieważ autor chciał wykonać w pętli wszystkie trzy warunki, powinien był dodać nawiasy klamrowe w następujący sposób:
Przykład 3 (w kompilatorze)
#include <avr/io.h> int main(void) { DDRD = 0xFF; DDRC = 0x00; PORTC = 0x0f; while(1) { if(!(PINC&(1<<PC4))) PORTD = (1<<PD0); if(!(PINC&(1<<PC2))) PORTD = (1<<PD2); if(!(PINC&(1<<PC3))) PORTD = (1<<PD1); } }
Zauważ jak poprawnie, a przez to czytelnie jest sformatowany powyższy kod programu. Takie poprawne formatowanie znacznie ułatwia szybkie wzrokowe odnalezienie błędu.
Tematy związane z nawiasami klamrowymi dot. tego przypadku:
Prawdopodobny drugi błąd
Czytelnik ukrywający się pod Anonimowy, w pierwszym komentarzu do tego artykułu słusznie zauważył, że program włącza rezystory pull-up tylko dla przycisków na pinach od PC0 do PC3, ale pin PC4 pozostawia bez włączonego pull-up.
Jeżeli więc autor tematu nie podłączył zewnętrznego rezystora, to przycisk na pinie wejściowym PC4 nie będzie pracować prawidłowo, ponieważ nic nie wymusi na nim stanu wysokiego podczas puszczenia przycisku. Jeżeli tego nie zrobił to symulator nie jest w stanie tego przewidzieć, a my na filmiku symulowaliśmy za pomocą PC4 prawidłowe działanie przycisku.
Ale problem ten można było stwierdzić już na etapie wykonywania instrukcji:
PORTC = 0x0f;
zaznaczonej czerwoną strzałką:
Stan po wykonaniu ustawienia portu C. |
Powyższy ekran przedstawia stan po wykonaniu powyższej instrukcji, kiedy to symulator zatrzymał się na pierwszym warunku (żółta strzałka). Możemy przyglądnąć się stanowi rejestrów odpowiedzialnych za piny portu C.
Rejestry portu C. |
Rejestr DDRC odpowiedzialny na kierunki pinów portu C ustawiony jest na zera (jasne kwadraty), czyli wszystkie piny są wejściami.
W przypadku rejestru PORTC odpowiedzialnego (dla pinów wejściowych) za włączenie rezystorów pull-up możemy zauważyć, że jeżeli autor planował włączyć rezystor pull-up na pinie PC4, to pin ten w rejestrze PORTC powinien być ustawiony na jedynkę (ciemny kwadrat) tak ja piny PC0-PC3. Jak widać tak się nie stało, co od razu powinno zostać przez autora wychwycone, a program poprawiony, czyli:
PORTC = 0x1f;
Powyższy błąd nie zmienia podstawowego problemu opisanego w niniejszym artykule i na filmach dot. pozostałych przycisków PC2 i PC3.
Inne funkcjonalności symulatora
Oprócz prostego sprawdzania stanu rejestrów oraz dokonywania zmian stanu pinów wejściowych i wyjściowych, symulator ma olbrzymie możliwości. Dlatego warto przyglądnąć się jego okienku I/O View:
AVR Studio 4.18 |
... i analogicznie w przypadku Atmel Studio.
Wykorzystujcie oryginalne narzędzia producentów mikrokontrolerów tym bardziej, gdy są darmowe. Naprawdę warto!!!
Hm ciekawostka. Nigdy tego nie uzywalem. Ale i tak na tym etapie nie zostal wykryty jeszcze jeden maly blad programowy. Chodzi o pin PC4-nie ma on podciagania do plusa zasilania i program moglby troche wariowac przez ten jeden przycisk;)
OdpowiedzUsuńSłuszna uwaga, ponieważ autor nie dodawał zewnętrznych rezystorów pull-up. Tego niestety nawet symulator nie wykryje. :-)
UsuńDopisałem Twoją uwagę na forum: tutaj
UsuńDodałem opis tego potencjalnego błędu - dziękuję za jego wychwycenie :-)
UsuńJacku dziękuję ci bardzo za ten artykuł wskazówkę. Nawet nie zauważyłem, że taki symulator istnieje. Zerknąłem na okienko, którego screen dałeś na końcu i jestem w szoku! Tam można nawet przerwania symulować - REWELACJA!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
OdpowiedzUsuńOwszem można i to z każdego wewnętrznego układu danego mikrokontrolera.
UsuńA to nie wszystko - warto tam pogrzebać nieco :-)
Ten komentarz został usunięty przez administratora bloga.
OdpowiedzUsuńBartek. Czytałeś co napisałem czerwonym tekstem poniżej?
UsuńTym artykułem Dondu zachęciłeś mnie do przetestowania Atmel Studio (dotychczas używałem tylko Eclipse)
OdpowiedzUsuńJak się śledzi zmienne?
OdpowiedzUsuńMenu View > Watch. Otworzy się okienko Watch, gdzie w kolumnie Name wpisujesz nazwę zmiennej i przyciskasz Enter.
UsuńUżywam symulatora od paru lat odkąd zacząłem z AVR. Najpierw AVR Studio, a teraz Atmel Studio. Muszę powiedzieć, że o ile teraz rzadko wykorzystuję (poziom już jakiś mam), to na początku symulator był podstawą mojej nauki. Wielokrotnie już miałem zadawać pytanie na Elce, ale gdy odpaliłem symulator, to z reguły szybko dochodziłem do rozwiązania problemu. Symulator oszczędził mi masę czasu. Tak swoją drogą, to szkoda, że dopiero teraz o tym piszesz, gdy już niestandardowe narzędzia rozpleniły się na dobre. Ale lepiej późno niż wcale :) Pozdrawiam ekipę!
OdpowiedzUsuńTak powinien był się pojawić wcześniej. Ale z drugiej strony, to ciekawość grzebania w menu programu który się używa powinna była od razu doprowadzić każdego, do odnalezienia symulatora, stąd nie było ciśnienia w zakresie pisania artykułu na ten temat.
UsuńArgument z rozplenieniem się nieoryginalnych środowisk jest trafiony :)
Również pozdrawiamy.
Ekipa
A ja się kiedyś męczyłem i ręcznie liczyłem ilość taktów funkcji przerwania, a wystarczy ją odpalić w symulatorze.
OdpowiedzUsuńpowinniście załączyć jeszcze link do tego filmiku atmela:
OdpowiedzUsuńhttp://www.youtube.com/watch?v=9QlDSNeuAdY
Filmik dodany :-)
UsuńWykorzystywałbym ten program, gdyby nie był on tak cholernie zasobożerny... :/
OdpowiedzUsuńSprawdziłem - całe Atmel Studio w trakcie symulacji programu (który dla ATmega32 zawiera się w parwie 10kB FLASH) zajmuje w systemie Windows niecałe 500MB RAM. Co do ewentualnego przyspieszenia Atmel Studio przeczytaj: Atmel Studio: Jak przyśpieszyć?
UsuńSymulator działa szybko, a wcale nie mam jakiegoś super komputera na biurku :-)
W ostateczności możesz korzystać z wcześniejszej wersji środowiska, która także zawiera symulator: AVR Studio
Atmel Studio używam od początku zainteresowania się AVRami , aktualnie mam wersję 7 i piszę w asemblerze na xmega128a1u, udało mi się podłączyć zewnętrzną pamięć, razem 128K, oczywiście pojawiły się problemy i skorzystałem z symulatora. Niestety za żadne skarby symulator nie chce obsłużyć pamięci ram powyżej 4000h, nie można zapisać ani odczytać, na podglądzie komórki wypełnione są znakami zapytania. Jak to odblokować ?
OdpowiedzUsuńok
OdpowiedzUsuń