Ostatnio zacząłem kodować dla Arduino. A te błędy w kodzie mnie zabijają. Ponieważ w Arduino nie ma debugera sprzętowego, serial.print () jest moją jedyną możliwością. Jakie metody / praktyki wdrażasz w celu debugowania kodu Arduino?
Ostatnio zacząłem kodować dla Arduino. A te błędy w kodzie mnie zabijają. Ponieważ w Arduino nie ma debugera sprzętowego, serial.print () jest moją jedyną możliwością. Jakie metody / praktyki wdrażasz w celu debugowania kodu Arduino?
Odpowiedzi:
Modułowość to twój przyjaciel. Napisz swoją główną pętlę, aby zrobić to, wywołując funkcje, które wywołują funkcje ..., aż do poziomu, na którym twoje funkcje będą proste. Rozpocznij od głównej pętli i następnego poziomu w dół, wykonaj funkcje kodów pośredniczących; albo pusty:
function foo(){
;
}
lub fałszywy:
function read_temperature(){
return(95);
}
, które zwracają tylko tyle, ile potrzebuje poziom wywołania, aby mógł kontynuować. Kiedy ten poziom działa, zejdź niżej i zacznij wypełniać prosty kod, który również wywołuje funkcje kodu pośredniczącego. Stopniowo odpakowuj funkcję na raz, aż będziesz mieć działającą aplikację.
Aby debugować funkcję, która zwraca złą wartość lub utworzyć ją bez żadnego wpływu ze strony pozostałej aplikacji, możesz zbudować rusztowanie - prosty szkic, który po prostu podaje funkcji kilka przykładowych wartości, aw ramach funkcji wydrukuj wartości parametrów i niektóre wartości pośrednie, dopóki nie uzyskasz wglądu w to, która część funkcji zawiedzie. Stworzyłem nawet fałszywe funkcje, które zachęcają terminal do zwrócenia wartości. (Oczywiście ta technika może działać tylko wtedy, gdy system może tolerować względnie lodowcową prędkość nas ludzi! Kolejne zastosowanie do rusztowań).
Stubbing działa szczególnie dobrze, zastępując funkcje interfejsu ze sprzętem, umożliwiając rozpoczęcie uruchamiania aplikacji, zanim będziesz musiał zagłębić się w arkusze danych, problemy z timerem i inne drobiazgi (takie jak brak części!), Które w przeciwnym razie mogą zostać zawieszone Twój postęp.
Mówiąc o problemach z timingiem, przełączanie pinów wyjściowych w określonym punkcie programu, takich jak wejście i wyjście z ISR, daje ci kwadratową falę na pinie Arduino, którego częstotliwość lub cykl pracy może dać ci wgląd w wewnętrzny timing twojego programu. Bezpośredni formularz port-I / O, np.
PORTC ^= 0x01;
, zniekształci czas mniej niż dzwonienie digitalWrite()
. Przydatne, jeśli masz pod ręką lunetę lub jeden z multimetrów cyfrowych z możliwością pomiaru częstotliwości i / lub cyklu pracy.
Podobnie, można użyć analogowego styku wyjściowego, aby wyprowadzić wartość liczbową do miernika z wnętrza programu, nie zakłócając zbytnio taktowania ani nie nadużywając kodu za pomocą szeregowych funkcji we / wy. Skorzystaj również z bezpośrednich formularzy We / Wy.
3 inne techniki:
Zbuduj funkcjonalność programu, powoli testując na każdym etapie, w ten sposób konfrontujesz tylko niewielki zestaw błędów na raz.
Zbuduj program wokół interpretera poleceń, aby móc pracować z sekcjami naraz, tak jak tutaj .
Impulsuj w znacznych momentach i użyj lunety.
Wizualna Micro plugin do Visual Studio zapewnia Arduino Debug . Obejmuje śledzenie i łamanie kodu źródłowego, pozwala także na „obserwację” i / lub modyfikację wyrażeń i zmiennych.
Wychodząc z luksusowych narzędzi takich jak ARM lub inne platformy (AVR, PIC z przyzwoitymi narzędziami) Zgadzam się, że możliwości debugowania Arduino są zbyt ograniczone. Ale to narzędzie startowe z niskim wejściem.
Serial.print () to twój przyjaciel. Do mojego konkretnego projektu (uczelni) nie miałem podłączonych żadnych diod LED, więc jest to Serial.print (). Jeśli chcę sprawdzić, czy kod działa poprawnie w instrukcjach, zwykle umieszczam Serial.print („A”); , następnie przechodzę do B, C itp. lub czegoś innego w sekcji Debuguję. Porównuję litery debugowania z tym, czego oczekuję.
Poza tym nie ma punktów przerwania ani stopniowania kodu. Arduino to nic innego jak płytka z chipem atmega AVR, bootloaderem + środowiskiem programistycznym i mnóstwem bibliotek oprogramowania. Niestety praca z programem ładującym ogranicza możliwości debugowania.
Aby użyć serial.print
bardziej kontrolowanego, możesz zdefiniować globalną zmienną logiczną, która będzie włączać i wyłączać debugowanie. Wszelkie wiersze serial.print
zostaną zawinięte w if
instrukcji, która zostanie wykonana tylko wtedy, gdy flaga debugowania jest WŁĄCZONA. W ten sposób możesz zostawić wiersze debugowania w kodzie, nawet gdy skończysz, ale pamiętaj, aby później ustawić flagę debugowania na WYŁ.
Lepiej niż bezpośrednio serial.print, używaj makr. Przykład:
#ifdef TRACE1
#define trace1(s) serial.print(s)
#define traceln1(s) serial.println(s)
#else
#define trace1(s)
#define traceln1(s)
#endif
Użyj tego w ten sposób:
function doIt(param1, param2) {
trace1("->doIt("); trace1("param1: "); trace1(param1); trace1(" param2: "); trace1(param2); traceln1(")");
...
traceln1("<-doIt");
}
Możesz mieć różne poziomy śledzenia (#ifdef TRACE2 ...)
z większą ilością szczegółów.
I można użyć „F” makra (trace1(F("param1"));)
. Makro „F” zapobiega używaniu przez łańcuch bardzo ograniczonej ilości SRAM.
Migaj diody LED, drukuj rzeczy na porcie szeregowym oraz pisz i debuguj małe sekcje kodu na raz, czasem tylko kilka linii.
Są chwile, w których możesz modularyzować. Jeśli na przykład w C można opracować i przetestować funkcję obliczeniową, która na przykład nie dotyka sprzętu na komputerze-hoście, innym procesorze, należy owinąć funkcję stanowiskiem testowym, aby podać dane wejściowe i sprawdzić dane wyjściowe itp.
Innym podobnym sposobem może być użycie symulatora zestawu instrukcji, jeśli masz dostęp do jednego (jeśli nie, jest to bardzo edukacyjny i satysfakcjonujący projekt, po wykonaniu kilku z nich możesz wybić jeden w weekend lub dwa). Nawet lepiej, jeśli ktoś ma klon procesora Verilog lub VHDL ( na przykład OpenCores ), możesz wypróbować GHDL, Verilator lub Icarus Verilog . Może być wystarczająco blisko, aby uwzględnić urządzenia peryferyjne, które Cię interesują, i możesz uzyskać wgląd w poziom sygnału w tym, co dzieje się w środku.
To prawda, że prawdopodobnie nie jest to idealny klon, ale może być wystarczająco dobry. Verilator sprawia, że tworzenie peryferiów w C / C ++ jest naprawdę bardzo łatwe, dzięki czemu można symulować wszystko, do czego podłączone jest urządzenie AVR.
Wyjście UART i migające diody LED i / lub migające linie GPIO oraz za pomocą oscyloskopu lub woltomierza. Kluczem do szaleństwa jest pisanie i debugowanie małych fragmentów kodu. Lepiej jest pisać 10 linii na raz i wykonywać 100 testów niż 1000 linii i próbować debugować je wszystkie za jednym razem. Zwłaszcza gdy dowiesz się, że większość kart danych i podręczników programistów nie zawsze jest poprawna. Zawsze wymagane jest hakowanie.