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!!!








